懒惰<_>和thunk(fun() - > ...)之间的实际差异

cme*_*ren 8 .net f# lazy-loading

在F#核心库中有高阶函数,它们采用thunk(fun () -> ...),但也可以在概念上采用Lazy<_>,例如Option.defaultWith.Lazy<_>通过lazy关键字,F#具有良好的语法支持,但我不能想到F#核心库中的任何函数Lazy<_>而不是thunk.我猜这是因为使用thunks比使用特定于.NET的Lazy<_>类型更"FP" .

但除了模糊地"减少FP"之外:如果最多需要一次这个值,那么使用Lazy<_>而不是thunk 的实际考虑是什么?例如,是否存在性能差异(CPU和/或分配/内存)?其他问题?用Lazy<_>th而不是thunk 可以更好地解决什么样的情况?

Hon*_*tan 9

作为一个包装thunk的单独对象,提供同步并保存结果引用,Lazy<_>与简单的thunk相比,有一些额外的开销.

如果你知道你只会评估thunk(最多)一次,我认为如果可以的话你就不会使用函数.你可以在另一个函数中包含对Lazy<_>s 的调用Value,但在这种情况下它没有我所知道的好处.

我记得有一个场景Lazy<_>是有用的,那就是我们在产品中有两个独立的功能标志,如果启用它们,可能需要初始化Orleankka actor系统 - 昂贵的操作,每次应用程序启动时应该只执行一次.

所以我们的选择要么是很多嵌套if的,可变的选项,要么:

let actorSystem = lazy initializeActorSystem ()

if feature1Enabled then
    let as = actorSystem.Value
    ...

if feature2Enabled then
    let as = actorSystem.Value
    ...
Run Code Online (Sandbox Code Playgroud)

与thunk不同,Lazy<_>还允许您检查thunk是否被评估.从同样的问题:

if actorSystem.IsValueCreated then
    actorSystem.Value.Dispose()
Run Code Online (Sandbox Code Playgroud)

其他情况通常在你不确定你最多会评估一次thunk时,特别是如果它可以同时发生.我认为我们也有这样的用例,用于读取一些外部配置,而不是本地提供给同时启动的多个组件 - Lazy<_>确保我们只进行一次远程调用(如果需要的话),无论哪个组件首先发现它需要它.