Black lives matter

# ReasonML: functions

[2017-12-16] dev

This blog post explores how functions work in ReasonML.

## Defining functions  #

An anonymous (nameless) function looks as follows:

``````(x) => x + 1;
``````

This function has a single parameter, `x`, and the body `x + 1`.

You can give that function a name by binding it to a variable:

``````let plus1 = (x) => x + 1;
``````

This is how you call `plus1`:

``````# plus1(5);
- : int = 6
``````

### Functions as parameters of other functions (high-order functions)  #

Functions can also be parameters of other functions. To demonstrate this feature, we briefly use lists, which will be explained in detail in a later blog post. Lists are, roughly, singly linked lists and similar to immutable arrays.

The list function `List.map(func, list)` takes `list`, applies `func` to each of its elements and returns the results in a new list. For example:

``````# List.map((x) => x + 1, [12, 5, 8, 4]);
- : list(int) = [13, 6, 9, 5]
``````

Functions that have functions as parameters or results are called higher-order functions. Functions that don’t are called first-order functions. `List.map()` is a higher-order function. `plus1()` is a first-order function.

### Blocks as function bodies  #

A function’s body is an expression. Given that scope blocks are expressions, the following two definitions for `plus1` are equivalent.

``````let plus1 = (x) => x + 1;

let plus1 = (x) => {
x + 1
};
``````

### Single parameters without parentheses  #

If a function has a single parameter and that parameter is defined via an identifier, you can omit the parentheses:

``````let plus1 = x => x + 1;
``````

### Avoiding warnings about unused parameters  #

ReasonML compilers and editor plugins warn you about unused variables. For example, for the following function, you get the warning “unused variable y”.

``````let getFirstParameter = (x, y) => x;
``````

You can avoid the warning by prefixing the names of unused variables with underscores:

``````let getFirstParameter = (x, _y) => x;
``````

You can also use the variable name `_`, multiple times:

``````let foo = (_, _) => 123;
``````

## Recursive bindings via `let rec`#

Normally, you can only refer to `let`-bound values that already exist. That means that you can’t define mutually recursive and self-recursive functions via `let`.

### Defining mutually recursive functions  #

Let’s examine mutually recursive functions first. The following two functions `even` and `odd` are mutually recursive. You must use the special `let rec` to define them:

``````let rec even = (x) =>
switch x {
| n when n < 0 => even(-x) /* A */
| 0 => true /* B */
| 1 => false /* C */
| n => odd(n - 1)
}
and odd = (x) => even(x - 1);
``````

Notice how `and` connects multiple `let rec` entries that all know each other. There is no semicolon before the `and`. The semicolon at the end indicates that `let rec` is finished.

`even` and `odd` are defined in terms of each other:

• An integer `x` is even if `x-1` is odd.
• An integer `x` is odd if `x-1` is even.

Obviously, this can’t go on forever, so we need to define base cases in `even()` (lines B and C). We also take care of negative integers there (line A).

Let’s use these functions:

``````# even(11);
- : bool = false
# odd(11);
- : bool = true

# even(24);
- : bool = true
# odd(24);
- : bool = false

# even(-1);
- : bool = false
# odd(-1);
- : bool = true
``````

### Defining self-recursive functions  #

You also need `let rec` for functions that call themselves recursively, because when the recursive call appears, the binding does not exist, yet. For example:

``````let rec factorial = (x) =>
if (x <= 0) {
1
} else {
x * factorial(x - 1)
};

factorial(3); /* 6 */
factorial(4); /* 24 */
``````

## Terminology: arity  #

The arity of a function is the number of its (positional) parameters. For example, the arity of `factorial()` is 1. The following adjectives describe functions with arities from 0 to 3:

• A nullary function is a function with arity 0.
• A unary function is a function with arity 1.
• A binary function is a function with arity 2.
• A ternary function is a function with arity 3.

