f# - 使用 'inline' 和报价评估时 (+) 和 (-) 之间的行为不一致

标签 f# inline quotations

有谁知道为什么sub add 时抛出异常不是吗?这是一个错误吗?

open Microsoft.FSharp.Linq.QuotationEvaluation

let inline add x = x + x
let inline sub x = x - x

let answer  = <@ add 1 @>.Eval() // 2, as expected
let answer2 = <@ sub 1 @>.Eval() // NotSupportedException

请注意,如果没有 inline 关键字,则不会抛出异常(但代码不是通用的)
此外,只有在使用引号时才会抛出异常。正常评估工作正常。

谢谢

编辑:简化的代码示例

最佳答案

感谢您提出这个问题 - 这是一个非常好的错误报告,带有简单的重现,我无法相信这一点,但您是完全正确的。加号有效,但减号不行。

问题subadd被编译为泛型方法并且 LINQ 版本调用这些泛型方法。内联在引用存储后执行,因此引用的代码包含对 sub 的调用。方法。这在普通 F# 代码中不是问题,因为函数是内联的,并且运算符在某些数字类型上被解析为 + 或 -。

但是,通用版本使用动态查找。如果您查看 prim-types.fs:3530 ,你会看到:

let inline (+) (x: ^T) (y: ^U) : ^V = 
  AdditionDynamic<(^T),(^U),(^V)>  x y 
  when ^T : int32       and ^U : int32      = (# "add" x y : int32 #)
  when ^T : float       and ^U : float      = (# "add" x y : float #)
  // ... lots of other cases
AdditionDynamic是从泛型方法中调用的。它执行动态查找,这会更慢,但它会起作用。有趣的是,对于减号运算符,F# 库不包括动态实现:
[<NoDynamicInvocation>]
let inline (-) (x: ^T) (y: ^U) : ^V = 
  ((^T or ^U): (static member (-) : ^T * ^U -> ^V) (x,y))
  when ^T : int32      and ^U : int32      = (# "sub" x y : int32 #)
  when ^T : float      and ^U : float      = (# "sub" x y : float #)
  // ... lots of other cases

我不知道为什么会这样 - 我认为没有任何技术原因,但它解释了为什么您会收到您报告的行为。如果您使用 ILSpy 查看编译后的代码,您会看到 add方法做了一些事情,sub方法只是抛出(所以这是异常的来源)。

至于解决方法 ,您需要以不使用通用减号运算符的方式编写代码。可能最好的选择是避免 inline函数(通过使用 sub_intsub_float )或编写您自己的 sub 动态实现(这可能可以使用 DLR 非常有效地完成(参见 this post)。

关于f# - 使用 'inline' 和报价评估时 (+) 和 (-) 之间的行为不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7471801/

相关文章:

.net - 在 F# 中对 2D 数组进行 Foreach 使编译器认为迭代值是对象类型。为什么?

casting - 当存在不同的重载时强制向上转型

reflection - 在 F# 引号内嵌入变量

regex - 如何将模式与可选的引号匹配?

f# - 如何将字典条目的值声明为可变的?

dictionary - 如何在不知道键的情况下从 F# Map 获取第一个键值对?

html - 网络邮件客户端截断内联样式

c# - 可以在 ASPX 标记中执行内联代码吗?

javascript - 内联 Javascript 未执行

php - 如何从文本(PHP)中提取引文?