Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define and use % as a prefix operator?

Tags:

f#

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?

like image 772
Joh Avatar asked May 06 '12 10:05

Joh


People also ask

What is the difference between ++ prefix and postfix operator?

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 −.

What is meant by operator is prefixed to operands?

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.

How to code the prefix operator in JavaScript?

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:

How to make ++ operator work like the standard operators?

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.


2 Answers

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.

like image 156
Stephen Swensen Avatar answered Oct 22 '22 16:10

Stephen Swensen


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 @>
like image 32
Tomas Petricek Avatar answered Oct 22 '22 16:10

Tomas Petricek