SCIP 1.3 Formulating Abstractions with Higher-Order Procedures
Mix.install([])
1.3
Higher Order Procedure: a procedure that manipulates a procedure.
1.3.1 Procedures as Arguments
When you observe muliple procedures share the same pattern or structure it is indicative of a useful abstraction waiting to be brought to the surface. In such a case, you would map out the common structure and then pass the actual computation as an argument. This is not a new concept as mathematicians have identified such abstractions for centuries, e.g. sigma notation and “summation of a series”.
1.3.2 Constructing Procedures Using Lambda
It would be cumbersome to define a named abstraction for trivial operations only so that they could be passed as arguments. Programming languages will provide a lambda
.
lambda
definitions are used to create procedures with no name.
multiply = fn a, b -> a * b end
multiply.(5, 5)
perform_operation = fn operation, a, b -> operation.(a, b) end
perform_operation.(multiply, 5, 5)
We will often need local variables in our procedures other than those bound as formal parameters.
Let’s say we wanted to compute:
$f(x, y) = x(1 + xy)^2 + y(1 - y) + (1 + xy)(1-y)$
That’s confusing! It could be simplified as:
$a = 1 + xy$
$b = 1 - y$
$f(x, y) = xa^2 + yb + ab$
If we were simply using functions, we could use lambda
functions to facilitate this local binding.
f = fn x, y ->
(fn a, b ->
x * (a * a) + y * b + a * b
end).(1 + x * y, 1 - y)
end
f.(3, 4)
That’s not very readable! We are using an anonymous function to capture (a, b)
as local parameters. Scheme has a let
special form that allows you do define local parameters. (Fun fact: the let
special form is in fact syntactic sugar for the underlying lambda
application).
Elixir has function level scope, allowing for the above to be refactored:
f = fn x, y ->
a = 1 + x * y
b = 1 - y
x * (a * a) + y * b + a * b
end
f.(3, 4)