12

下面的第一个定义在使用 f# 3.0 编译并将警告级别设置为 5 时在标题中产生警告。第二个定义编译干净。我想知道是否有人可以解释一下编译器担心我可能会意外变异的原因,或者如何用 let 子句拆分表达式有助于避免这种情况。非常感谢。

let ticks_with_warning () : int64 =
   System.DateTime.Now.Ticks 

let ticks_clean () : int64 =
   let t = System.DateTime.Now
   t.Ticks 
4

1 回答 1

12

我无法真正解释为什么编译器会在您的特定情况下发出此警告-我同意@ildjarn 的观点,您可以放心地忽略它,因为编译器可能只是过于谨慎了。

但是,我可以给你一个例子,警告实际上可能会给你一个有用的提示,表明某些事情可能不会像你期望的那样进行。如果我们有一个像这样的可变对象struct

[<Struct>]
type Test =
  val mutable ticks : int64
  member x.Inc() = x.ticks <- x.ticks + 1L
  new (init) = { ticks = init }

现在,该Inc方法改变了结构(您也可以访问可变字段ticks)。我们可以尝试编写一个创建Test值并对其进行变异的函数:

let foo () =
  let t = Test(1L)
  t.Inc()  // Warning: The value has been copied to ensure the original is not mutated
  t

我们没有将本地值标记tmutable,因此编译器会尝试确保在我们调用 时该值不会发生变化Inc。它不知道是否Inc改变值,所以唯一安全的事情是创建一个副本 - 从而foo返回值Test(1L)

如果我们标记tmutable,那么编译器不必担心由于调用而改变它,因此它不会给出警告(并且函数返回Test(2L)):

let foo () =
  let mutable t = Test(1L)
  t.Inc()
  t

不过,我不确定是什么导致了您的示例中的警告。也许编译器认为(作为一些中间表示的结果)该Ticks操作可能会改变左侧的值(System.DateTime.Nowt分别)并且它希望防止这种情况发生。

奇怪的是,如果您在 F# 中编写自己的DateTime结构,则在这两种情况下都会收到警告,除非您将变量标记tmutable(这是我所期望的),但标准的行为DateTime是不同的。所以也许编译器知道我缺少的标准类型......

于 2012-12-06T22:55:24.270 回答