Recently, I've been learning how to program in J (
website wikipedia online interpreter random link). It can be thought of as a “functional” programming language, being based off of arrays and their modification; in fact, everything is an array: numbers are singleton arrays, lists are arrays, tables are arrays of arrays, etc.; I'm not entirely sure about where boxes fall, but that's a different story.)
Anyhow, functional programming is
my favorite form of programming, and J and I become friends instantly. I tried APL, but I didn't feel like non-ASCII with a useful language. So J it was.
How is this related, you may ask? Well, for my favorite project Jolf I am planning on adding an aspect of functionality to it. And this is the content of the post: to walk you through how to make JavaScript functions more functional. This is the first post in a planned three-part series. This post will cover the code behind function composition, as well as provide an overview of the topic at hand.
Step 1: outline
So what do I plan on adding? Well, to put it simply, I'm planning on emulating J's feature. To illustrate this, I will assume we have functions
add
,
multiply
, etc. defined. So, let this serve as a basic list:
- Function composition. That is, for functions
f
and , the composition f ○ g
is basically this:
[f ○ g](x0,…,xn) ≡ f(g(x0,…,xn))
[square ○ add](x, y) = (x + y)2
Put concisely, it's
performing function f
on the result of function g(x0,…,xn)
.
- Function reflection. This is, for my purpose, converting an n-ary function to a unary one by using a single argument for all of its arguments. For a binary function
f
, it is thus:
f~(x) = f(x, x)
add~(x) = x + x
- Function Argument Binding. “Simply” stated, to bind (or link) an argument to a function is to have that argument be passed to that function; the function that describes this behaviour is the binding of that argument to the original function. So, if we have
f(x,y)
being a binary function, and f ⌒ n
symbolically represents the action of binding, then
[f ⌒ n](x) = f(x, n)
[multiply ⌒ 2](x) = 2x
Show us some code already!
Yeah, yeah, alrighty then. Let's tackle function composition first. As we know,
[f ○ g] = f(g(x))
. For this, we're going to modify
Array.prototype
. (Don't give me any flak for doing this, as I honestly don't care if it conflicts with other programs modifying the prototype. I will slap you with a trout if you do!)
We are going to write a function that returns
another function that will take an arbitrary amount of arguments. It will then call the argument to the prototype with the arguments to the returned function, then the result of that will be called by the
this
function. That's very wordy, huh? Let's work on function composition without a prototype before converting it. This is simple enough, no? We can return a function that calls
f
with the application of
g
on the arguments. For this, we can use
Array#apply
in tandem with
Array.from
.
(NOTE!
Array.from
is a feature with spotty support! If you are worried with cross-browser compatabiliy, use
Array.prototype.slice.call(window, arrayLike)
instead.)
function compose(f, g){
return function(){
return f(g.apply(window, Array.from(arguments)));
}
}
You can test this code out and see that this works. Now, for some abstraction to the prototype! Since we are using the
this
value within the function, I will be using a closure for consistant behaviour. This is what the basic function may look like:
Function.prototype.compose = function(otherFunc){
return (function(orig){
return function(){
return orig(otherFunc.apply(window, Array.from(arguments)));
}
})(this);
}
This works by creating a closure that takes the
this
as a variable. Other than that, the code is pretty much the same as the original code constructed.
Ready for a bit of a challenge? Good, because I'm not asking. You have homework!
Exercise A: it would be nice to be able to call
Function#compose
with multiple arguments without having to link multiple
.compose
calls together. Implement this in the body of
Function#compose
, either your own or the one I have provided. Here following are some test cases for you that your function should pass. Beforehand I have defined the actual functions used.
function square(x){ return x*x; }
function halve(x){ return x/2; }
function add(x, y){ return x+y; }
function id(x){ return x; }
id.compose(square,add)(5, 6); // (5*6)^2 = 30^2 = 900
id.compose(square,halve)(3); // (3/2)^2 = 1.5^2 = 2.25
// make sure you implement it in this order!
halve.compose(square,id,add)(10, 4); // ((10+4)^2)/2 = (14^2)/2 = 196/2 = 98
Exercise B: Do this code in the shortest amount of bytes. You may convert the code to ES6, if it helps. It must, however, be in JavaScript ;). The shortest code by the time the next post is made wins an honourable mention. (You may post it in the comments below.)