如何定义和使用%作为前缀运算符?

Joh*_*Joh 5 f#

type T() =
    static member (~%)(t : T) = t

let t = T()
let t' = %t // FAILS
Run Code Online (Sandbox Code Playgroud)

错误消息说t预计是类型Quotation.Expr<'a>.%是一个所谓的有效前缀运算符,但它是否可以实际使用它?

Ste*_*sen 5

您之所以看到这种行为,是因为F#没有(~%)像大多数顶级运算符那样定义静态约束.它被定义为一个函数Quotations.Expr<'a> -> 'a.因此,您在类型上定义的(~%)函数(它是别名op_Splice)T不能通过使用顶级(~%)运算符来解决.

您可以通过以下FSI交互来看到这一点:

> <@ (~%) @>;;

  <@ (~%) @>;;
  ^^^^^^^^^^

C:\Users\Stephen\AppData\Local\Temp\stdin(5,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type
    val it : Expr<(Expr<'_a> -> '_a)>    
Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.
Run Code Online (Sandbox Code Playgroud)

因此,如果我们(~%)按如下方式重新定义顶级运算符,那么您的示例将编译而不会出现错误:

let inline (~%) (x : ^a) = (^a : (static member op_Splice : ^a -> 'b) (x))
Run Code Online (Sandbox Code Playgroud)

但请注意引用拼接将不再起作用:

let x = <@ 3 @>
<@ %x @>
----^
error FS0001: The type 'Expr<int>' does not support the operator '~%'
Run Code Online (Sandbox Code Playgroud)

这是因为(~%)编译器特别为原始定义处理引用拼接.事实上,你可以看到ExprExpr<'T>签名,这些类型完全不定义任何运营商,更不用说op_Splice.

您可以使用&&和中||缀运算符查看类似的结果.哪个可以重新定义(映射到op_BooleanAndop_BooleanOr),但除非它们是,否则它们将由编译器专门处理.