DEV Community

Cover image for OOP vs functional programming

OOP vs functional programming

Jakob Klamser on August 28, 2020

Introduction Before we get started with coding I want to give you a quick introduction to object-oriented and functional programming. Bo...
Collapse
 
mattlant profile image
Matt Lanteigne

When you say 'Functional' do you mean to say procedural? BASIC is a procedural language, but definitely not a Functional language, whereas languages like F#, Haskell (like you mention), Scala are Functional languages.

The reason I ask is because you give more of a view of Procedural vs OOP, rather than functional vs imperative (procedural/OOP with state maintenance and change) as an example.

The OOP example you provide could still be viewed as functional in the sense you don't mutate state.

If functional - in the sense you are operating on immutable functions only - is truly the goal, perhaps an example where the OOP approach maintains and mutates state?

Beyond that though, it's well written article.

Collapse
 
skyjur profile image
Ski • Edited

You can do OOP with immutable objects. In js numbers, strings are objects but immutable.

But Date on other hand is mutable. Having some objects mutable some immutable leads to bugs if one does not know what are doing.

But regardless if it's mutable or not, state is mutated somewhere still.

Take redux reducer for example

reducer = (state, event) => newState
Enter fullscreen mode Exit fullscreen mode

it's immutable function on the surface, but it returns new (updated) state - of whole application state, thus there is a power to change anything in application state here. Mutability is not the main problem. Using purely immutable interfaces can help avoid certain type of bugs. But eventually it leads to the same big problem - somewhere state is updated, and the big question is, how is it encapsulated.

A careful developer would divide state in small chunks and ensure all reducers only take care of one little piece of state

const appReducer = state => {
   return {
      x: xReducer(state.x),
      y: yReducer(state.y)
   }
}
Enter fullscreen mode Exit fullscreen mode

not careful developer still can just as easily add unpredictable side effects even though interface is immutable

const xReducer = (state, event) => {
   if(event.type == 'UpdateX') {
      return { 
          ... state,
          x: state.x + event.size,
          y: state.y * -1 // hmm
      }
   }
   return state
}
Enter fullscreen mode Exit fullscreen mode

In redux on can also do same thing that one can do with setters/getters in object

const xReducer = (state, event) => {
    if(event.type === 'SetX') {
       return {...state, x: event.value}
    }
    return state
}
Enter fullscreen mode Exit fullscreen mode

now one can modify state anywhere from app code - immutable and functional version of setters/getters antipattern of OOP

render() {
  dispatch(SetX(1))
  dispatch(SetY(2))
}
Enter fullscreen mode Exit fullscreen mode

Immutability and functional development it self doesn't do that much. It's good but it's not a lot. One must also follow SOLID and other better known guiding principles (like Law of Demeter) to build good code and it doesn't matter OOP or functional.

Collapse
 
klamserdev profile image
Jakob Klamser

You are totally right, I struggled to get a good example that is small and simple enough for beginners to understand. I think bringing in state and managing it would have been a bit to much for beginners.

I hope the point I wanted to make gets more clear now.

Thanks for the constructive feedback I really appreciate it :)

Collapse
 
pentacular profile image
pentacular

I think that trying to turn functions into classes may not have produced the most convincing examples. :)

I think that the biggest problem is that oop requires a more explicit structuring of domains of responsibility -- it might be easier to go from an oop design to a functional design.

Collapse
 
klamserdev profile image
Jakob Klamser

That's a good point. My intention was not to go too deep into the details and try to find a simple example.
Maybe the order was a bit confusing.
Thanks for the feedback :)

Collapse
 
pentacular profile image
pentacular

You're welcome.

Collapse
 
veciz profile image
Berkan Ƈetinkaya • Edited

This is more like OOP vs Procedural comparison indeed :)
By the way you can use recursion for your factorial function

function factorial(x) {
  return x === 0 ? 1 : x * factorial(x - 1);
}

Nice article :)

Collapse
 
klamserdev profile image
Jakob Klamser

Thanks for the feedback. I linked to geeksforgeeks for more details on the implementation possibilities for a factorial. I decided to go for the iterative approach because I didn’t want to introduce recursion in this article 😁

Collapse
 
veciz profile image
Berkan Ƈetinkaya

Yep, just seen it :) I was thinking to opposite, factorial is good chance to show off the recursion :)

Collapse
 
skyjur profile image
Ski • Edited

To me week spot of functional code in js is composition.

const add(x) =>
   y => x + y

const mul(x) =>
   y => x * y

