【问题标题】:Was point free functions able to inline?无点函数能够内联吗?
【发布时间】:2012-09-13 08:51:27
【问题描述】:
let inline myfunction x y = ...

let inline mycurried = myfunction x // error, only functions may be marked inline

似乎不可能显式地inline 柯里化函数。 所以每当mycurried被调用时,即使myfunctioninlined,它也不会得到inlined,对吗?

那么这可以看作是柯里化函数的缺点之一吗?

【问题讨论】:

    标签: f# inline currying pointfree


    【解决方案1】:

    我认为你只需要在双方都添加一个显式参数(虽然我没有尝试过):

    let inline myfunction x y = ... 
    
    let inline mycurried y = myfunction 42 y  // or whatever value (42)
    

    【讨论】:

      【解决方案2】:

      我认为您的问题是 point-free 函数是否可以内联。

      您发现的限制不是因为柯里化函数。 请注意,在您的示例中,柯里化函数在右侧,在左侧您有一个无点函数。

      F# 只允许内联函数,而不是常量。

      我原则上你可能认为这可以被认为是一个错误,因为类型推断足够聪明,可以发现这是一个(无点)函数,但请阅读 Tomas 关于副作用的注释。

      显然,当编译器仅在左侧找到一个标识符时,它会因以下错误而失败:

      let inline myfunction x y = x + y
      
      let inline mycurried  = myfunction 1
      
      --> Only functions may be marked 'inline'
      

      正如 Brian 所说,一种解决方法是在两边都添加一个显式参数:

      let inline mycurried x  = (myfunction 1) x
      

      但是你的函数不再是无点的,它与:

      let inline mycurried x  = myfunction 1 x
      

      另一种方法可能是添加显式泛型参数:

      let inline mycurried<'a>  = myfunction 1
      

      当泛型参数显式出现在左侧时,它会编译。

      我希望他们删除错误消息并将其变为警告,例如:

      Since only functions can be 'inline' this value will be compiled as a function.
      

      更新

      感谢 Tomas 的回答(以及您的反对票)。

      我个人的意见是这应该是一个警告,所以你知道你的代码的语义最终会改变,但接下来要由你决定做什么。

      你说内联“只是一种优化”,但这并不完全正确:

      。简单地将所有函数内联并不能保证最佳代码。

      。您可能想使用静态约束,然后必须使用内联。

      我希望能够定义我的(某种)通用常量,就像 F# 库已经做的那样(即:GenericZero 和 GenericOne)。我知道我的代码会很纯净,所以我不在乎它是否每次都执行。

      【讨论】:

      • 请注意,将 let 绑定从 value (let x = ...) 更改为 function (let x arg = ...) 可能会改变function 如果用于计算函数的表达式有一些影响。在第一种情况下,效果将被评估一次,在第二种情况下,它们将被重复评估。如果您希望效果只评估一次,那么您不能使用inline。如果你想多次执行它们,那么我认为添加一个额外的参数是公平的。
      • 添加一个泛型参数是非常微妙的,因为它看起来仍然像一个值,但它实际上表现为一个函数(即重复评估效果),所以我不会建议这样做。 (此外,我很确定该行为实际上是未定义的——即编译器的未来版本可能会决定对每个泛型类型参数只评估一次效果)。
      • 该错误消息在纯语言(如 Haskell)中没有意义,但我认为防止您在添加 inline 时意外更改程序的行为只是为了优化它是一个非常好决定。
      • 感谢您将此解释添加到答案中!
      【解决方案3】:

      编译器只允许inline 用于定义函数的 let 绑定。这与with F# value restriction 发生的事情本质上是一样的(参见also here)。正如 Brian 所说,您可以通过向函数添加参数来轻松解决此问题。

      为什么会有这个限制?如果它不存在,那么添加 inline 会改变程序的含义,那会很糟糕!

      例如,假设你有一个这样的函数(它创建可变状态并返回一个计数器函数):

      let createCounter n = 
        let state = ref n
        (fun () -> incr state; !state)
      

      现在,下面的代码:

      let counter = createCounter 0
      

      ... 创建一个可以多次使用的全局函数(调用 counter()),它会为您提供从 1 开始的唯一整数。如果您可以将其标记为 inline

      let inline counter = createCounter 0
      

      ...那么每次你使用counter(),编译器都应该用createCounter 0 ()替换它,这样你每次调用计数器都会得到1!

      【讨论】:

      • 你是对的。实际上,您可以按照我提到的技巧将其编译为内联:let inline counter = ......无论如何,我仍然更喜欢警告而不是编译错误并且不得不依赖解决方法/技巧。
      • @Gustavo 我认为当内联(实际上只是一种优化)会改变代码的语义时,出现编译器错误是非常公平的。该错误只是告诉您编译器 cannot 内联值(这很有意义)。如果您想(并且可以)将值转换为函数,那么您必须明确地执行此操作是很好的,因为您必须考虑这是否给了您想要的含义。
      • @Gustavo 是的,如果你写let inline counter&lt;'T&gt; = createCounter 0,你可以编译代码,我认为这很糟糕。这意味着当您调用counter&lt;_&gt;() 时,您总是会得到1,这不是预期的行为。
      猜你喜欢
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多