F#模块在加载时的效果

时间:2018-08-10 18:46:48

标签: module f# lazy-evaluation

我怀疑这种行为可能不是F#独有的,但我会选择它,因为这是我在工作中所使用的。

假设我有一个模块

open System

module Bar =
    let bar =
        Console.WriteLine("BAR!")
        "bar"

在fsx中有以下内容:

// this is probably a standard library function (Operators.defaultArg?)
let getValueOr v = function
| Some x -> x
| None -> v

let opt = Some "foo"

Console.WriteLine( opt |> getValueOr Bar.bar )

运行此命令时,我看到以下内容

BAR!
foo

这是可以预期的,因为参数通常在函数体之前进行评估,所以我希望Bar.bar中的效果会在getValueOr部分应用它之前(甚至在读取模块时)发生。 / p>

但是,当我将Bar模块编译成DLL并#r时,我看到的只是

foo

换句话说,Bar.bar没有得到评估……为什么?是因为#r吗?

这种行为实际上是我想要创建的东西所希望的,但这有点违反直觉,我想更好地理解它。

1 个答案:

答案 0 :(得分:4)

发生这种情况是因为进行了优化。

在FSI中运行时,优化功能已关闭,因此一切工作都按您期望的方式进行。

但是,当您在Release中进行编译(即进行优化)时,F#编译器可以做更多的事情,因为它知道代码的结构。在这种情况下,函数getValueOr将在调用站点内联,最后一行大致变为以下内容:

// C# syntax
Console.WriteLine( foo == null ? Bar.bar : foo.Value )

这是另一个有趣的实验:如果将Bar模块的定义移到引用Bar.bar的位置,则效果(可能)会再次出现,因为{{1}的定义}本身将被内联,大致如下:

bar

底线是:不受控制的效果不好。它们使您的程序变幻莫测。尽量避免它们。