Beyond arity 3, we talk about 4-ary functions, 5-ary functions, etc. Functions whose arity can vary, are called variadic functions.

## The types of functions  #

Functions are the first time that we get in contact with complex types: types built by combining other types. Let’s use ReasonML’s command line `rtop` to determine the types of two functions.

### Types of first-order functions  #

First, a function `add()`:

``````# let add = (x, y) => x + y;
let add: (int, int) => int = <fun>;
``````

Therefore, the type of `add` is:

``````(int, int) => int
``````

The arrow indicates that `add` is a function. Its parameters are two ints. Its result is a single int.

The notation `(int, int) => int` is also called the (type) signature of `add`. It describes the types of its inputs and its outputs.

### Types of higher-order functions  #

Second, a higher-order function `callFunc()`:

``````# let callFunc = (f) => f(1) + f(2);
let callFunc: ((int) => int) => int = <fun>;
``````

You can see that the parameter of `callFunc` is itself a function and has the type `(int) => int`.

This is how `callFunc()` is used:

``````# callFunc(x => x);
- : int = 3
# callFunc(x => 2 * x);
- : int = 6
``````

### Type annotations and type inference  #

In some statically typed programming languages, you have to provide type annotations for all parameters and the return value of a function. For example:

``````# let add = (x: int, y: int): int => x + y;
let add: (int, int) => int = <fun>;
``````

The first two occurrences of `: int` are type annotations for the parameters `x` and `y`. The last `: int` declares the result of `add`.

However, ReasonML allows you to omit the return type. It then uses type inference to deduce the return type from the types of the parameters and the function’s body:

``````# let add = (x: int, y: int) => x + y;
let add: (int, int) => int = <fun>;
``````

However, ReasonML’s type inference is even more sophisticated than that. It does not only deduce types top-down (from the parameters to the body to the result). It can also deduce types bottom-up. For example, it can infer that `x` and `y` are ints, due to them being operands of the int operator `+`:

``````# let add = (x, y) => x + y;
let add: (int, int) => int = <fun>;
``````

In other words: most type annotations in ReasonML are optional.

### Type annotations can lead to more specific inferred types  #

Even if type annotations are optional, providing them sometimes increases the specificity of types. Consider, for example, the following function `createIntPair`:

``````# let createIntPair = (x, y) => (x, y);
let createIntPair: ('a, 'b) => ('a, 'b) = <fun>;
``````

All the types that ReasonML infers are type variables. These start with apostrophes and mean “any type”. They are explained in more detail later.

If we annotate the parameters, we get more specific types:

``````# let createIntPair = (x: int, y: int) => (x, y);
let createIntPair: (int, int) => (int, int) = <fun>;
``````

We also get more specific types if we only annotate the return value:

``````# let createIntPair = (x, y): (int, int) => (x, y);
let createIntPair: (int, int) => (int, int) = <fun>;
``````

### Best practice: annotate all parameters  #

The coding style I prefer for functions is to annotate all parameters, but to let ReasonML infer the return type. Apart from improving type checking, annotations for parameters are also good documentation (which is automatically checked for consistency).

## There are no functions without parameters  #

There are no nullary functions in ReasonML, but you will rarely notice when using it.

For example, if you define a function that has no parameters, ReasonML adds a parameter for you, whose type is `unit`:

``````# let f = () => 123;
let f: (unit) => int = <fun>;
``````

Recall that the type `unit` only has the single element `()`, which means “no value” (it’s roughly similar to `null` in many C-style languages).

When calling functions, omitting parameters is the same as passing `()` as a single parameter:

``````# f();
- : int = 123
# f(());
- : int = 123
``````

The following interaction is another demonstration of this phenomenon: If you call a unary function without parameters, `rtop` underlines `()` and complains about that expression having the wrong type. Notably, it does not complain about missing parameters (it doesn’t partially apply either – details later).

