我怀疑这种行为可能不是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
吗?
这种行为实际上是我想要创建的东西所希望的,但这有点违反直觉,我想更好地理解它。
答案 0 :(得分:4)
发生这种情况是因为进行了优化。
在FSI中运行时,优化功能已关闭,因此一切工作都按您期望的方式进行。
但是,当您在Release中进行编译(即进行优化)时,F#编译器可以做更多的事情,因为它知道代码的结构。在这种情况下,函数getValueOr
将在调用站点内联,最后一行大致变为以下内容:
// C# syntax
Console.WriteLine( foo == null ? Bar.bar : foo.Value )
这是另一个有趣的实验:如果将Bar
模块的定义移到引用Bar.bar
的位置,则效果(可能)会再次出现,因为{{1}的定义}本身将被内联,大致如下:
bar
底线是:不受控制的效果不好。它们使您的程序变幻莫测。尽量避免它们。