type T() =
static member (~%)(t : T) = t
let t = T()
let t' = %t // FAILS
错误消息说t
应该是 type Quotation.Expr<'a>
。% 是一个所谓的有效前缀运算符,但是否可以实际使用它?
您看到此行为的原因是因为 F# 没有(~%)
像大多数顶级运算符那样使用静态约束进行定义。它被定义为一个函数Quotations.Expr<'a> -> 'a
。因此,您在 type 上定义的(~%)
函数(它是 的别名)不能通过使用顶级运算符来解析。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.
因此,如果我们如下重新定义顶级(~%)
运算符,那么您的示例将编译而不会出错:
let inline (~%) (x : ^a) = (^a : (static member op_Splice : ^a -> 'b) (x))
但请注意,引号拼接将不再起作用:
let x = <@ 3 @>
<@ %x @>
----^
error FS0001: The type 'Expr<int>' does not support the operator '~%'
那是因为 的原始定义(~%)
被编译器特殊处理以进行引号拼接。实际上,您可以在和签名中看到这些类型根本没有定义任何运算符,更不用说.Expr
Expr<'T>
op_Splice
&&
您可以使用和中||
缀运算符看到类似的结果。可以重新定义(映射到op_BooleanAnd
and op_BooleanOr
),但除非它们是,否则编译器会对其进行特殊处理。
我不确定为什么%
操作员会这样,但您可以使用全局let
绑定重新定义它:
let (~%) a = -a
%10
如果无法将运算符定义为static
成员(我不确定是否是这种情况,或者我只是遗漏了一些东西),您仍然可以定义一个inline
调用对象的某些静态成员的定义。这应该为您提供基本相同的功能:
// Instead of defining static member '%', we define static member 'Percent'
type T() =
static member Percent(t : T) = t
// Inline definition of '~%' that calls the static member 'Percent' of an object
let inline (~%) (x : ^T) = (^T : (static member Percent : ^T -> 'R) (x))
// Now you can use the '%t' syntax to invoke the static member
let t = T()
let t' = %t
背景:在 F# 引用代码中,它用于将表达式“拼接”到另一个表达式中(以构建由另一个先前定义的表达式组成的表达式)。错误消息表明编译器没有看到您的定义。
let two = <@ 2 @>
let oneAndTwo = <@ 1 + %two @>