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

时间:2012-05-06 10:17:11

标签: f#

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

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

错误消息显示t应为Quotation.Expr<'a>类型。 %应该是valid prefix operator,但实际可以使用吗?

2 个答案:

答案 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 '~%'

这是因为(~%)的原始定义由编译器专门处理引用拼接。实际上,您可以在ExprExpr<'T>个签名中see表示这些类型根本没有定义任何运算符,更不用说op_Splice

您可以在&&||中缀运算符中看到类似的结果。哪个可以重新定义(映射到op_BooleanAndop_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 @>