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

let t = T()
let t' = %t // FAILS

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

4

2 回答 2

5

您看到此行为的原因是因为 F# 没有(~%)像大多数顶级运算符那样使用静态约束进行定义。它被定义为一个函数Quotations.Expr<'a> -> 'a。因此,您在 type 上定义的(~%)函数(它是 的别名)不能通过使用顶级运算符来解析。op_SpliceT(~%)

您可以通过以下 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 '~%'

那是因为 的原始定义(~%)被编译器特殊处理以进行引号拼接。实际上,您可以在和签名中看到这些类型根本没有定义任何运算符,更不用说.ExprExpr<'T>op_Splice

&&您可以使用和中||缀运算符看到类似的结果。可以重新定义(映射到op_BooleanAndand op_BooleanOr),但除非它们是,否则编译器会对其进行特殊处理。

于 2012-05-06T17:16:46.350 回答
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 @>
于 2012-05-06T10:29:45.113 回答