为什么 Lazy.CreateFromValue 的行为在 .NET Framework 版本之间的 F# 中发生了变化?
Why has the behaviour of Lazy.CreateFromValue changed in F# between .NET framework versions?
我刚刚在 F# 中编译这段代码时遇到了框架版本之间的行为变化:
let test = Lazy.CreateFromValue 1
针对 .NET Framework 2.0 编译,表达式生成 "already created" 惰性对象,即:
test.IsValueCreated = true
针对 .NET Framework 4.0 编译时,表达式会生成 "unevaluated" 惰性对象,即:
test.IsValueCreated = false
只有在后一种情况下访问test.Value后两者是等价的
我在任何地方都找不到关于此更改的任何参考,因此我的问题是为什么它的行为不同以及更改背后的原因是什么(它正在中断)。在我看来,.NET 2.0 中的行为更有意义 - 从具体值创建惰性对象应该导致 "already evaluated" 惰性对象。
Lazy<T>
class 已添加到 .NET 4 中的框架。F# 对惰性的支持更像是一个允许 API 在 .NET 2 上工作的存根, 但在使用 API.
时不提供真正的惰性实现
F# 编译器在使用 CreateFromValue
.
时创建了 own lazy type for .NET 2, as it predated the .NET 4 version. In .NET 2, the lazy type just sets the value explicitly, and isn't truly lazy (see the source)
因此,CreateFromValue
extension on .NET 4 is truly lazy - it instantiates the lazy type with a function that returns the value. On .NET 2, the internal type is used instead 满足了 Lazy<'T>.CreateFromValue
的 API,但并不是真正的懒惰。
在 .NET 4.0 之前,框架未随 Lazy<'T>
一起提供。
那是古老的历史,但 F# 最初有自己的 Lazy
实现,与我们今天所拥有的不同 - 您可以瞥见它 here。
当很明显 Lazy
即将进入框架时,该实施被放弃了。相反,F# 2.0 在 FSharp.Core
程序集中附带了自己的 System.Lazy<'T>
(注意命名空间)实现。这是您仍然可以看到的实现 here。这个想法是,一旦 .NET 4.0 可用,F# 将从那里无缝地获取 System.Lazy<'T>
,而不会影响用户。这在大多数情况下都有效,但并非完全没有问题。
您会注意到,F# 实现有一个 CreateFromValue
成员,它产生一个类型为 Lazy<'T>
的值,该值已标记为已评估。这在语义上非常有意义,因为您显然首先给它一个评估值。
为什么 .NET 4.0 实现不做同样的事情?
如果你用 F# 查看 documentation for Lazy<'T>
, you'll find that there's no way of creating it in an evaluated state. There's no CreateFromValue
member on it, and no constructor takes a value of 'T
, only a Func<'T>
. CreateFromValue
is actually provided as an extension method。
以不间断的方式提供此方法将非常容易:
static member CreateFromValue(value : 'T) : System.Lazy<'T> =
let x = System.Lazy<'T>.Create(fun () -> value)
x.Value |> ignore
x
但出于某种原因,这并没有发生。也许这是一个深思熟虑的选择——我想你可以为这种改变争论不休——但也许这是一个疏忽。如果你看看这种类型的错综复杂的历史,我想你会同意它可能会更糟。
我刚刚在 F# 中编译这段代码时遇到了框架版本之间的行为变化:
let test = Lazy.CreateFromValue 1
针对 .NET Framework 2.0 编译,表达式生成 "already created" 惰性对象,即:
test.IsValueCreated = true
针对 .NET Framework 4.0 编译时,表达式会生成 "unevaluated" 惰性对象,即:
test.IsValueCreated = false
只有在后一种情况下访问test.Value后两者是等价的
我在任何地方都找不到关于此更改的任何参考,因此我的问题是为什么它的行为不同以及更改背后的原因是什么(它正在中断)。在我看来,.NET 2.0 中的行为更有意义 - 从具体值创建惰性对象应该导致 "already evaluated" 惰性对象。
Lazy<T>
class 已添加到 .NET 4 中的框架。F# 对惰性的支持更像是一个允许 API 在 .NET 2 上工作的存根, 但在使用 API.
F# 编译器在使用 CreateFromValue
.
因此,CreateFromValue
extension on .NET 4 is truly lazy - it instantiates the lazy type with a function that returns the value. On .NET 2, the internal type is used instead 满足了 Lazy<'T>.CreateFromValue
的 API,但并不是真正的懒惰。
在 .NET 4.0 之前,框架未随 Lazy<'T>
一起提供。
那是古老的历史,但 F# 最初有自己的 Lazy
实现,与我们今天所拥有的不同 - 您可以瞥见它 here。
当很明显 Lazy
即将进入框架时,该实施被放弃了。相反,F# 2.0 在 FSharp.Core
程序集中附带了自己的 System.Lazy<'T>
(注意命名空间)实现。这是您仍然可以看到的实现 here。这个想法是,一旦 .NET 4.0 可用,F# 将从那里无缝地获取 System.Lazy<'T>
,而不会影响用户。这在大多数情况下都有效,但并非完全没有问题。
您会注意到,F# 实现有一个 CreateFromValue
成员,它产生一个类型为 Lazy<'T>
的值,该值已标记为已评估。这在语义上非常有意义,因为您显然首先给它一个评估值。
为什么 .NET 4.0 实现不做同样的事情?
如果你用 F# 查看 documentation for Lazy<'T>
, you'll find that there's no way of creating it in an evaluated state. There's no CreateFromValue
member on it, and no constructor takes a value of 'T
, only a Func<'T>
. CreateFromValue
is actually provided as an extension method。
以不间断的方式提供此方法将非常容易:
static member CreateFromValue(value : 'T) : System.Lazy<'T> =
let x = System.Lazy<'T>.Create(fun () -> value)
x.Value |> ignore
x
但出于某种原因,这并没有发生。也许这是一个深思熟虑的选择——我想你可以为这种改变争论不休——但也许这是一个疏忽。如果你看看这种类型的错综复杂的历史,我想你会同意它可能会更糟。