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?
Prefix Operator. The increment operator ++ if used as prefix on a variable, the value of variable gets incremented by 1. After that the value is returned unlike Postfix operator. It is called Prefix increment operator. In the same way the prefix decrement operator works but it decrements by 1. For example, an example of prefix operator −.
In this notation, operator is prefixed to operands, i.e. operator is written ahead of operands. For example, +ab. This is equivalent to its infix notation a + b.
We can code the prefix operator the same way as we did for the binary + operator. Note that the implementation does not take any argument and the calling object itself serves the data for the first operand. Have a look at the below implementation:
class Number { public: Number& operator++ () // prefix ++ { // Do work on this. (increment your object here) return *this; } // You want to make the ++ operator work like the standard operators // The simple way to do this is to implement postfix in terms of prefix.
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.
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 @>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With