``````# let times2 = (x) => x * 2;
let times2: (int) => int = <fun>;
# times2();
Error: This expression has type unit but
an expression was expected of type int
``````

To summarize, ReasonML does not have nullary functions, but it hides that fact both when defining and when calling functions.

### Why no nullary functions?  #

Why doesn’t ReasonML have nullary functions? That is due to ReasonML always performing partial application (explained in detail later): If you don’t provide all of a function’s parameters, you get a new function from the remaining parameters to the result. As a consequence, if you could actually provide no parameters at all, then `func()` would be the same as `func`. That is, the former wouldn’t do anything.

## Destructuring function parameters  #

Destructuring can be used wherever variables are bound to values: in `let` expressions, but also in parameter definitions. The latter is used in the following function, which computes the sum of the components of a tuple:

``````let addComponents = ((x, y)) => x + y;
let tuple = (3, 4);
``````

The double parentheses around `x, y` indicate that `addComponents` is a function with a single parameter, a tuple whose components are `x` and `y`. It is not a function with the two parameters `x` and `y`. Its type is:

``````addComponents: ((int, int)) => int
``````

When it comes to type annotations, you can either annotate the components:

``````# let addComponents = ((x: int, y: int)) => x + y;
let addComponents: ((int, int)) => int = <fun>;
``````

Or you can annotate the whole parameter:

``````# let addComponents = ((x, y): (int, int)) => x + y;
let addComponents: ((int, int)) => int = <fun>;
``````

## Labeled parameters  #

So far, we have only used positional parameters: the position of an actual parameter at the call site determines what formal parameter it is bound to.

But ReasonML also supports labeled parameters. Here, labels are used to associate actual parameters with formal parameters.

As an example, let’s examine a version of `add` that uses labeled parameters:

``````let add = (~x, ~y) => x + y;
``````

In this function definition, we used the same name for the label `~x` and the parameter `x`. You can also use separate names, e.g. `~x` for the label and `op1` for the parameter:

``````let add = (~x as op1, ~y as op2) => op1 + op2;
``````

At call sites, you can abbreviate `~x=x` as just `~x`:

``````let x = 7;
let y = 9;
``````

One nice feature of labels is that you can mention labeled parameters in any order:

``````# add(~x=3, ~y=4);
- : int = 7
- : int = 7
``````

### Compatibility of function types with labeled parameters  #

There is one unfortunate caveat to being able to mention labeled parameters in any order: function types are only compatible if labels are mentioned in the same order.

Consider the following three functions.

``````let add = (~x, ~y) => x + y;
``````

`addxy` works as expected with `add`:

``````# addxy(add);
- : int = 7
``````

However, with `addyx`, we get an error, because the labels are in the wrong order:

``````# addyx(add);
Error: This expression has type
(~x: int, ~y: int) => int
but an expression was expected of type
(~y: int, ~x: int) => int
``````

## Optional parameters  #

In ReasonML, only labeled parameters can be optional. In the following code, both `x` and `y` are optional.

