Operators

Definition

Function whose name is a set of symbols

  • Unary operator: let (~symbols) = ...

  • Binary operator: let (symbols) = ...

  • Symbols = combination of % & * + - . / < = > ? @ ^ | ! $

2 ways of using operators:

  1. As an operator: 1 + 2

    • without the ()

    • prefix position if unary -1.

    • infix position id binary 1 + 2

  2. As a function: (+) 1 2

    • with the ()

Standard operators

Also defined in FSharp.Core.

  • Arithmetic operators: +, -...

  • Pipeline operators

  • Composition operators

Piping operators

Binary operators, placed between a simple value and a function

  • Apply the value to the function = Pass value as argument

  • Avoid parentheses otherwise required for precedence reason

  • There are several pipes:

    • Pipe right |> : the usual pipe

    • Pipe left <| a.k.a. reversed pipe

    • Pipe right 2 ||>

    • Etc.

Pipe right |>

Reverse the order between function and value: val |> fn ≑ fn val

  • Natural "subject-verb" order, like a method call of an object (obj.M(x))

  • Pipeline: chain function calls, without intermediate variables

  • Help the type inference - example:

Pipe left <|

fn <| expression ≑ fn (expression)

  • ☝ Usage a little less common than |>

  • βœ… Minor advantage: avoids parentheses

  • ❌ Major disadvantage: reads from right to left β†’ Reverses natural English reading direction and execution order

Quid of such expression: x |> fn <| y ❓

Executed from left to right: (x |> fn) <| y ≑ (fn x) <| y ≑ fn x y

  • Goal: use fn as infix

  • Cons: difficult to read because of double reading direction ❗

πŸ‘‰ Tip: TO AVOID

Pipe right 2 ||>

(x, y) ||> fn ≑ fn x y

  • To pass 2 arguments at once, in the form of a tuple

  • Used infrequently, but useful with fold to pass the initial value (seed) and the list before defining the folder function and help the type inference for a lambda folder.

☝️ This operator corresponds to the notion of "uncurrying", i.e. being able to call a curried f function by passing it these 2 parameters in the form of a tuple:

Compose >>

Binary operators, placed between two functions β†’ The result of the 1st function is used as an argument for the 2nd function

f >> g ≑ fun x -> g (f x) ≑ fun x -> x |> f |> g

Reversed Compose <<

Rarely used, except to restore a natural reading order.

Example with not (which replaces the ! in Cβ™―):

Pipe |> or Compose >> ?

Definition
Focus, Mindset

Compose

let h = f >> g

Functions

Pipe

let result = value |> f

Values

Point-free style

A.k.a Tacit programming

Writing functions without mentioning the parameters (referred here as "points"), just by using function composition (1) or partial application (2).

Pros/Cons βš–οΈ

βœ… Pros

  • Concise style

  • At function/operation level, by abstracting the parameters

❌ Cons

Loses the name of the parameter now implicit in the signature β†’ Unimportant if the function remains understandable:

  • When the parameters name is not significant (e.g. x)

  • When the combination of parameters type + function name is unambiguous

  • For private usage - Not recommended for a public API

Limit πŸ›‘

Works poorly with generic functions:

πŸ”— Fβ™― coding conventions > Partial application and point-free programming

Custom operators

2 possibilities:

  • Operator overload

  • Creation of a new operator

Operator overload

Usually concerns a specific type β†’ Overload defined within the associated type (as in Cβ™―)

Creation of a new operator

  • Definition rather in a module or associated type

  • Usual use-case: alias for existing function, used as infix

Symbols allowed in an operator

Unary "tilde" operator β†’ ~ followed by +, -, +., -., %, %%, &, &&

Unary "snake " operator β†’ Several ~, e.g. ~~~~

Unary "bang" operator β†’ ! followed by a combination of !, %, &, *, +, ., /, <, =, >, @, ^, |, ~, ? β†’ Except != which is binary

Binary operator β†’ Any combination of !, %, &, *, +, ., /, <, =, >, @, ^, |, ~, ? β†’ which does not match a unary operator

Usage symbols

All operators are used as is ❗ Except the unary "tilde" operator: used without the initial ~

Operator
Declaration
Usage

Unary tilde

let (~&&) x = …

&&x

Unary snake

let (~~~) x = …

~~~x

Unary bang

let (!!!) x = …

!!!x

Binary

let (<Λ†>) x y = …

x <Λ†> y

☝ To define an operator beginning or ending with a *, you must put a space between ( and * as well as between * and ) to distinguish from a block of Fβ™― comments (* *). β†’ let ( *+ ) x y = x * y + y βœ…

Operator or function?

Infix operator vs function

πŸ‘ Pros:

  • Respects the natural reading order (left β†’ right)

  • Avoids parentheses β†’ 1 + 2 * 3 vs multiply (add 1 2) 3

⚠️ Cons:

  • A "folkloric" operator (e.g. @!) will be less comprehensible than a function whose name uses the domain language.

Using an operator as a function

πŸ’‘ You can use the partial application of a binary operator:

Examples:

  • Instead of a lambda: β†’ (+) 1 ≑ fun x -> x + 1

  • To define a new function : β†’ let isPositive = (<) 0 ≑ let isPositive x = 0 < x ≑ x >= 0

Last updated

Was this helpful?