使用“内联”时,(+) 和 (-) 之间的行为不一致;及报价评估
有谁知道为什么 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 关键字,则不会引发异常(但代码不是通用的) 此外,仅在使用引号时才会引发异常。正常的评估效果很好。
谢谢
编辑:简化的代码示例
Does anyone know why sub
throws an exception when add
does not? And is this a bug?
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
Note, without the inline keyword the exception is not thrown (but the code is not generic)
Also, the exception is only thrown when using quotations. Normal evaluation works fine.
Thanks
Edit: simplified code example
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
感谢您提出这个问题 - 这是一个非常好的错误报告,带有简单的重现,我简直不敢相信这一点,但您是完全正确的。加号有效,但减号无效。
问题是
sub
和add
被编译为泛型方法,并且 LINQ 版本调用这些泛型方法。内联是在存储引用之后执行的,因此引用的代码包含对sub
方法的调用。这在普通 F# 代码中不是问题,因为函数是内联的,并且运算符在某些数字类型上解析为 + 或 -。然而,通用版本使用动态查找。如果您查看
prim-types.fs:3530
,您会看到:AdditionDynamic
是从泛型方法中调用的。它进行动态查找,速度会慢一些,但它会起作用。有趣的是,对于减号运算符,F# 库不包含动态实现:我不知道为什么会出现这种情况 - 我认为没有任何技术原因,但它解释了为什么会出现您报告的行为。如果您使用 ILSpy 查看编译后的代码,您会发现
add
方法做了一些事情,而sub
方法只是抛出异常(所以这就是异常的来源) 。至于解决方法,您需要以不使用通用减号运算符的方式编写代码。也许最好的选择是避免
inline
函数(通过使用sub_int
或sub_float
)或编写您自己的sub 动态实现
(使用 DLR 可能可以非常有效地完成此操作(请参阅 这篇文章)。Thanks for this question - it is a really nice bug report with a simple repro and I couldn't believe this, but you're completely right. Plus works, but minus doesn't.
The problem is that
sub
andadd
are compiled as generic methods and the LINQ version invokes these generic methods. The inlining is performed after quotations are stored, so the quoted code contains call to thesub
method. This isn't a problem in normal F# code, because the functions are inlined and the operators are resolved to + or - over some numeric types.However, the generic version uses a dynamic lookup. If you look into
prim-types.fs:3530
, you'll see:The
AdditionDynamic
is what gets called from the generic method. It does the dynamic lookup, which will be slower, but it will work. Interestingly, for the minus operator, the F# library doesn't include dynamic implementation:I have no idea why this is the case - I don't think there is any technical reason, but it explains why you get the behavior you reported. If you look at the compiled code using ILSpy, you'll see that the
add
method does something and thesub
method just throws (so this is where the exception comes from).As for a workaround, you need to write the code in a way in which it doesn't use the generic minus operator. Probably the best option is to avoid
inline
functions (either by usingsub_int
orsub_float
) or by writing your own dynamic implementation ofsub
(which can be done probably quite efficiently using DLR (see this post).