有谁知道为什么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 关键字,则不会抛出异常(但代码不是通用的)
此外,只有在使用引号时才会抛出异常。正常评估工作正常。
谢谢
编辑:简化的代码示例
最佳答案
感谢您提出这个问题 - 这是一个非常好的错误报告,带有简单的重现,我无法相信这一点,但您是完全正确的。加号有效,但减号不行。
问题是sub
和 add
被编译为泛型方法并且 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_int
或 sub_float
)或编写您自己的 sub
动态实现(这可能可以使用 DLR 非常有效地完成(参见 this post)。
关于f# - 使用 'inline' 和报价评估时 (+) 和 (-) 之间的行为不一致,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7471801/