f#编译器可以根据类型使用套用来分隔代码路径吗?



f#编译器是否可以通过套用一个函数来分离代码路径,其中不同的类型意味着通过随后调用的函数的不同路径?

考虑下面的歧视联合。有两种可能,理论上是不同的类型:

type Choice =
    | Halve
    | Double

假设我们有一些特定的函数用于这些情况:

let halve value = value / 2.0
let divide value = value * 2.0

和2个函数,根据某些param的类型提供2个单独的代码路径(其余部分用于完成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 "%gn" (outer Choice.Halve 4.0)
    let doubler = outer Choice.Double
    printf "%gn" (doubler 6.0)

half和Double因此是独立的代码路径,我们可以把它们写成两个独立的函数。

理论上,柯里化会说有两个不同的函数;如果您将第一个参数curry到选择中的任何一个。一半或选择。双类型(如doubler),那么你有一个特定于该类型的函数,编译器应该能够优化以后的分支。

是这样吗?

如果我在IL中查看,我可以看到没有这样的优化,但我想这可能是jit。一位同事认为,分支预测使这种优化变得不必要。

唯一避免不必要的分支的方法是将结构倒置并将divide/halve函数传递进去吗?

—Edit—

John Palmer建议添加inline,所以我尝试了一下,并为outer获得了以下优化的IL:

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

然而,在main中没有明显的优化doubler的柯里化函数——所以未柯里化的函数被优化了,而不是柯里化的函数。

我不认为编译器会自动做到这一点,但你可以写你的代码,使它显式地实现:

let inner param = 
    match param with
    | Choice.Halve -> (fun value -> halve value)
    | Choice.Double -> (fun value -> divide value)

这个函数可以像你的原始函数一样使用,例如inner 1.0 2.0,但是当你只使用一个参数调用它时,它实际上运行body的第一部分,并返回一个在match情况下创建的函数。

相关内容

  • 没有找到相关文章

最新更新