Functional Programming Design Pattern
Functional Programming Design Pattern
computing problems. Furthermore, all problems known to be details on the definitions of numbers in λ-calculus and on the
computable could be computed by TMs, and all problems conversion rules).
known to be uncomputable could not be solved by any TM
[7]. Furthermore, modern-day computers build in accordance In λ-calculus, one calculates a function on an integer
to the von Neumann architecture since the EDVAC were by applying those conversion rules until a function equivalent
inspired by the notion of finite universal TMs [8]. All these to the resulting integer is produced. Thus, in some sense,
reasons seem to explain why TMs are deemed as the de facto if you have a λ-function, you can “effectively calculate” its
definition of a computer. result when applied on an integer by performing a finite
number of conversions. Conversely, Church defines a function
Note, in the previous paragraph that the motivation behind the on positive integers to be “effectively calculable” if it can be
definition of a TM are problems deemed to be computable, defined as a λ-function [6], which implies that it should take
and without them, the TM would make no sense. In that a finite number of steps to arrive at its result. Furthermore,
manner, even though the definition of a TM does not depend through the Church-Turing thesis, Allan Turing also proved
on any more primitive notion of computers or computation, that every “effectively caculable” function is also computable,
it is relevant as effect of its ability to solve problems deemed and vice-versa [10].
to be computable. If the TM was not capable of solving all
problems deemed to be computable, it would likely not be Note how each of these terms were defined by their
considered a computer at all. Note that this scenario wouldn’t respective constructs: “Turing computable” are problems that
cause the definition of a TM to be false. Rather, its operations can be computed by TMs, and “effectively calculable” are
would only allow it to solve a subset of the problems deemed functions that can be λ-defined. In that manner, at the same
to be computable. But how good is a computer that can’t solve time that each term is equivalent, they are independent of
problems that we know we can compute? Thus, a TM only each other and are defined without requiring the other one to
makes sense given the existence of such computable problems. exist.
Now, you might have noticed something. I have first Similar as to how they are independent, they are also
mentioned that computable problems are defined in terms of equivalent in that they capture the same idea, but through
a TM, and even though the definition of a TM precedes the different representations. It could be said that Turing takes
definition of a computable problem, it only makes sense given on a more “procedural” approach on computability, perhaps
the existence of computable problems. This might seem as a mimicking human’s computational process, while Church
circular definition, but in reality, this is the duality between takes on a more mathematical approach.
semantics and representation. For one to become aware of an
idea, one must first capture it through some definition. At the Although there have been lots of details on both computational
same time, a lexical definition only makes sense in light of a models that have been left out, a reader interested on the topic
semantically valid idea. is encouraged to look at the references in this paper look at the
references of these references, and deepen their understanding
Another example where this phenomenon is clearer is about these computational models. Nonetheless, my main
when we look at Pythagora’s theorem. Without some goal behind this theoretical background was not to thoroughly
representation like Pythagora’s equation, the idea captured cover these computational models, but to provide the reader
by the mathematical equation is so vague we might not with a perspective on this duality which constantly manifests
even be aware of its existence. On the other hand, if the itself through FP. Thus, have in mind that even though we
idea was false, the formula would have no useful meaning. will cover applications of FP in the context of imperative
And even though we tend to represent Pythagora’s theorem OOD, you can think in terms of FP as far as possible until
through the classical equation c2 = a2 + b2 , many other you reach the primitives upon which your solution depends
equivalent representations exist. Now, we can represent on which are defined in terms of imperative programming.
Pythagora’s theorem through different representations, but
what if we could grasp the notion of computability through
other representations? That’s where Lambda Calculus comes C. Imperative VS Declarative Languages
in. Now that we covered TM and λ-calculus computational
models, I invite you to think of a procedural language like
C as a realization of a TM. Think of how it describes the
B. Different Representation of Computers procedures to solve a problems similar to how a TM describes
Lambda Calculus was introduced in the form as we know the procedures to compute a problem, except that C does so at
it by Alonzo Church as a means to construct a formal system a higher abstraction level. This similarity is not a coincidence.
which could define and reason about mathematical objects The language is intended to replicate the computational model
[9]. This formal system defines every mathematical object that inspires it, and a TM is not only a good candidate to be
in terms of functions, including numbers themselves. It also followed in reason of being the computational model most
defines certain conversion rules (or substitution rules) which computing scientists are familiarized with, but also because
allows derivations to be performed (see section 4.2 on [7] for it is the same computational model traditional computers
3
mimick, thus being easier to implement and often having E. Partial Application
better performance as a result of a more straightforward
Partial application is a concept of lambda calculus which
implementation.
defines a function as another functions with partially applied
arguments. For instance, if we have the function sum(a,b)
A procedural language is a type of imperative language. An
:= a + b, we can define sum2(a) := sum(a, 2) s.t.
imperative language imperatively describes the computer how
sum2(3) = sum(3, 2) = 3 + 2.
to solve a problem. Object-oriented programming is another
example of an imperative language, but now we have the idea
of objects. On the other hand, FP is a functional language
F. Closure
which declares the program in terms of functions and is one
type of a declarative language. Declarative languages, on the Closure is a concept already present in procedural lan-
other hand, declares what needs to be executed, but does not guages, but which must be resignified in the context of FP. Clo-
imperatively describe how [11]. Simiarly to how a procedural sure is the determination of a variable’s value by the context
language “realizes” a TM, a functional language “realizes” of where the functions is defined. For instance, we may have
λ-calculus. something like fooProducer(x) := foo() := x, s.t.
fooProducer is a one-argument function that produces
The major takeaway in the definition and distinction a zero-argument function that returns the value passed to
between these language categories is what kind of syntax fooProducer. In this case, foo is able to determine the
you should expect from each. Idiomatic FP should not value of x because foo and x are both defined in the context
be describing procedures to solve a problem, but rather a of fooProducer.
series of function applications that should take on as much
a mathematical format as possible. Of course, this is not
entirely possible since you might find yourself having to define G. Currying
some primitive language constructs in terms of imperative
Currying is similar to partial application in that it sets the
programming, or having to perform some computations not
arguments of a function, but in this case, it does so by setting
easily expressed mathematically, but these should be avoided
one argument at a time by returning a one-argument function
as much as possible, and when cannot be avoided, be clearly
for each argument until all arguments are set. Here are a
compartimentalized to avoid “contaminating” functional
few examples for a one, two, and three argument functions
code by causing side-effects where not expected, impairing
respectively:
readability or causing other undesirable effects.
• oneArg(x) := x, then oneArgCurry(x) := ()
:= oneArg(x)
III. F UNCTIONAL P ROGRAMMING C ONCEPTS
• sum(a, b) := a + b, then sumCurry(a) :=
A. Immutability (b) := () := sum(a, b)
Immutability is key to functional programming, and al- • sumAndMult(a, b, c) := (a + b) * c, then
though it might be difficult to get around, it provides with sumAndMultCurry(a) := (b) := (c) := ()
several benefits which will later be discussed. Immutability, := sumAndMult(a, b, c)
as the name says, implies in no variables being able to change Note that currying is only possible by leveraging closure.
values once defined.
J. Tail Recursion
Tail recursion is a technique to run a call of a recursive The way the Serializer works is by mapping class names to
function after returning from the calling function. This is often a call to the object constructor, and this mapping is done by
how imperative-programming loops and iterative functions are the Serializer which is invoked by the decorator. But here’s
represented in idiomatic FP. This feature is, unfortunately, the catch: the constructor the decorator function takes has
not natively supported by most imperative languages which no information on its own arguments. This is a problem
support FP. Below is one example of a function that computes for objects that require arguments to be constructed, and
Pn
i: imposing restrictions on the class design is likely to lead to
i=0
bad class design, and what would go against the purpose of
tailSum(i, n, accumulator): a serializer that was meant to be flexible and generalizable.
if i == n:
return accumulator The solution to this problem was a multi-argument decorator.
else: The multi-argument decorator takes in a variable number of
return tailSum(i+1, n, accumulator+i) arguments and returns a function that takes in a constructor
and performs a side-effect. Thus, it is a higher-order function
sum(n): that returns a higher-order function. By leveraging closure
tailSum(0, n, 0) and partially applying the constructor, I am able to do the
task. Below is the code for the decorator:
IV. R EMARKS ON S KIPPED C ONCEPTS
Note that there is more to FP than the concepts here
presented, even though the most used FP techniques in OOD 1 export function s e r i a l i z a b l e ( . . . args
(from my experience) have been described above. For a more : any [ ] ) {
comprehensive resource on functional patterns, see Greg 2 return ( constructor : Function ) =>
Baker’s notes on FP [14] or refer to a FP resource, such as {
Haskell’s wiki [15]. 3 let objectSerializer =
ObjectSerializer .
Note as well that I have skipped discussions on type getObjectSerializer () ;
theory [16], a mathematical theory from which FP borrows 4 l e t c o n s t r = c o n s t r u c t o r . bind ( null
many concepts to deal with types in a rigorously defined , . . . args )
manner and cope with certain situations not clearly defined 5 objectSerializer .
by λ-calculus alone. I believe that it would be more benefitial registerSerializable (
to deepen the discussion on FP in this document rather than c o n s t r u c t o r . name , c o n s t r ) ;
introducing the idea of type theory without being able to 6 }
properly expand on it. Nonetheless, I encourage interested 7 }
readers to do their own research on the topic as some
understanding from the topic can be very benefitial for the
FP programmer. Note how in line 2 we begin defining an anonymous function
that is going to be returned by the decorator, and in line 4,
V. A PPLICATIONS OF FP IN H AITI H OSPITAL P ROJECT inside the anonymous function, the method bind is called,
setting this to be null inside the constructor (there is no
Now, I would like to go over some FP applications in a real-
this object prior to its construction) and partially applying
world Typescript OOD project, showing how some FP tech-
the multiple arguments to the constructor, which can be
niques can be applied even in a project that follows a different
passed in the nested function thanks to closure. Through this
programming paradigm. For the following illustrations, I will
partial application, a zero-arguments constructor is returned,
be using code from the Haiti Hospital project [17], commit
which can be called at any moment to construct an object
c0569944c6820b65a5326811c2d4c4345ef1e63f. I
with the arguments partially applied. Of course, this design
will try to provide with some snippets of code in this docu-
should only work if the arguments passed to the decorator
ment, but the commit should be referred to for further context.
correspond to the appropriate arguments that construct an
instance of the object.
A. Example 1: Partial Appliction of Functions
The Serializer is an experimental class at the time of this This is one example of a feat achieved by FP that probably
writing which is capable of serializing any object that is could not be achieved otherwise. Partial application of
decorated with the @serializable decorator. A decorator functions allows developers to, in a way, bring down the
is a function that takes in a constructor and performs some type of arguments to a least common denominator, when
side-effect with it. Decorator functions are run when the one exists. In this case, the least common denominator of
class is declared, and thus should run before any usage of arguments is zero (no arguments), which can be achieved by
that class. The class is located in the common folder of the partially applying all arguments, as it is in fact done in the
front-end. code above.
5