F#编译器可以通过currying一个函数来分离代码路径,其中不同类型意味着通过后续调用的函数的不同路径?
考虑以下有区别的联合。有两种可能性,理论上是不同的类型:
type Choice =
| Halve
| Double
假设我们对其中一些情况有一些特定的功能:
let halve value = value / 2.0
let divide value = value * 2.0
以及 2 个函数,它们根据某些类型提供 2 个单独的代码路径param
(完成 fs 文件的其余部分):
let inner param value =
match param with
| Choice.Halve -> halve value
| Choice.Double -> divide value
let outer param value =
let internalVariable =
match param with
| Choice.Halve -> "halving"
| Choice.Double -> "doubling"
inner param value
[<EntryPoint>]
let main argv =
printf "%g\n" (outer Choice.Halve 4.0)
let doubler = outer Choice.Double
printf "%g\n" (doubler 6.0)
因此,Half 和 Double 是独立的代码路径,我们可以将它们编写为两个独立的函数。
从理论上讲,柯里化会说有两种不同的功能;如果您将第一个参数 curry 为 Choice.Halve 或 Choice.Double 类型(如 in doubler
),那么您拥有特定于该类型的函数,编译器应该能够优化以后的分支。
是这样吗?
如果我查看 IL,我看不到这样的优化,但我想这可能是 JITted。一位同事建议分支预测使这种优化变得不必要。
是避免不必要的分支反转结构并传递divide
/halve
函数的唯一方法吗?
- 编辑 -
John Palmer 建议添加inline
,所以我尝试了一下,得到了以下优化的 IL outer
:
IL_0001: ldarg.0
IL_0002: call instance int32 Program/Choice::get_Tag()
IL_0007: ldc.i4.1
IL_0008: bne.un.s IL_0016
IL_000a: ldarg.1
IL_000b: ldc.r8 2
IL_0014: mul
IL_0015: ret
IL_0016: ldarg.1
IL_0017: ldc.r8 2
IL_0020: div
IL_0021: ret
然而,curried 函数并没有明显的优化- 所以 uncurrieddoubler
函数main
正在被优化,而不是 curried 函数。