为什么在.NET框架版本之间F#中的Lazy.CreateFromValue行为发生了变化?

时间:2016-12-06 17:41:21

标签: .net f# lazy-evaluation .net-framework-version

我刚刚在F#编译这段代码时遇到了框架版本之间行为的变化:

let test = Lazy.CreateFromValue 1

针对.NET framework 2.0编译,表达式导致“已创建”的Lazy对象,即:

test.IsValueCreated = true

当针对.NET framework 4.0编译时,表达式会产生一个“未评估”的惰性对象,即:

 test.IsValueCreated = false

只有在后一种情况下访问 test.Value 后,两者才相同。

我无法在任何地方找到任何对这种变化的引用,因此我的问题是为什么这种行为有所不同以及改变背后的原因是什么(它正在破裂)。在我看来,.NET 2.0中的行为更有意义 - 从具体值创建一个Lazy对象应该会导致“已经评估”的惰性对象。

2 个答案:

答案 0 :(得分:14)

在.NET 4.0之前,该框架未附带Lazy<'T>

这是古老的历史,但是F#最初有Lazy的实现,与我们今天的不同 - 你可以瞥见它here

Lazy明确表示框架正确进入时,该实施已被放弃。相反,F#2.0在System.Lazy<'T>程序集中附带了自己的FSharp.Core实现(注意命名空间)。这是你仍然可以看到here的实现。这个想法是,一旦.NET 4.0可用,F#将从那里无缝地接收System.Lazy<'T>,而不会破坏用户。这在很大程度上起作用,但并非完全没有问题。

您会注意到F#实现有一个CreateFromValue成员,它产生的类型Lazy<'T>已标记为已评估。这在语义上是完全有意义的,因为你显然首先给它一个评估值。

为什么.NET 4.0实现不会这样做呢?

如果查看Lazy<'T>的{​​{3}},您会发现在评估状态下无法创建它。它上面没有CreateFromValue成员,没有构造函数的值为'T,只有Func<'T>CreateFromValue实际上是由F#提供的documentation

以非破坏的方式提供此方法非常容易:

static member CreateFromValue(value : 'T) : System.Lazy<'T> =
    let x = System.Lazy<'T>.Create(fun () -> value)
    x.Value |> ignore
    x

但是由于某种原因没有发生。也许这是一个刻意的选择 - 我想你可以支持和反对这种改变 - 但也许这是一种疏忽。如果你看一下这种令人费解的历史,我想你会同意它可能会更糟。

答案 1 :(得分:4)

{4}}类被添加到.NET 4中的框架中。对于惰性的F#支持更多的是允许API在.NET 2上运行的存根,但并不提供真正的使用此API时的延迟实现。

F#编译器为.NET 2创建了它的Lazy<T>,因为它早于.NET 4版本。在.NET 2中,惰性类型只是显式设置值,并且在使用CreateFromValue时并不是真正的懒惰(请参阅own lazy type)。

因此,CreateFromValue source实际上是懒惰的 - 它使用返回值的函数实例化惰性类型。在.NET 2上,extension on .NET 4符合Lazy<'T>.CreateFromValue的API,但并不是真正的懒惰。