Functional Programming with TypeScript — Part 1

cbyad
5 min readJun 9, 2020

--

This article targets anyone unfamiliar with functional programming (FP) but want to learn about it from scratch. Starting FP with dedicated languages like Haskell, OCaml, Erlang or Scala can take a lot of time for a beginner. FP treats many concepts and it’s impossible to cover them all in a single blog post. But with the basics and some concepts we can considerably improve our code (elegance, conciseness, safety, robustness, maintainability and more). We will use TypeScript to bring up the topic even if it’s not a pure functional programming.

What is FP ?

Definition from wikipedia

Functional programming is a programming paradigm where programs are constructed by applying and composing functions. It is a declarative programming paradigm in which function definitions are trees of expressions that each return a value, rather than a sequence of imperative statements which change the state of the program or world.

Immutability

Photo by In Lieu & In View Photography on Unsplash

A common and good practice in FP consists in avoiding the modification of any variable already set. Once they are declared if we want later compute on them we need to introduce new variables which hold the result. It seems inefficient but it’s not the case and we will not cover discussion about this side. Sometimes, you can’t follow this rule and you need to mutate the state, but that’s uncommon. These are benefits of Immutability:

  1. Easy to test and maintain: state inside code block don’t change, a function never change any external state therefore function behavior are reproducible and easy to understand
  2. Coherent state: once a state is valid, it remains valid along the flow of the program
  3. Thread Safe: state shared by many threads or processes can’t be modified. Therefore, doing concurrency programming is easier and safer

In typescript consider using const instead of let to introduce your variables

Type

Type take an important place in FP. TypeScript is a statically typed language. Once a variable is manually typed or inferred by compiler, It can never change. All variables, functions, elements of our language have a type.

Functions

  • Pure functions
Photo by chuttersnap on Unsplash

Functions should always produce the same result from same arguments. It should not depend on global or external state of It scope. No side effect.
A side effect is a modification outside of the function scope. For example, by updating an external variable or throwing an exception.

  • Higher-order Functions (HOF)

Higher-order Functions or sometimes called First-class Functions are functions that can take functions as parameter and/or return a function.

  • Functional type

1- printStr is a function that takes two strings and return nothing, its type is (string, string) => void

2- square is a function that takes a number and return a number, its type is number => number

3- compute is a function that takes a number, a function f and return a number. so its type is (number, fType) => number. f takes a number and return a number, its type is number => number. Therefore by substitution the final type of compute is (number, number => number) => number

  • Currying
Photo by Dragne Marius on Unsplash

Sorry it’s not the Indian food. From the HOF we know now that is possible to functions to take and return functions. Currying is basically the fact of nesting returning functions and be able to partially consume a function. Curried function takes arguments one at time. With the previous definition of compute we can’t use it partially. To use compute we must fill the two parameters. To be more functional we give a curried compute version.

Now compute is more compact, it’s a function that takes a number and return a new function. Its type is number => newFunctionType. This new function takes a function f and return a number. so newFunctionType is fType => number . At this step, compute type is number => (fType) => number . f is a function that takes a number and return a number, number => number Therefore compute is number => (number => number) => number

partialResult return a function because we consumed only the first argument. Passing value 5 to compute, f store it in its lexical scope (closure). And now partialResult is waiting for a function with type number => number to get final result.

  • Recursion

Above example is classic factorial of a number. It is more concise and elegant than an imperative approach. It is pure. Recursion is not FP concept. Imperative languages use it but prefer loops for performance. Simple Recursion isn’t performant either in FP because its computation increases significantly the stack size and can reach stack overflow.

  • Tail recursion

Basically, tail recursion is the solution for the problem described above. Modern and smart compilers convert tail recursion function to imperative computation. Below a tail recursion version of the factorial.

Here at each recursive stage we hold the result and it stored in an accumulator variable. Opposite to simple recursion where we need to unroll until stop 🛑 condition to get the value.

Data structures and transformations

Applying some functions as map, filter, reduce,… is very common. We will not implement them here, but you can do it very easily. Below example with Array from the standard library.

  • filter creates new array by removing unsatisfied condition of the predicate. We start from Array<number> and result with Array<number>. filter type is (predicate : (element: A) => boolean) => Array<A>
  • map creates new array by transforming each element of previous array with supplied function. The result array can be of different type. More generally map type is (f : (element: A) => B) => Array<B>
  • reduce is a terminal operation, aggregate by combining elements together with supplied function. We start from Array<A> and result with B. reduce type is (f : (accumulator: B, element : A) => B) => B

lodash give more efficient implementation of data structures and functions than the standard library. You can try it.

Note that these functions are not exclusive to Array, Map, Set,… we can use them and many more on other kinds of structure. Look up this article talking about Monad and their possible implementation.

Many others concept haven’t been covered such as referential transparency, lazy evaluation,… In the next article we will go deeper by exploring opportunities offered by a dedicated FP library fp-ts and show an actual example on an API development 🤓.

--

--

cbyad

Functional Programming & AI/ML enthusiast 🤓🇨🇮