2019-03-28 Cristian Carlesso

Writing purely ReasonML

I’ve recently stumbled upon a series of videos where Tsoding solves some brain teaser from Hacker Rank

In the first episode the problem was to get some integers as an input from the stdio and to sum them together.

This one line[1] is the proposed solution:

``````main
= interact
\$ show
. sum
. tail
. words
``````

We know that OCaml is usually compared to Haskell, and that Reason is basically just OCaml(TM).

Surely it would be easy to use imperative code to solve this problem, but can we reach something as close as possible to the solution above?

One thing that makes the expression above very readable is the chain of function compositions, unfortunately Reason doesn’t provide a `.` operator, but luckily is super easy to define our own operators.

Unfortunately, we cannot use `.` as it’s a reserved token, but we can though take inspiration from F#:

`>>` Composes two functions (forward composition operator)

`<<` Composes two functions but in reverse order: it executes the second one first (backward composition operator).

Let’s define the same operators in ReasonML:

First let’s define a compose function:

``````let compose
= (f, g, x) => g(f(x));
``````

Let’s also define a backwardsCompose function that execute the second function first:

``````let backwardCompose
= (f, g, x) => f(g(x));
``````

Now it’s possible to associate the two operators to these functions:

``````let (>>) = compose;
let (<<) = backwardCompose;
``````

After this, let’s substitute Haskell `.` for `<<` and drop the `main =` part as Reason doesn’t need it:

``````interact
\$ show
<< sum
<< tail
<< words
``````

We’re unfortunately not done yet as those functions don’t exist in Reason, so we must define them ourself.

Let’s temporarily ignore `interact` for a moment and try to define the remaining functions:

• `show` in Haskell converts from a number to a string, given that this problem is the sum of integers, we will use the standard `string_of_int`:
``````let show = string_of_int;
``````
• `sum` takes a list of numbers and return the sum; think of this as a `fold` that apply the `+` operator starting with `0` as the initial value:
``````let sum = List.fold_left((+), 0);
``````
• `map` and `tail` are the Haskell equivalents of respectively `List.map` and the `List.tl` functions:
``````let map = List.map;
let tail = List.tl;
``````
• `read` is the reverse of `show`: it takes a string and converts it to a number, so what we need here is `int_of_string`:
``````let read = int_of_string;
``````
• finally `words` split a string by white spaces, we can apply the `Str.split` and `Str.regexp` functions:
``````let words
= Str.split
@@ Str.regexp("[ \t\n]+");
``````

Notice how here I’ve used the `@@` application operator:

`a @@ b` is equivalent to `a(b)`.

This is the equivalent to `\$` in Haskell, so we can either change the `\$` to `@@` in the original expression or, since it’s not defined yet, try to alias `\$` to `@@`:

``````let (\$) = @@;
``````

We’re only left with `interact`.

`interact` puts all the standard input in a string; it then executes a function with it redirecting the output to the standard output.

I couldn’t find anything similar in ReasonML, so I hacked together the interact function using `read_line` instead:

``````let interact
= f => {
let acc = ref([]);
try (
while (true) {
acc := List.concat(
[
acc^,
]
);
}
) {
| End_of_file =>
String.concat("\n", acc^)
|> f
|> print_string
};
};
``````

Ugh... this uses mutations and imperative code and handling with I/O.

Last thing remaining yet to do is to change the application of `map read` as I’ve describe above in Reason function application is not the space character:

``````interact
\$ show
<< sum
<< tail
<< words
``````

Rewriting this by reversing the functional composition gives us a more top-down flow:

``````interact
\$ words
>> tail
>> sum
>> show;
``````

if we try to compile this, we’ll see that it doesn’t quite work as expected due of how Reason infer the associativity of `\$` and `>>`, and it‘s solved by adding parenthesis:

``````interact
\$ (
words
>> tail
>> sum
>> show
);
``````

The main reason `\$` exists in Haskell is because of associativity though.

Another way to avoid using parentheses we have in Reason is to use the reverse application operator `|>` and moving `interact` to the end:

``````words
>> tail
Apart for the `interact` function we reproduced a solution very similar to the Haskell one, while keeping the same level of purity using mostly a functional approach.