Reputation: 2380
type T() =
static member (~%)(t : T) = t
let t = T()
let t' = %t // FAILS
The error message says t
was expected to be of type Quotation.Expr<'a>
.
% is a supposedly valid prefix operator, but is it possible to actually use it?
Upvotes: 5
Views: 344
Reputation: 22297
The reason why you are seeing this behavior is because F# does not define (~%)
with static constraints like most top-level operators. It is defined as a function Quotations.Expr<'a> -> 'a
. Hence, the (~%)
function (which is an alias for op_Splice
) you defined on type T
is not resolved by uses of the top-level (~%)
operator.
You can see this by the following FSI interaction:
> <@ (~%) @>;;
<@ (~%) @>;;
^^^^^^^^^^
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.
Thus if we redefine the top-level (~%)
operator as follows, then your example will compile without error:
let inline (~%) (x : ^a) = (^a : (static member op_Splice : ^a -> 'b) (x))
but do note that quotation splicing will no longer work:
let x = <@ 3 @>
<@ %x @>
----^
error FS0001: The type 'Expr<int>' does not support the operator '~%'
that's because the original definition of (~%)
is treated specially by the compiler for quotation splicing. Indeed, you can see in the Expr
and Expr<'T>
signatures that those types do not define any operators at all, let alone op_Splice
.
You can see similar results with &&
and ||
infix operators. Which can be redefined (mapping to op_BooleanAnd
and op_BooleanOr
), but unless they are, they are treated specially by the compiler.
Upvotes: 5
Reputation: 243061
I'm not exactly sure why the %
operator behaves like this, but you can redefine it using global let
binding:
let (~%) a = -a
%10
If the operator cannot be defined as static
member (I'm not sure if that's the case, or if I'm just missing something), you can still define an inline
definition that invokes some static member of an object. This should give you essentially the same functionality:
// 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
Background: In F# quotation code, it is used for "splicing" of expressions into another expression (to build an expression that is composed from another, previously defined expression). The error message suggests that the compiler did not see your definition.
let two = <@ 2 @>
let oneAndTwo = <@ 1 + %two @>
Upvotes: 4