REASON Functions
Functions in reason may look familiar if you are conversant with es6.
Check out the example we had in the previous lesson on getting the newAge
```reason
let newAge = (age) => age + 1;
```
So newAge is a function that takes an int argument and returns the int added to 1.
As you have already seen the code you write gets compiled to JavaScript. For instance if your file was named fn.re then the compiled file will be named fn.bs.js
Functions don’t necessarily need to take any arguments. They can perform an operation by calling other functions within them, functions taking no arguments are said to take unit as the argument. ().
```reason
let resolveDate = () => {
Js.log("Returning the current date")
Js.Date.now()
}
```
So this resolveDate function when called prints to the console of the Js environment its running on. (Browser, node etc) and returns the current date
Multiple Arguments
Functions can take multiple arguments.
Eg: lets define a function that takes 2 strings and returns their concatenation.
Say it takes a first name and then a last name and returns a concatenated string of the FullNames.
```reason
let name = (first, last) => first ++ " " ++ last
```
name(“Jane”, “Doe”) gives us Jane Doe
Reason ML functions are automatically curried. In case you are wondering, currying is the process of breaking down a function with multiple arguments into a series of functions that take partial arguments.
For instance, we can call the function above as
```reason
let firstName = name("John")
let fullName = firstName("Doe")
```
If you open the compiled Js file you will notice something.
FirstName is now a function that takes on one argument and returns the named function with John as the first name and the parameter you will pass as the last.
```js
function name(first, last) {
return first + (" " + last);
}
function firstName(param) {
return name("John", param);
}
var fullName = firstName("Doe");
```
Output :-
The result of fullName is John Doe.
This is all good , but now we can be confused as to which is the first name and position in which we are to place it in the arguments, each time we forget we would have to lookup the function definition. This can slow us down and an alternative to solving this problem would be to use labeled arguments
We can rewrite the function as
```reason
let name = (~firstname, ~lastname) => firstname ++ " " ++ lastname
```
and then refactor the get name functions as follows.
```reason
let somename = name(~lastname="Doe")
let fullName = somename(~firstname="John")
```
Labeled functions are powerful because the order in which you place to the arguments doesn’t matter.
On peeking to the compiled Js we see:
```js
function name(firstname, lastname) {
return firstname + (" " + lastname);
}
function somename(param) {
return name(param, "Doe");
}
var fullName = "John Doe";
```
fullName no longer returns a function call but gives us back a string. A string is less expensive as compared to a function call or a chain of function calls. This is a win for us.
Optional Arguments
Lets say we want to make a function that takes a person’s name and age and returns a string of the name and age, but in this case the age is optional as users can opt not to give their age.
```reason
let toString = (~name, ~age=?, ()) => {
switch age {
| None => "The User's name is " ++ name ++ ""
| Some(agevalue) => "The User's name is " ++ name ++ " and his age is" ++ string_of_int(agevalue)
};
}
```
What we now have is a combination of 2 powerful concepts, Pattern Matching and Variants.
We shall now talk more about those in upcoming lessons.
In general a variant is a data structure that can represent other data structures. (integers, strings, lists etc).
Recall type arguments, well the None and Some come from an option type, which returns either a None or Some of value provided.
```reason
option('a) = None | Some('a)
```
To bring this into context, if age isn’t provided in the function call age gets matched to None
```reason
let alex = toString(~name="Alex", ())
```
and if age is provided it gets matched to Some(age)
```reason
let george = toString(~name="George", ~age=23, ())
```
Note :The Unit () is used so as not to bring about confusion when age is not provided. We don’t want the compiler to curry the function as it awaits an argument that will not be provided.
```reason
let alex = toString(~name="Alex")
```
The above instance will curry the function as it awaits the age param. A peek into the Js file and you will see the following.
```js
function alex(param, param$1) {
return toString("Alex", param, param$1);
}
```
But on providing the Unit the compiler then knows that we have explicitly left out the age parameter.
Refactor the alex let binding back to the original state.
```reason
let alex = toString(~name="Alex", ())
```
and a peek into the Js. You will see the following.
```js
var alex = toString("Alex", undefined, /* () */ 0);
```
Which shows that age has been marked as undefined.
Recursive Functions
Recursive functions are functions that call themselves repeatedly, until a certain condition is met.
Recursive functions have a different signature compared to normal functions. The signature applies to both Ocaml and Reason alike when declaring a recursive function we use the rec keyword.
Let’s define a recursive function that computes the sum of a list’s values.
```reason
let rec sum = lst => {
switch lst {
| [] => 0
| [hd, ...tl] => hd + sum(tl)
};
}
```
Again we are met with pattern matching which we will discuss broadly in upcoming lessons.s
So if the list is empty we return 0, but if the list is not empty we add the head to the recursive call of the same function with the tail of the list.
Note :- Head of a list is the first element.
- Tail of a list is the list minus the head.
- Whenever you want to make a function recursive always include the rec keyword.
```reason
let a = sum([1,2,3,4])
```
With this a will equal 10.
In the next lesson, we will look at variants and how we can use them.