``````let add = (~x=?, ~y=?, ()) =>
switch (x, y) {
| (Some(x'), Some(y')) => x' + y'
| (Some(x'), None) => x'
| (None, Some(y')) => y'
| (None, None) => 0
};

add(~x=7, ~y=2, ()); /* 9 */
``````

Let’s examine what this relatively complicated code does.

Why the `()` as the last parameter? That is explained in the next section.

What does the `switch` expression do? `x` and `y` were declared to be optional via `=?`. As a consequence, both have the type `option(int)`. `option` is a variant. Details will be explained in an upcoming blog post. For now, I’ll give a brief preview. The definition of `option` is:

``````type option('a) = None | Some('a);
``````

`option` is used as follows when you call `add()`:

• If you omit the parameter `~x` then `x` will be bound to `None`.
• If you provide the value `123` for `~x` then `x` will be bound to `Some(123)`.

In other words, `option` wraps values and the `switch` expression in the example unwraps them.

### With optional parameters, you need at least one positional parameter  #

Why does `add` have a parameter of type `unit` (an empty parameter, if you will) at the end?

``````let add = (~x=?, ~y=?, ()) =>
···
``````

The reason has to do with partial application (which is explained in detail later). In a nutshell, two things are in conflict here:

• With partial application, if you omit parameters, you create a function that lets you fill in those remaining parameters.
• With optional parameters, if you omit parameters, they should be bound to their defaults.

To resolve this conflict, ReasonML fills in defaults for all missing optional parameters when it encounters the first positional parameter. Before it encounters a positional parameter, it still waits for the missing optional parameters. That is, you always need a positional parameter to trigger the call. Since `add()` doesn’t have one, we added an empty one. The pattern `()` forces that parameter to be `()`, via destructuring.

Another reason for the empty parameter is that, otherwise, we wouldn’t be able to trigger both defaults, because `add()` is the same as `add(())`.

### Type annotations for optional parameters  #

When you annotate optional parameters, they must all have `option(···)` types:

``````let add = (~x: option(int)=?, ~y: option(int)=?, ()) =>
···
``````

The type signature of `add` is:

``````(~x: int=?, ~y: int=?, unit) => int
``````

It’s unfortunate that the definition differs from the type signature in this case. The rationale is that we want to distinguish two things:

• External types: You normally call `add` with ints and not with `option` values.
• Internal types: Internally, you need to process `option` values.

In the next section, we use parameter default values for `add`. Then the internal types are different, but the external types (and therefore the type of `add`) are the same.

### Parameter default values  #

Handling missing parameters can be cumbersome:

``````let add = (~x=?, ~y=?, ()) =>
switch (x, y) {
| (Some(x'), Some(y')) => x' + y'
| (Some(x'), None) => x'
| (None, Some(y')) => y'
| (None, None) => 0
};
``````

In this case, all we want is for `x` and `y` to be zero if they are omitted. ReasonML has special syntax for this:

``````let add = (~x=0, ~y=0, ()) => x + y;
``````

This new version of `add` is used exactly the same way as before: whether or not we handle missing parameters ourselves or automatically (via default values) is hidden from users of the function.

### Type annotations with parameter default values  #

If there are default values, you annotate parameters as follows:

``````let add = (~x: int=0, ~y: int=0, ()) => x + y;
``````

The type of `add` is:

``````(~x: int=?, ~y: int=?, unit) => int
``````

### Passing `option` values to optional parameters (advanced)  #

Internally, optional parameters are received as elements of the `option` type (`None` or `Some(x)`). Until now, you could only pass those values by either providing or omitting parameters. But there is also a way to pass those values directly. Before we get to use cases for this feature, let’s try it out first, via the following function.

``````let multiply = (~x=1, ~y=1, ()) => x * y;
``````

`multiply` has two optional parameters. Let’s start by providing `~x` and omitting `~y`, via elements of `option`:

``````# multiply(~x = ?Some(14), ~y = ?None, ());
- : int = 14
``````

The syntax for passing `option` values is:

``````~label = ?expression
``````

If `expression` is a variable whose name is `label`, you can abbreviate: the following two syntaxes are equivalent.

``````~foo = ?foo
~foo?
``````

So what is the use case? It’s one function forwarding an optional parameter to another function’s optional parameter. That way, it can rely on that function’s parameter default value and doesn’t have to define one itself.

Let’s look at an example: The following function `square` has an optional parameter, which is passes on to `multiply`’s two optional parameters:

``````let square = (~x=?, ()) => multiply(~x?, ~y=?x, ());
``````

`square` does not have to specify a parameter default value, it can use `multiply`’s defaults.

## Partial application  #

Partial application is a mechanism that makes functions more versatile: If you omit one or more parameters at the end of a function call `f(···)`, `f` returns a function that maps the missing parameters to `f`’s final result. That is, you apply `f` to its parameters in multiple steps. The first step is called a partial application or a partial call.

Let’s see how that works. We first create a function `add` with two parameters:

``````# let add = (x, y) => x + y;
let add: (int, int) => int = <fun>;
``````

Then we partially call the binary function `add` to create the unary function `plus5`:

``````# let plus5 = add(5);
let plus5: (int) => int = <fun>;
``````

We have only provided `add`’s first parameter, `x`. Whenever we call `plus5`, we provide `add`’s second parameter, `y`:

``````# plus5(2);
- : int = 7
``````

### Why is partial application useful?  #

Partial application lets you write more compact code. To demonstrate how, we’ll work with a list of numbers:

``````# let numbers = [11, 2, 8];
let numbers: list(int) = [11, 2, 8];
``````

Next, we’ll use the standard library function `List.map`. `List.map(func, myList)` takes `myList`, applies `func` to each of its elements and returns them as a new list.

We use `List.map` to add 2 to each element of `numbers`:

``````# List.map(x => add(2, x), numbers);
- : list(int) = [13, 4, 10]
``````

With partial application we can make this code more compact:

``````# List.map(add(2), numbers);
- : list(int) = [13, 4, 10]
``````

Which version is better? That depends on your taste. The first version is – arguably – more self-descriptive, the second version is more concise.

Partial application really shines with the pipe operator (`|>`) for function composition (which is explained later).

### Partial application and labeled parameters  #

So far, we have only see partial application with positional parameters, but it works with labeled parameters, too. Consider, again, the labeled version of `add`:

``````# let add = (~x, ~y) => x + y;
let add: (~x: int, ~y: int) => int = <fun>;
``````

If we call `add` with only the first labeled parameter, we get a function that maps the second parameter to the result:

``````# add(~x=4);
- : (~y: int) => int = <fun>
``````

Providing only the second labeled parameter works analogously.

``````# add(~y=4);
- : (~x: int) => int = <fun>
``````

That is, labels don’t impose an order here. That means that partial application is more versatile with labels, because you can partially apply any labeled parameter, not just the last one.

#### Partially application and optional parameters  #

How about optional parameters? The following version of `add` has only optional parameters:

``````# let add = (~x=0, ~y=0, ()) => x + y;
let add: (~x: int=?, ~y: int=?, unit) => int = <fun>;
``````

If you mention only the label `~x` or only the label `~y`, partial application works as before (with the addition of the positional parameter of type `unit`):

``````# add(~x=3);
- : (~y: int=?, unit) => int = <fun>
- : (~x: int=?, unit) => int = <fun>
``````

However, if you provide the positional parameter, you don’t apply partially. The defaults are filled in immediately:

``````# add(~x=3, ());
- : int = 3
- : int = 3
``````

Even if you take one or two intermediate steps, the `()` is always needed to trigger the actual function call. One intermediate step looks as follows.

``````# let plus5 = add(~x=5);
let plus5: (~y: int=?, unit) => int = <fun>;
# plus5(());
- : int = 5
``````

Two intermediate steps:

``````# let plus5 = add(~x=5);
let plus5: (~y: int=?, unit) => int = <fun>;
# let result8 = plus5(~y=3);
let result8: (unit) => int = <fun>;
# result8(());
- : int = 8
``````

Currying is one technique for implementing partial application for positional parameters. Currying a function means transforming it from a function with an arity of 1 or more to a series of unary function calls.

For example, take the binary function `add`:

``````let add = (x, y) => x + y;
``````

To curry `add` means to convert it to the following function:

``````let add = x => y => x + y;
``````

Now we have to invoke `add` as follows:

``````# add(3)(1);
- : int = 4
``````

What have we gained? Partial application is easy now:

``````# let plus4 = add(4);
let plus4: (int) => int = <fun>;
# plus4(7);
- : int = 11
``````

And now the surprise: all functions in ReasonML are automatically curried. That’s how it supports partial application. You can see that if you look at the type of the curried `add`:

``````# let add = x => y => x + y;
let add: (int, int) => int = <fun>;
``````

On other words: `add(x, y)` is the same as `add(x)(y)` and the following two types are equivalent:

``````(int, int) => int
int => int => int
``````

Let’s conclude with a function that curries binary functions. Given that currying functions that are already curried is meaningless, we’ll curry a function whose single parameter is a pair.

``````let curry2 = (f: (('a, 'b)) => 'c) => x => y => f((x, y));
``````

Let’s use `curry2` with a unary version of `add`:

``````# let add = ((x, y)) => x + y;
let add: ((int, int)) => int = <fun>;
- : (int, int) => int = <fun>
``````

The type at the end tells us that we have successfully created a binary function.

## The reverse-application operator (`|>`)  #

The operator `|>` is called reverse-application operator or pipe operator. It lets you chain function calls: `x |> f` is the same as `f(x)`. That may not look like much, but it is quite useful when combining function calls.

### Example: piping ints and strings  #

``````let times2 = (x: int) => x * 2;
let twice = (s: string) => s ++ s;
``````

If we use them with traditional function calls, we get:

``````# twice(string_of_int(times2(4)));
- : string = "88"
``````

First we apply `times2` to 4, then `string_of_int` (a function in the standard library) to the result, etc. The pipe operator lets us write code that is closer to the description that I have just given:

``````let result = 4 |> times2 |> string_of_int |> twice;
``````

### Example: piping lists  #

With more complex data and currying, we get a style that is reminiscent of chained method calls in object-oriented programming.

For example, the following code works with a list of ints:

``````[4, 2, 1, 3, 5]
|> List.map(x => x + 1)
|> List.filter(x => x < 5)
|> List.sort(compare);
``````

These functions will be explained in detail in an upcoming blog post. For now, it is enough to have a rough idea of how they work.

The three computational steps are:

``````# let l0 = [4, 2, 1, 3, 5];
let l0: list(int) = [4, 2, 1, 3, 5];
# let l1 = List.map(x => x + 1, l0);
let l1: list(int) = [5, 3, 2, 4, 6];
# let l2 = List.filter(x => x < 5, l1);
let l2: list(int) = [3, 2, 4];
# let l3 = List.sort(compare, l2);
let l3: list(int) = [2, 3, 4];
``````

We see that in all of these functions, the primary parameter comes last. When we piped, we first filled in the secondary parameters via partial application, creating a function. Then the pipe operator filled in the primary parameter, by calling that function.

The primary parameter is similar to `this` or `self` in object-oriented programming languages.

### Caveat: piping into unary functions  #

When you use partial application to create the operands for the pipe operator, there is one mistake that is easy to make. See if you can spot that mistake in the following code.

``````let conc = (x, y) => y ++ x;

"a"
|> conc("b")
|> conc("c")
|> print_string();

/* Error: This expression has type unit
but an expression was expected of type string */
``````

The problem is that we are trying to partially apply zero parameters. That doesn’t work, because `print_string()` is the same as `print_string(())`. And `print_string`’s single parameter is of type `string` (not of type `unit`).

If you omit the parentheses after `print_string`, everything works as intended:

``````"a"
|> conc("b")
|> conc("c")
|> print_string;

/* Output: abc */
``````

## Tips for designing function signatures  #

These are a few tips for designing the type signatures of functions:

• Primary parameters should be positional:
• If a function has a single primary parameter, make it a positional parameter and put it at the end. That supports the pipe operator for function composition.
• Some functions have multiple primary parameters that are all similar. Turn these into multiple positional parameters at the end. An example would be a function that concatenates two lists into a single list. In that case, both positional parameters are lists.
• Exception to this rule: If there are two or more primary parameters that are different, all of them should be labeled.
• All other parameters should be labeled
• If a function has only a single parameter, it tends to be unlabeled, even if it is not strictly primary.
• A function should always have at least one positional parameter.
• Rationale: If you want to be able to transparently add optional parameters later on, you need a positional parameter. Being able to do this without changing call sites helps with evolving APIs.
• If no positional parameter make sense, simply use `()`:
``````let makePoint = (~x, ~y, ()) => (x, y);
``````
We match `()` as a positional parameter via destructuring, but never use that value.
• Note that ReasonML automatically adds the parameter `()` to nullary functions, anyway.

The idea behind these rules is to make code as self-descriptive as possible: The primary (or only) parameter is described by the name of the function, the remaining parameters are described by their labels.

As soon as a function has more than one positional parameter, it usually becomes difficult to tell what each parameter does. Compare, for example, the following two function calls. The second one is much easier to understand.

``````blit(bytes, 0, bytes, 10, 10);
blit(~src=bytes, ~src_pos=0, ~dst=bytes, ~dst_pos=10, ~len=10);
``````

The listed rules should also influence the name of the function. See how it works with labeled parameters (if there are any) and without them: Are function calls pleasant to read? Does the function name work as a description of the positional parameter?

ReasonML’s naming style reminds me of invoking commands in Unix shells.

Source of this section: Sect. “Suggestions for labeling” in the OCaml Manual.

## Single-argument match functions  #

ReasonML provides an abbreviation for unary functions that immediately switch on their parameters. Take, for example the following function.

``````let divTuple = (tuple) =>
switch tuple {
| (_, 0) => (-1)
| (x, y) => x / y
};
``````

This function is used as follows:

``````# divTuple((9, 3));
- : int = 3
# divTuple((9, 0));
- : int = -1
``````

If you use the `fun` operator to define `divTuple`, the code becomes shorter:

``````let divTuple =
fun
| (_, 0) => (-1)
| (x, y) => x / y;
``````

## Operators  #

One neat feature of ReasonML is that operators are just functions. You can use them like functions if you put them in parentheses:

``````# (+)(7, 1);
- : int = 8
``````

And you can define your own operators:

``````# let (+++) = (s, t) => s ++ " " ++ t;
let ( +++ ): (string, string) => string = <fun>;
# "hello" +++ "world";
- : string = "hello world"
``````

By putting an operator in parentheses, you can also easily look up its type:

``````# (++);
- : (string, string) => string = <fun>
``````

### Rules for operators  #

There are two kinds of operators: infix operators (between two operands) and prefix operators (before single operands).

The following operator characters can be used for both kinds of operators:

``````! \$ % & * + - . / : < = > ? @ ^ | ~
``````

Infix operators:

First character Followed by operator characters
`= < > @ ^ ❘ & + - * / \$ %` 0+
`#` 1+

Additionally, the following keywords are infix operators:

``````* + - -. == != < > || && mod land lor lxor lsl lsr asr
``````

Prefix operators:

First character Followed by operator characters
`!` 0+
`? ~` 1+

Additionally, the following keywords are prefix operators:

``````- -.
``````

Source of this section: Sect. “Prefix and infix symbols” in the OCaml Manual.

### Precedences and associativities of operators  #

The following tables lists operators and their associativities. The higher up an operator, the higher its precedence is (the stronger it binds). For example, `*` has a higher precedence than `+`.

Construction or operator Associativity
prefix operator
`. .( .[ .{`
`[ ]` (array index)
`#···`
applications, `assert`, `lazy` left
`- -.` (prefix)
`**··· lsl lsr asr` right
`*··· /··· %··· mod land lor lxor` left
`+··· -···` left
`@··· ^···` right
`=··· <··· >··· ❘··· &··· \$··· !=` left
`&&` right
`❘❘` right
`if`
`let switch fun try`

Legend:

• `op···` means `op` followed by other operator characters.
• Applications: function application, constructor application, tag application

Source of this table: Sect. “Expressions” in the OCaml manual

### When does associativity matter?  #

Associativity matters whenever an operator is not commutative. With a commutative operator, the order of the operands does not matter. For example, plus (`+`) is commutative. However, minus (`-`) is not commutative.

Left associativity means that operations are grouped from the left. Then the following two expressions are equivalent:

``````x op y op z
(x op y) op z
``````

Minus (`-`) is left-associative:

``````# 3 - 2 - 1;
- : int = 0
``````

Right associativity means that operations are grouped from the right. Then the following two expressions are equivalent:

``````x op y op z
x op (y op z)
``````

We can define our own right-associative minus operator. According to the operator table, if it starts with an `@` symbol, it is automatically right-associative:

``````let (@-) = (x, y) => x - y;
``````

If we use it, we get a different result than normal minus:

``````# 3 @- 2 @- 1;
- : int = 2
``````

## Polymorphic functions  #

Recall the definition of polymorphism from a previous blog post: making the same operation work for several types. There are multiple ways in which polymorphism can be achieved. OOP languages achieve it via subclassing. Overloading is another popular kind of polymorphism.

ReasonML supports parametric polymorphism: Instead of using a concrete type such as `int` for a parameter or a result, you use a type variable. If the type of a parameter is a type variable, values of any type are accepted (in case you are interested: such type variables are universally quantified). Type variables can be viewed as parameters of the function type; hence the name parametric polymorphism.

A function that uses type variables is called a generic function.

### Example: `id()`#

For example, `id` is the identity function that simply returns its parameter:

``````# let id = x => x;
let id: ('a) => 'a = <fun>;
``````

The type for `id` that ReasonML infers is interesting: It can’t detect a type for `x`, so it uses the type variable `'a` to indicate “any type”. All types whose names start with apostrophes are type variables. ReasonML also infers that the return type of `id` is the same as the type of its parameter. That is useful information and helps with inferring the type of `id`’s result.

In other words: `id` is generic and works with any type.

``````# id(123);
- : int = 123
# id("abc");
- : string = "abc"
``````

### Example: `first()`#

Let’s look another example: a generic function `first` for accessing the first component of a pair (a 2-tuple).

``````# let first = ((x, y)) => x;
let first: (('a, 'b)) => 'a = <fun>;
``````

`first` uses destructuring to access the first component of that tuple. Type inference tells us that the return type is the same as the type of the first component.

We can use an underscore to indicate that we are not interested in the second component:

``````# let first = ((x, _)) => x;
let first: (('a, 'b)) => 'a = <fun>;
``````

With a type-annotated component, `first` looks as follows:

``````# let first = ((x: 'a, _)) => x;
let first: (('a, 'b)) => 'a = <fun>;
``````

### Example: `ListLabels.map()`#

As a quick preview, I’m showing the signature of another function that I’ll explain properly in an upcoming blog post.

``````ListLabels.map: (~f: ('a) => 'b, list('a)) => list('b)
``````

• Overloading provides different implementations for the same operation. For example, some programming languages let you use `+` for arithmetic, string concatenation and/or array concatenation.

• Parametric polymorphism specifies a single implementation that works with several types.

## ReasonML does not support variadic functions  #

ReasonML does not support variadic functions (e.g. via varargs). That is, you can’t define a function that computes the sum of an arbitrary number of parameters:

``````let sum = (x0, ···, xn) => x0 + ··· + xn;
``````

Instead, you are forced to define one function for each arity:

``````let sum2(a: int, b: int) = a + b;
let sum3(a: int, b: int, c: int) = a + b + c;
let sum4(a: int, b: int, c: int, d: int) = a + b + c + d;
``````

You have seen a similar technique with currying, where we couldn’t define a variadic function `curry()` and had to go with a binary `curry2()`, instead. You’ll occasionally see it in libraries, too.

An alternative to this technique is to use lists of ints.