在以下 F# 代码中,f1 将 Span 作为输入,f2 调用 f1。当参数通过管道传递而不是传递时,编译器会给出指示的错误。
let f1 (span: Span<byte>) = span.Length
let f2() =
let buf = [|0uy|].AsSpan()
buf |> f1 // FS0412
f1 buf // Ok
Run Code Online (Sandbox Code Playgroud)
FS0412 A type instantiation involves a byref type. This is not permitted by the rules of Common IL
有没有办法将 Span 通过管道传输到 F# 函数中?
只是为了增加一点色彩来说明原因:
Byref 和类似 byref 的类型与函数优先编程背道而驰。
这些类型需要将其整个生命周期都放在堆栈上,并进行一些编译器分析,允许运行时省略一些检查。这对性能来说非常好。
当一个函数成为一流函数时,它涉及堆分配。Invoke基本上,它是一个带有方法的对象。在 F# 编译器中,有几种优化尝试将 F# 函数转换为静态方法(大多数 F# 函数声明都是这样),但在很多情况下它们会作为堆上的对象发出(有些情况下您可以预测,有些你无法预测)。这意味着几件事:
在某些情况下,这在技术上是可能的,但源代码中没有任何内容可以表明为什么在某些情况下可行,而在其他情况下则不然。原因很简单,“因为编译器需要将此函数作为对象发出”,这是完全不可预测且不统一的。一项提议的建议将对此有所帮助,但它已关闭,有利于编译器中的调整,并且像此建议一样,从可预测性的角度来看,估计这可能不会太糟糕。
现在|>情况更有趣了,声明的其他几个函数也是如此inline。该|>运算符的字面意思是接受高阶函数作为参数,因此自然不应该支持它。但因为它被定义为inline,所以它实际上可以工作,因为它只是一种优化。然而,这也可能要求您只能在其他 inline函数的上下文中使用它。您可能无法通过管道输入任何任意函数。
这就是为什么这不是一个错误,而是一种设计行为,如果实施的话,需要认真考虑增强:https://github.com/fsharp/fslang-suggestions/issues/688
| 归档时间: |
|
| 查看次数: |
204 次 |
| 最近记录: |