title |
---|
Functions |
Quick overview: Functions
Functions are a core part of any language. They perform logic and return values based on the arguments provided.
There is a comprehensive Cheat-sheet at the end.
Define named functions with a let binding, parentheses ()
,
and an arrow =>
:
let add = (x, y) => {
x + y;
};
Short function bodies can leave out the surrounding block:
let add = (x, y) => x + y;
Call functions using their name and a comma separated list of arguments:
let twentyThree = add(10, 13);
- Note: There is no
return
keyword. See implicit return for details.
Use a unit
argument when writing a function that should
have "no arguments". This is commonly used when a function has side-effects.
The unit argument looks like ()
:
let launchMissile = () => {
someSideEffects();
print_endline("Missiles have been launched!");
};
launchMissile();
Arguments can be named using a ~
prefix. This can be helpful when dealing with
many function arguments, or arguments with the same types.
let makeCircle = (~x, ~y, ~radius) => { ... };
Call functions with named arguments using an =
:
makeCircle(~x=5, ~y=5, ~radius=10);
Because the arguments are named, the calling order can be changed:
makeCircle(~radius=10, ~y=5, ~x=5);
Functions can be created inline, they do not have to have a name and let binding. This is helpful when working with functions that accept other functions as arguments.
/* Not inline */
let double = value => value * 2;
let x = List.map(double, x);
/* Inline function */
let x = List.map(value => value * 2, x);
See Recursion for details on defining recursive functions.
let rec infiniteRecursion = () => infiniteRecursion();
Reason functions can be partially called. This is like creating a new function with some of the arguments already set as a particular value.
let add = (x, y) => x + y;
let addFive = add(5);
let eleven = addFive(6);
let twelve = addFive(7);
If you want to partially call a function with an argument that is not the first
argument use a _
to skip arguments:
let divide = (a, b) => a / b;
let half = divide(_, 2);
let five = half(10);
When defining a function, named arguments can be marked as non-mandatory. This means they do not need to be supplied as arguments by the caller. You may simultaneously mark a named argument as non-mandatory, and specify a default value (in the event it was not supplied by the caller).
Use =?
after a named argument to mark it as non-mandatory. Inside the function
this value will be wrapped in an option based on whether or not the caller
provided the value.
Notice: The caller provides an int
, but inside the function value
is
option(int)
.
let addOne = (~value=?, ()) => {
switch (value) {
| Some(value) => value + 1
| None => 1
};
};
addOne(); /* 1 */
addOne(~value=11, ()); /* 12 */
Default values can be provided to named arguments using =value
.
let makeCircle = (~x=0, ~y=0, ~radius=10, ()) => { ... };
/* Position (0, 0) with radius 10 */
makeCircle();
/* Position (10, 0) with radius 2 */
makeCircle(~x=10, ~radius=2, ());
In the above examples a final unit argument was added to the function definitions. This is related to the partial application feature. When using non-mandatory arguments that can be omitted at call sites, the compiler needs some indication of when you are "done" calling a function.
Specifically, there must be a positional argument after all non-mandatory arguments so the compiler can determine whether to partially apply the provided arguments or evaluate the function using default values.
Consider if there were no final unit argument to makeCircle
, what
should this do:
makeCircle(~radius=1);
It is not clear if that should return a function waiting for x
and y
coordinates, or if should default the x
and y
coordinates to zeroes. With
the final unit argument these cases are disambiguated:
/* Function waiting for x and y coordinates. */
let makeUnitCircle = makeCircle(~radius=1);
let c = makeUnitCircle(~x=5, ~y=5, ());
/* Creates a circle at (0, 0) with radius 1 */
let c = makeCircle(~radius=1, ());
Sometimes you will want to provide a non-mandatory argument based on whether
a variable is Some(data)
or None
.
let fn = (~data=?, ()) => { ... };
let x = Some(100);
/* Provide data based on whether x is Some or None */
switch (x) {
| Some(data) => fn(~data, ());
| None => fn();
};
Passing x
directly to ~data
will not work because the callsite does not
expect an option type.
/* Error: Incorrect type */
fn(~data=x, ());
Reason provides a shorthand syntax for the pattern above. Prefixing a named
argument's value with a question mark at the call site will pass whatever is
inside of the Some()
, but if the value is None
, then the named argument
will not be supplied at all.
let a = Some(100);
let b = None;
/* fn is called like `fn(~data=100, ())` */
fn(~data=?a, ());
/* fn is called like `fn()` */
fn(~data=?b, ());
Note: This shorthand works with non-mandatory arguments that have default values too.
When defining default values, previous arguments may be used:
let add = (a, ~b, ~c=a+1, ~d=b+1, ()) => a + b + c + d;
add(1, ~b=1, ()); /* 6 */
add(1, ~b=1, ~c=10, ()); /* 14 */
There is no way to define a function that accepts a variable number of arguments in Reason. A workaround can be to accept a list as the final argument.
Feature | Example |
---|---|
Anonymous function | (x, y) => x + y |
Named function | let add = (x, y) => x + y; |
Named arguments | let add = (~x, ~y) => x + y; |
Non-mandatory arguments | let add = (~x=?, ~y=?, ()) => { ... }; |
Non-mandatory with default | let add = (~x=1, ~y=1, ()) => x + y; |
Feature | Example |
---|---|
Anonymous function | (x: int, y: int): int => x + y |
Named function | let add = (x: int, y: int): int => x + y; |
Named arguments | let add = (~x: int, ~y: int): int => x + y; |
Non-mandatory arguments | let add = (~x: option(int)=?, ~y: option(int)=?, ()): int => { ... }; |
Non-mandatory with default | let add = (~x: int=1, ~y: int=1, ()): int => x + y; |
Feature | Example |
---|---|
Normal arguments | add(1, 2) |
Named arguments | add(~x=1, ~y=2, ()) |
Named argument punning | add(~x, ~y, ()) |
Partial application | let addTen = add(10); |
Partial application out of order | let half = divide(_, 2); |
Options to Non-mandatory arguments | add(~x=?Some(1), ~y=?foo, ()) |
Feature | Example |
---|---|
Normal function | type f = (int, int) => int; |
Named arguments | type f = (~x: int, ~y: int) => int; |
Non-mandatory arguments | type f = (~x: int=?, ~y: int=?, unit) => int; |