Today I’d like to continue on this path and show you the awesome
To interface with the express Node.js web framework, we may write the following bindings in
src/FFI/Express.ml.(NOTE: Remember to include
bsbresults in the following
Nice! We can run
node lib/js/src/index.jsand get ourselves a running express server.
Consider the type we wrote for the
apprepresenting our express instance, a
stringfor the path, a function (which takes a request and response), and returns a no-op (type
This pattern is very common in JS, and works in the following way: instead of
appand returning a
unit(or no-op), we return another
appwhich we can then use on a subsequent
That’s a lot to unpack, so let’s demonstrate how to get from A to B in code.
So what’s different here? First, we changed the return type of
app. Next we remove the definition for
Then, instead of using
appas the first argument for our second call to
get, we pass in
f. This is type-safe (remember:
express ()all have the same type) and sure enough if we compile this script and run it — we get a working Express app!
In fact, if we wanted to, we could start combining some of these lines by inlining the definition for
fentirely like so:
Or a step further, inlining
These two examples are_identical_to the first, but notice that
appis only referenced once in our code. Let’s peek at BuckleScript’s output
See, once we smush together our
listencalls, there’s no need for temporary variables like
g. BuckleScript knows this, and merely puts everything inline for us — in a “chained” manner.
This may start to look a little LISP-y to you, and that’s fair — this syntax is not easier to read than our original example which specifies
appmultiple times. Let’s move on and see how we can clean up this code a little.
As we start composing functions (like we did by inlining
gin the previous section), we’ll start to see quite a bit of parentheses. Consider the following bit of code:
Sure we can dress this up with further indentation, but developers reading this code will still construct a sort of “stack” in their head as they read the subsequent functions from left to right (“Okay apply discount of the age group of the age of the…”)
To remedy this, OCaml provides the infix
|>(or “pipe”) operator. We can inspect its type via
We see that we take an item of type
a, a function from
band return an item of type
And if we were to use this pipe multiple times:
We can see here how the pipe operator (
|>) allows us to unfold various layers of function composition. It’s quite neat, and leads to some very readable code. Let’s use it with our example above:
How about that last layer? What if we wanted to unfold
Decent! However we hit a snag.
apply_discounttakes_two_arguments, the user’s age group, and a price (
group -> price -> total). If we were to write our code like so:
We would receive a type error because
pricewould be used as the_first_argument to
apply_discount. This means we need some parentheses (technically you could use OCaml’s
@@, but hold your horses), which we are trying to avoid!
One way to fix this?Just make
pricethe first argument!
If we were to redefine
group -> price -> totalto
price -> group -> total, we could then remove our parentheses entirely:
Now price is used as the first argument, and second argument (the age group) makes its way to
apply_discountfrom the pipeline.
“Jordan this is great but I don’t really care about discounts and age groups, I’m trying to write a web server before my startup goes under.”
Well fear no more, let’s return to our express example from earlier.
If we were to swap in some
|>operators, we’ll quickly run into the same exact problem we had with
|>doesn’t really buy us much. Since an
apptype must be the first argument to
listen, we’re left with a confusing mix of parentheses and
As we learned in the previous section, our solution is tomove this argument to the end. Let’s try it with some helper functions:
And use ’em like so:
And voila! An
apptype makes it way from
express (), through the pipe and onto the end of
get_ “/" index. That method also returns an
apptype, which finds its way at the end of
get_ “/about" about, and so on and so forth. We now have ourselves a beautiful, type-safe chain of functions that map to the chainable express API.
listencould work like that for us? Well they can!
The current bindings for
listenare defined using the
@@bs.sendattribute as follows:
However, BuckleScript also provides us with a
@@bs.send.pipewhich, you guessed it, allows us to define functions that work well with the
|>operator.From the docs:
is similar to
except that the first argument, i.e, the object, is put in the position of last argument to help user write in a
Here’s a modified binding for
The difference here is that the first
appin the type definition has been moved into the attribute, right after
@@bs.send.pipe: . Here’s our new definition for
Now, we can swap out
listen_in favor of their original counterparts.
Okay so that was a lot of words to tell you how
@@bs.send.pipeworks, but I hope this post gave you a bit of intuition for why it exists and why you may want to use it. With that, here a few more questions to ponder on:
- You may have noticed that the type of the callback for
req -> res -> resWhy the second
resWell, express has
cookiewhich are also chainable (they return a
Write chainable bindings for these methods.
@@bs.send.pipedid not exist and we were stuck with our old definitions of
listen:could we create a function called
make_chainable get === get_and
make_chainable listen === listen_?
Why or why not? (As a hint: what if
listenboth had three arguments, could we do it then?)