Quote Wikipedia:
A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments.
Now what does this mean? It means a combinator is a function (output is determined solely by its input) whose input includes a function as an argument.
What do such functions look like and what are they used for? Here are some examples:
(f o g)(x) = f(g(x))
Here o
is a combinator that takes in 2 functions , f
and g
, and returns a function as its result, the composition of f
with g
, namely f o g
.
Combinators can be used to hide logic. Say we have a data type NumberUndefined
, where NumberUndefined
can take on a numeric value Num x
or a value Undefined
, where x
a is a Number
. Now we want to construct addition, subtraction, multiplication, and division for this new numeric type. The semantics are the same as for those of Number
except if Undefined
is an input, the output must also be Undefined
and when dividing by the number 0
the output is also Undefined
.
One could write the tedious code as below:
Undefined +' num = Undefined
num +' Undefined = Undefined
(Num x) +' (Num y) = Num (x + y)
Undefined -' num = Undefined
num -' Undefined = Undefined
(Num x) -' (Num y) = Num (x - y)
Undefined *' num = Undefined
num *' Undefined = Undefined
(Num x) *' (Num y) = Num (x * y)
Undefined /' num = Undefined
num /' Undefined = Undefined
(Num x) /' (Num y) = if y == 0 then Undefined else Num (x / y)
Notice how the all have the same logic concerning Undefined
input values. Only division does a bit more. The solution is to extract out the logic by making it a combinator.
comb (~) Undefined num = Undefined
comb (~) num Undefined = Undefined
comb (~) (Num x) (Num y) = Num (x ~ y)
x +' y = comb (+) x y
x -' y = comb (-) x y
x *' y = comb (*) x y
x /' y = if y == Num 0 then Undefined else comb (/) x y
This can be generalized into the so-called Maybe
monad that programmers make use of in functional languages like Haskell, but I won't go there.