type T() =
static member (~%)(t : T) = t
let t = T()
let t' = %t // FAILS
错误消息显示t
应为Quotation.Expr<'a>
类型。
%应该是valid prefix operator,但实际可以使用吗?
答案 0 :(得分: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.
因此,如果我们按如下方式重新定义顶级(~%)
运算符,那么您的示例将编译而不会出现错误:
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>
个签名中see表示这些类型根本没有定义任何运算符,更不用说op_Splice
。
您可以在&&
和||
中缀运算符中看到类似的结果。哪个可以重新定义(映射到op_BooleanAnd
和op_BooleanOr
),但除非它们是,否则它们将由编译器专门处理。
答案 1 :(得分:4)
我不确定为什么%
运算符的行为与此类似,但您可以使用全局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 @>