有人知道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关键字,则不会抛出异常(但代码不是通用的) 此外,仅在使用引用时抛出异常。正常评估工作正常。
由于
编辑:简化代码示例
答案 0 :(得分:13)
感谢您提出这个问题 - 这是一个非常好的错误报告,有一个简单的复制品,我无法相信这一点,但您完全正确。加上工作,但减去不工作。
问题是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)。