使用“内联”时,(+) 和 (-) 之间的行为不一致;及报价评估

发布于 2024-12-05 03:02:38 字数 419 浏览 1 评论 0原文

有谁知道为什么 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

小镇女孩 2024-12-12 03:02:38

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

问题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 可能可以非常有效地完成此操作(请参阅 这篇文章)。

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 and add 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 the sub 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:

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

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:

[<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

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 the sub 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 using sub_int or sub_float) or by writing your own dynamic implementation of sub (which can be done probably quite efficiently using DLR (see this post).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文