# Beautiful APIs in CoffeeScript

Let’s say we want to make a maths library in CoffeeScript (e.g. a Matrix library). We could easily write an API for addition that looks like:

``````nine = four.plus five
``````

But what if we want to do this:

``````nine = four plus five
``````

I know it’s only removing a `.`, but I think it looks a bit nicer. Let’s see how to do it.

First thing to note is that this relies on some of CoffeeScript’s syntactic sugar. With brackets, the code is:

``````nine = four(plus(five))
``````

Not exactly pretty, but it allows us to more clearly see what’s going on.

We can see that our numbers need to be functions that take in whatever the result of `plus(five)` is. Let’s create one of these numbers like so:

``````makeNumber = (number) ->
(op) ->

four = makeNumber 4
console.log four # [Function]
``````

(Ideally we’d be writing tests for this stuff before implementing it. Instead I’m using `console.log`. Let’s call this, uh… Log-Driven Development (LDD))

Whatever the result of `plus(five)` is, it’s going to need the other number (`four`) to do the addition. Let’s implement that now.

``````makeNumber = (number) ->
(op) ->
op number
``````

Now that’s done, let’s have a go at implementing the `plus` function.

``````plus = (number) ->
(otherNumber) ->
number + otherNumber
``````

Only, this won’t work. Why not? Well, have a look at the types of `number` and `otherNumber`. `number` is something we created with the `makeNumber` function. A ‘wrapped number’ if you will. `otherNumber` is just a normal number.

How do we add these? We need to ‘unwrap’ `number`. Let’s do this by calling the wrapper with no argument e.g. `four()`. This can be implemented like so:

``````makeNumber = (number) ->
(op) ->
op?(number) or number

four = makeNumber 4
console.log four() # 4
``````

And refactor our `plus` function:

``````plus = (number) ->
(otherNumber) ->
number() + otherNumber
``````

Something’s not quite right about this though. Feels a bit asymmetrical. How about if both `number` and `otherNumber` were both wrapped? Let’s try it.

``````makeNumber = (number) ->
wrapper = (op) ->
op?(wrapper) or number

plus = (number) ->
(otherNumber) ->
number() + otherNumber()
``````

Note how we pass the `wrapper` into `op` in `makeNumber`.

OK, looking good! Let’s put it together and give it a go:

``````makeNumber = (number) ->
wrapper = (op) ->
op?(wrapper) or number

four = makeNumber 4
five = makeNumber 5

plus = (number) ->
(otherNumber) ->
number() + otherNumber()

nine = four plus five
console.log nine() # 9
``````

Last thing to do is to make `plus` return a wrapped number, to keep everything in our nicely wrapped format:

``````plus = (number) ->
(otherNumber) ->
makeNumber number() + otherNumber()
``````

We can easily extend this to other operations, such as multiplication:

``````makeNumber = (number) ->
wrapper = (op) ->
op?(wrapper) or number

four = makeNumber 4
five = makeNumber 5

plus = (number) ->
(otherNumber) ->
makeNumber number() + otherNumber()

times = (number) ->
(otherNumber) ->
makeNumber number() * otherNumber()

nine = four plus five
twenty = four times five
console.log nine() # 9
console.log twenty() # 20
``````

We can even extend it to something like vector addition:

``````makeNumber = (number) ->
wrapper = (op) ->
op?(wrapper) or number

fours = makeNumber [4, 4]
fives = makeNumber [5, 5]

vPlus = (vector) ->
(otherVector) ->
r = []
for v, i in vector()
r[i] = v + otherVector()[i]
makeNumber r

nines = fours vPlus fives
console.log nines() # [9, 9]
``````

And there we have it. A beautifully readable (IMHO) API in CoffeeScript.

Now, why would anyone go to all this effort just to remove the `.`? I’ll be honest, I did it simply because it looks nice.

To my surprise though, it also creates a very flexible API that works for many operands (e.g. numbers, vectors) and allows pluggable user-supplied operations (addition, multiplication…).

My only wish is for some kind of type system to check for silly things like using `vPlus` with ordinary numbers at compile time. Oh well. Maybe I should use TypeScript