F# custom operator with 3 parameters

177 Views Asked by At

I found the following piece of code in the fantomas library for F#. I am having a hard time understanding this as an F# noob. From what I understand, it's a custom operator that takes 3 arguments, but why would an operator need 3 arguments? And what exactly is happening here?

/// Function composition operator
let internal (+>) (ctx: Context -> Context) (f: _ -> Context) x =
    let y = ctx x

    match y.WriterModel.Mode with
    | ShortExpression infos when
        infos
        |> Seq.exists (fun x -> x.ConfirmedMultiline)
        ->
        y
    | _ -> f y

Here's an example of how fantomas uses this operator in ther CodePrinter module.

    let short =
        genExpr astContext e1
        +> sepSpace
        +> genInfixOperator "=" operatorExpr
        +> sepSpace
        +> genExpr astContext e2
2

There are 2 best solutions below

4
citykid On

Operators behave a lot like function names:

let (++) a b c d =
    a + b + c + d

(++) 1 2 3 4

One difference is that operators can be used infix. An operator with more than 2 arguments allows infix only for the first 2 arguments:

// the following is equal:
let f = (++) 1 2 // like a function name
let f = 1 ++ 2   // with infix
f 50 60

I did not find how fantomas uses the operator you mention, would be curious, in particular since fantomas is a high profile f# project.

0
Alphacat On

It might be instructive to compare this to the regular function composition operator, >>. The definition for this is:

let (>>) (f : a' -> b') (g : b' -> c') (x : a') =
   g ( f x )

Esentially, it applies f to x, and then applies g to the result.

If we have the following functions:

let plusOne i = i + 1
let timesTwo j = j * 2

And apply it the following way:

let plusOneTimesTwo = plusOne >> timesTwo

What we're really doing is something like this:

let plusOneTimesTwo = (>>) plusOne timesTwo

When you don't supply all of the necessary arguments to a function (in this case, x), what you get is a function that takes the remaining arguments and then returns what the original function would return (this is partial application.) In this case, plusOneTimesTwo's function signature is now x : int -> int.

The example you've listed is essentially the same thing, but it's performing additional logic to determine whether it wants to apply the second function to the result y or to return it as-is.

Related Questions in F#