const createComposedOperation(op1, op2) =>
    (a, b) =>
        op1(a) + op2(b)

const app = (inputs, outputs) => {
   const composed = createComposedOperation(add(1), mul(2))

   inputs.on(value => outputs.write(composed(value.a, value.b)))
}
Enter fullscreen mode Exit fullscreen mode

I think this quickly becomes hard to reason about it once you get little larger codebase, because it can be hard to find code that is gonna run, when function is passed as argument and all you see when opening some deep module is this

const createComposedOperation(op1, op2) =>
    (a, b) =>
        op1(a) + op2(b)
Enter fullscreen mode Exit fullscreen mode

When instance is passed as argument - it has a type (it's class name) - and so it's very easy to locate related code even without typescript.

class ComposedOperation {
  constructor(
       private op1: Addition,
       private op2: Multiplication) {}

   solve(a, b) {
      return this.op1.add(a) + this.op2.mul(b)
   }
}
Enter fullscreen mode Exit fullscreen mode

with typescript it's possible to achive similar end goal with just functions but in expense of declaring a lot of additional types (while with classes - class it self already is a type). With all additional type declarations functional approach can get even more verbose that class based approach.

Collapse
 
peerreynders profile image
peerreynders • Edited

Lambda calculus deliberately only deals with single argument functions because they compose well.

For functions with multiple arguments you have to curry to preserve composability.

function compose(g, f) {
  return value => g(f(value));
}

const add = x => y => x + y;
const mul = x => y => x * y;

// fn(a,b) = (1 + a) + (2 * b)
//
// a => b => add(add(1)(a))(mul(2)(b));
// a => b => opBoth(opA(a))(opB(b))

function composeTwo(opBoth, opA, opB) {
  const fnA = compose(opBoth, opA);
  const fnB = fn => b => fn(opB(b));

  return compose(fnB, fnA);
}

const composed = composeTwo(add, add(1), mul(2));

console.log(composed(1)(4)); // 10

with TypeScript it's possible to achieve similar end goal with just functions but in expense of declaring a lot of additional types.

TypeScript pushes JavaScript deeper into 'class-based object-orientation' territory (apart from soundness being a non-goal) - so any deviation from that ideal will require a deep dive into the typing metalanguage.

Statically typed FP is better served by ReScript.

Quote

TypeScript is designed to be a JavaScript super linter, it uses the type system to give your better editing experience, it has seamless interop with JavaScript, at the same time, it inherits all JavaScript gotchas.

Collapse
 
skyjur profile image
Ski • Edited

IMHO currying in javascript only makes things worse

I don't need to curry in order to make anything more composable. I could always compose simply by declaring new function

const add = (a, b) => a+b
const mul = (a, b) => a* b

const composed = (a) => 
   add(add(a, 1), mul(a, 2))

number of things is better in this approach over curried version:

  • I can tell from syntax how many arguments composed function takes
  • I don't need helpers for composition
  • when binding arguments it's not guaranteed that one always wants to bind arguments in same order as it's defined in curried function
  • I know for sure just from syntax that the the end result is a function not object or result or anything else
  • debugging is easier (I can put breakpoint or log statements and inspect where variables go and what value they have)

So in nutshell currying to me is a solution for non existing problem which makes things worse when applied religiously.

In my initial example I did used one case of a 2nd order function, which was a case of curried function. But this was done in order to separate dependencies from data. I would do that only if I know usage patterns

const createFunction = (dependencies) => {  // usually other 2nd order functions here
   (data) => { // data objects here
   }
}

This implies that intended use of it is of is to be instantiated before usage.

const someFunction = createFunction()
someFunction()

and definitely not createFunction()() - if there is a case to use it like this then it's better to declare it with flat arguments.

To me problems with this functional approach and composed functions happen when composed function must be passed down as parameter. This is where to me class based approach wins over function based approach. After function is passed as argument, it becomes more difficult to trace which code is relevant when function is executed. It's not necessary a problem when writing a code but it's a problem when you are in large code base and want to understand it. With typescript can declare interface of function but that does not help finding implementation. On other hand in class based approach receiver can indicate concrete class that it expects (even without typescript - with js docs).

Thread Thread
 
peerreynders profile image
peerreynders

Your initial composing function

const createComposedOperation(op1, op2) =>
    (a, b) =>
        op1(a) + op2(b)

accepted arbitrary single argument functions.

Now your preference

const composed = (a) => 
   add(add(a, 1), mul(a, 2))

is to manually assemble the function.

I was referring to generalized function composition. Given that functions only return a single value general function composition can only compose functions that accept a single value. In that context currying is the workaround to fake multi argument functions.

I wasn't advocating "curry all the things" just for the sake of it.

This style

const createFunction = (dependencies) => {
  (data) => {
    // some code here
  }
}

is related to

const createFunction = (a, c) => {
  (b, d) => fn(a,b,c,d);
};

i.e. using a closure to mimic partial application - it's just that "some code here" is never isolated into an independent function.

Your particular annotation identifies dependencies as arguments to a kind of constructor: "A closure is an object that supports exactly one method: apply."

After function is passed as argument, it becomes more difficult to trace which code is relevant when function is executed.

Passing a function as an argument to a higher order function is equivalent to passing a strategy object to a context object (Strategy Pattern) - i.e. this kind of composition exists in both paradigms.

With TypeScript can declare interface of function but that does not help finding implementation. On other hand in class based approach receiver can indicate concrete class that it expects. On other hand in class based approach receiver can indicate concrete class that it expects.

What you are saying is that you find it more difficult to work with interfaces than concrete implementations (i.e. this isn't about functions vs. objects). That may be so but:

"Design Patterns: Elements of Reusable Object-Oriented Software" p.18

  1. Clients remain unaware of the specific types of objects they use, as long as the objects adhere to the interface that clients expect.
  2. Clients remain unaware of the classes that implement these objects. Clients only know about the abstract class(es) defining the interface.

This so greatly reduces implementation dependencies between subsystems that it leads to the following principle of reusable object-oriented design:

Program to an interface, not an implementation.

i.e. classes depending on other concrete classes should be the exception, not the rule.

The natural boundary around a class that depends on other "concrete classes" automatically includes those "concrete dependencies" (and recursively their concrete dependencies). This creates a much larger unit that needs to be "reasoned about" as a whole.

Interfaces are at the core of many OO practices including the dependency inversion principle.

You probably have other, bigger problems in the code base when you have difficulty tracking down the concrete implementation that is used to service a particular interface at a call site.

It's also a running joke that function types satisfy the interface segregation principle by default.


The impression I'm getting here is that you find imperative code ("do this then that") easier to read - which isn't surprising given that most of us learn programming that way - it's the allure of the familiar.

Functional programming tends to focus less on the "how" and more on the "why" and "what" (some say it's more declarative) - but it still has its 'step-by-step' moments. For example:

// transform the string to an {ok/err} result
// use an IIFE to initialize function closure
const transform = (() => {
  const fn0 = validateRequired('Please provide a value');
  const fn1 = andThen(validateInteger('Please provide a integer'));
  const fn2 = map(n => Number.parseInt(n, 10));
  const fn3 = andThen(validateBetween('Please specify an integer between 0 and 18', 0, 18));
  const fn4 = map(factorial);

  return value => fn4(fn3(fn2(fn1(fn0(value)))));
})();

Now transform is a terrible name, even textToFactorial would have been better but at the time I was trying to make a general point. But the "steps" are still there, clearly outlining what is going on. The big difference (to imperative code) is that this code isn't transforming any data at this point - the function that will be transforming the data is being "wired up".

Functional code is composed of functions and as functions are generally smaller than objects there will be proportionally more code dedicated to "wiring up" the capability rather than "doing" the capability. So when reading the code one has to differentiate between "construction" code (the scaffolding) and "running" code.

But the same is true for any non-trivial object-oriented code base. As it grows and God Objects are avoided more and more code is dedicated to setting up the relationships between the collaborating objects before they can do any useful work.

However a network of interacting stateful objects can grow in complexity rapidly. A composition of stateless functions (or immutable closures) is typically easier to reason about. Coming from an imperative background the functional approach is different enough to take some getting used to.

(One issue with React hooks is that functional components are a now just as stateful as objects - which gives rise to much richer (i.e. complex) behaviour - the standing argument is that hooks are more declarative than object methods but that is a whole discussion its own).

Thread Thread
 
skyjur profile image
Ski • Edited

You probably have other, bigger problems in the code base when you have difficulty tracking down the concrete implementation that is used to service a particular interface at a call site.

It's not that I have difficulty. It's just not as fast. With OOP when coding on top of interfaces tooling supports jump-to-implementation. It's just less straight forward with function types.

I do not advocate towards breaking any best practices. That said not every practice that is necessary when designing reusable code is also necessary or even good when building one off pieces of implementation.

Passing a function as an argument to a higher order function is equivalent to passing a strategy object to a context object

Strategy is just one use case. There are many cases. In UI applications hardly anything passed down element tree is 1st order function. If it was 1st order most of time you'd not pass it down - it can be imported and called directly.

The impression I'm getting here is that you find imperative code ("do this then that") easier to read - which isn't surprising given that most of us learn programming that way - it's the allure of the familiar.

Well firstly I think it's little bit condescending for you to make impression of why I "find imperative code easier to read".

But regardless of that, I think this is a terrible argument. Normally if someone does not find my code immediately straight forward I tend to believe that I made it too difficult. Sometimes I just couldn't think of easier way. Sometimes simply because I did something I thought was clever - turns out it wasn't.

Now to your example. If you're writing in this style then at least you could get rid of fn1, fn2, etc and that closure pattern - all that make it very ugly

const transform = createFlow([
   validateRequired('Please provide a value');
   andThen(validateInteger('Please provide a integer'));
   map(n => Number.parseInt(n, 10));
   andThen(validateBetween('Please specify an integer between 0 and 18', 0, 18));
   map(factorial);
])
Enter fullscreen mode Exit fullscreen mode

to me very big downside of this code is that it's very hard to use debugger on it. Almost in no point can you place a breakpoint on it except for parseInt() part

And what is reason for it? I don't think there is good one. It's very imperative style in the end. Why would you write imperative code in declarative-functional-composition? Just makes no sense. There are situations where declarative style is great. There are situations where imperative style is great. Use one that is the most appropriate. Application like this would be best split into input, validation, action phases. Validator can be setup in declarative way. The rest can go in imperative. And result is best of both worlds, easy to follow, easy to debug.

const validate = number.required.between(0, 18)

function app(data) {
   const [value, error] = validate(data)
   if(error) return [null, error]
   return [factorial(value), null]
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
peerreynders profile image
peerreynders • Edited

In UI applications hardly anything passed down element tree is 1st order function.

Not sure where this is going.
As far as I'm aware "higher order function" doesn't imply an actual ordinality.
Eric Elliot used first order function to refer to a function that doesn't "take a function as an argument or return a function as output". Eric Normand on the other hand uses:

  • zero-order function - a function that takes values, non-functions
  • first-order function - a function that takes a function
  • second order function - a function that takes a function that takes a function.

So I can only conclude that the "n-th order function" terminology with reference to higher order functions is neither standardized nor commonplace.

"Higher order function" simply calls attention to the fact that a function is specialized by and/or returns another function. But in the end in the functional style it is as natural to return functions and take them as arguments as it is to return an object and take object arguments in the object style.

The impression I'm getting is that you're saying that "a function that takes a function that takes a function" (and beyond) is getting hard to track. But given that functions have a type, a function can simply transform one type of function into another type of function.

I think it's little bit condescending

No condescension was implied. The point was familiarity bias - Rich Hickey style i.e. easy is a result of familiarity, not simplicity and the unfamiliar can seem difficult even when it's simple.

At its core JavaScript is an imperative language - it just happens to also have first class functions and supports closures which can be leveraged when practicing a functional style.

And given that TypeScript keeps coming up, it's been my observation that TypeScript is much less conducive to enabling a functional style than JavaScript - to the point that it could be argued that TypeScript is the "wrong tool for the job" to support a statically typed functional style. I know of fp-ts but you have to work much harder in TypeScript to practice a functional style compared to an object style. But that's not the fault of the "functional style" but a result of TypeScript being streamlined for OO style typing.

at least you could get rid of fn1, fn2, etc and that closure pattern - all that make it very ugly

That "ugliness" exists for illustration purposes - being explicit about the bound values being functions and the manner in which those functions are being composed.

it's very hard to use debugger on it.

That argument keeps coming up. You can still set a breakpoint at any function declaration and you can run into similar problems with dynamically assembled objects.

One could just as easily argue that it is disappointing that

  1. debugging tools make this hard
  2. we to this day have such a heavy reliance on debuggers given code reviews, automated unit tests, linters, and static type checkers ("I do not use a debugger" - debuggers have their place but some people seem to use it like this - i.e. debugger guided program construction).

I think the "hard to debug" argument has even more of a negative impact when it comes to adoption of streams which could potentially simplify UI architecture or perhaps enable some alternate approaches.

Why would you write imperative code in declarative-functional-composition?

The code isn't imperative, the style is. Even Haskell has the do notation ("Haskell is the world’s finest imperative programming language").
Some people find it more intention-revealing.

The functions are organized in the run-time sequence of the composed function so that it's clear what the transform will do. The {ok/err} type implements the necessary Railway-Oriented Programming (via andThen and map).

easy to follow, easy to debug.

To me that sample code seems influenced by Go:

"It must be familiar, roughly C-like. Programmers working at Google are early in their careers and are most familiar with procedural languages, particularly from the C family. The need to get programmers productive quickly in a new language means that the language cannot be too radical."

There's anecdotal evidence that something like How to Design Programs (HtDP 2e) may be a better first exposure to programming: ā€œIt’s mind boggling that your HtDP students are better C++ problem solvers than people who went through the C++ course alreadyā€.

The Structure and Interpretation of the Computer Science Curriculum

Collapse
 
peerreynders profile image
peerreynders • Edited

TL;DNR

While JavaScript is an imperative language (i.e. it isn't a functional programming language) I think it's fair to call it "Function-Oriented".
From that perspective mastering functions and closures is an essential part of developing JavaScript competence.


While "class-based object-orientation" goes back to Simula (1962) and Smalltalk (1972) it largely became mainstream due to languages like Java (1995) and C# (2000). While both Java and C# have gained a number of features that aren't directly related to classes over the years, it is probably accurate to say that their fundamental base unit of composition is a class instance, i.e. an object.

The same isn't true for JavaScript. The base unit of composition is a function and its stateful counterpart the closure. On a fundamental level object literals are simply associative arrays where the keys are limited to Strings and Symbols while the value can be of any type (in modern JavaScript Map fits the use case of an associative array much better). The notion of an "object with methods" emerges when functions are stored as values in a plain object (the prototype chain largely exists to help reuse functions across multiple objects).

While in Java and C# classes and class instances (objects) are atomic units of composition, in JavaScript an object in the object-oriented sense is an aggregate of a plain object and functions as values.

Personally the mental model of an object as an aggregate clarified the role of the function context this immensely. this is a special reference that gives a function access to some "other data". If a function is meant to act as a method (is accessed via an object reference) it needs to use this to refer to the rest of the "object". But this can also be used to pass any other context with call (or apply) and bind can be used to create another function with a "bound" this (an arrow function's this is automatically bound to the scope it's defined in). But a function is also free to ignore this entirely.

In my judgement the introduction of the module first as a pattern and then as a language feature is more important than the adoption of the class syntax sugar. It's possible to create a well structured code base with just modules, functions and plain objects.

In fact during the ES5 years (2009-2015) an alternate model of object-orientation not based on constructor functions or classes but instead based on object factories emerged (OOP with Functions in JavaScript). A factory creates a plain object holding functions that are linked to "object state" not via this but through the shared closure that created the functions. It's an approach well worth being familiar with in order to get better acquainted with closures.

So in JavaScript object-orientation isn't class-based (class is more of a creational convention) and the base unit of composition is the function (and plain objects). Objects compose as well and objects can compose with functions and closures. As a result objects (and more so classes) aren't always the "go-to" building block in JavaScript - functions and closures often play the role that small classes do in mainstream OO languages.

Aside: There are some opinions that Closure components are a more obvious solution than hooks for stateful functional components (Hooks reimagined, Preact Composition API).

Collapse
 
peerreynders profile image
peerreynders • Edited

Functional programming is primarily about composing functions to transform a value.

So I might write the "functional-style" version as:

;(function(){
  // functional.js

  /*
      Functional Core
   */
  // Result module
  // Either {ok}  - contains the successful value
  // Or     {err} - contains the error message
  const makeOk = value => ({ok: value});
  const makeErr = message => ({err: message});

  // Higher order functions (HOFs) for Result values
  const andThen = fn =>
    rs => {
      const {ok} = rs;
      if (ok === undefined) return rs;

      return fn(ok);
    };
  const map = fn =>
    rs => {
      const {ok} = rs;
      if (ok === undefined) return rs;

      return makeOk(fn(ok));
    };

  // Validations
  const validateRequired = message =>
        value => value.trim().length > 0 ? makeOk(value) : makeErr(message);

  const digits = new RegExp('\\s*\\d+\\s*');
  const validateInteger = message =>
        value => digits.test(value) ? makeOk(value) : makeErr(message);

  const validateBetween = (message, lower, upper) =>
        value => lower <= value && value <= upper ? makeOk(value) : makeErr(message);

  // JavaScript isn't functional so iteration (+ mutation)
  // is more natural than recursion.
  function factorial(n) {
    let temp = [1, n];

    // iterate until terminating condition
    while(temp[1] > 1) {
      temp = stepFactorial(temp);
    }
    return temp[0];
  }

  const stepFactorial = ([acc, n]) => [acc * n, --n];

  // Partially apply the functions and
  // "compose" the resulting single argument functions
  // to transform the input value
  const composition = [
    validateRequired('Please provide a value'),
    andThen(validateInteger('Please provide a integer')),
    map(n => Number.parseInt(n, 10)),
    andThen(validateBetween('Please specify an integer between 0 and 18', 0, 18)),
    map(factorial)
  ];

  function pipe(fns, value) {
    let result = value;
    for(let i = 0; i < fns.length; ++i) {
      result = fns[i](result);
    }
    return result;
  }

  // transform the string to an {ok/err} result
  const transform = value => pipe(composition, value);

  /*
    Imperative Shell
  */
  function whenDomUpdated(fn) {
    requestAnimationFrame(
      () => requestAnimationFrame(fn)
    );
  }

  function calculateListener(event) {
    const inputEl = event.target.querySelector('input');
    const {ok, err} = transform(inputEl.value);

    if (err === undefined) {
      resultEl.innerText = ok.toString();

    } else {
      resultEl.innerText = '';
      whenDomUpdated(() => {
        alert(err);
      });
    }

    event.preventDefault();
  }

  // Initialization Script
  const formEl = document.querySelector('#factorial-form');
  const resultEl = document.querySelector('#factorial-result');
  formEl.addEventListener('submit', calculateListener);

}());
Enter fullscreen mode Exit fullscreen mode

Edit: It's useful to remember Master Qc Na's lessons:

  • Objects are merely a poor man's closures
  • Closures are a poor man's object

Using a closure

function validateBetween(message, lower, upper) {
  return function(value) {
    // As part of its closure the returned function has access
    // to the `message`, `lower`, `upper` argument values
    return lower <= value && value <= upper ? makeOk(value) : makeErr(message);
  };
}

const validate = validateBetween('Please specify an integer between 0 and 18', 0, 18);

console.log(validate(18)); // {ok: 18}
console.log(validate(19)); // {err: 'Please specify an integer between 0 and 18'}
Enter fullscreen mode Exit fullscreen mode

versus using an object

class ValidateBetween{
  constructor(message, lower, upper){
    // store argument values in object properties
    this.message = message;
    this.lower = lower;
    this.upper = upper;
  }

  execute(value) {
    // Stored `message`, `lower`, `upper` properties
    // are accessed via `this`
    return this.lower <= value && value <= this.upper ?
           makeOk(value) :
           makeErr(this.message);
  };
}

const validate = new ValidateBetween('Please specify an integer between 0 and 18', 0, 18);

console.log(validate.execute(18)); // {ok: 18}
console.log(validate.execute(19)); // {err: 'Please specify an integer between 0 and 18'}
Enter fullscreen mode Exit fullscreen mode

composition and pipe could be replaced with:

  function composeFns() {
    const fn0 = validateRequired('Please provide a value');
    const fn1 = andThen(validateInteger('Please provide a integer'));
    const fn2 = map(n => Number.parseInt(n, 10));
    const fn3 = andThen(validateBetween('Please specify an integer between 0 and 18', 0, 18));
    const fn4 = map(factorial);

    return value => fn4(fn3(fn2(fn1(fn0(value)))));
  }

  // transform the string to an {ok/err} result
  const transform = composeFns();
Enter fullscreen mode Exit fullscreen mode

or

  // transform the string to an {ok/err} result
  // use an IIFE to initialize function closure
  const transform = (() => {
    const fn0 = validateRequired('Please provide a value');
    const fn1 = andThen(validateInteger('Please provide a integer'));
    const fn2 = map(n => Number.parseInt(n, 10));
    const fn3 = andThen(validateBetween('Please specify an integer between 0 and 18', 0, 18));
    const fn4 = map(factorial);

    return value => fn4(fn3(fn2(fn1(fn0(value)))));
  })();
Enter fullscreen mode Exit fullscreen mode

IIFE (Immediately Invoked Function Expression)

Collapse
 
matias2018 profile image
matias2018

Great article, thanks! It would be great if you could write more similar articles and an insight on when to choose a paradigm over another.

Collapse
 
klamserdev profile image
Jakob Klamser

Thanks for the awesome feedback. Iā€˜m currently writing a 3 part beginners series on react. The first part is already out here.
After that I wanted to do a vanilla JS article again 😁