DEV Community

Matthijs Groen for Kabisa Software Artisans

Posted on • Originally published at kabisa.nl on <time datetime="2021-02-16T00:00:00Z" class="date-no-year">Feb 16</time>

My journey in learning Functional Programming

This post is part 1 of the series "Learning Functional Programming using JavaScript". Already read part 1? Part 2 and part 3 of this Blog Post series are out now!

Functional Programming

Hi, I'm Matthijs Groen, a frontend developer at
Kabisa and I would like to share my experiences with
diving into the concepts of Functional Programming.

Before we dive into the Functional Programming (FP) goodness, I'd like to share
where I was coming from and why I had so much trouble adjusting to the FP
concepts.

I spent most of my developer life writing object-oriented code and some
procedural code before that. In Object-Oriented programming (at least how I was
taught) how you name objects, methods etc. matters.

I have worked with Ruby on Rails for years. Not only
was everything an object in that language, Rails itself is a framework with
"conventions". These conventions make you productive in some cases, but also
make you feel trapped in others.

3.times do
  puts "This will be printed 3 times"
end
Enter fullscreen mode Exit fullscreen mode

Yes, even a number is an object.

After working with Ruby (doing full-stack), my work changed to be more front-end
only. We used Backbone.js for years. Backbone, just
like Ruby on Rails is Object-Oriented, and follows the Model-View-Controller
pattern.

A few years ago we changed the front-end stack from Backbone.js (in combination
with CoffeeScript) to
Preact, Redux and modern
ECMAScript.

The thing I liked most when I just switched from Backbone.js to Preact was the
way you could nest views. In Backbone this really was a pain. So you would end
up with big templates. In Preact, you make a small reusable component for
everything, and nesting views was actually the way to build up your UI.

It also changed for us where logic lived.

The location of logic was no longer decided by the framework. You could put it
everywhere. This made the business logic totally separate and on its own. Easier
to test, in loose small functions. No framework to have any opinion about it.
Data in, data out. Just functions, and functions calling other functions.

No hidden dependencies, or object hierarchies.

It makes refactoring or moving code around a breeze. Now not only the views were
composable, the whole software became composable.

<HeaderBar theme="blue">
  <SiteMenu />
  <UserProfile user={user} />
</HeaderBar>
Enter fullscreen mode Exit fullscreen mode

Nesting views (Components) in Preact is as easy as nesting HTML self.

First steps...

I really started to like this approach. In the meantime, I got more colleagues
on my team that really were into functional programming.
Elixir, Haskell,
Monoids, Functors, Category theory. I didn't understand half of what they were
saying. And when I asked to explain stuff to me, most of the time it only gave
me headaches...

But I really started to like working with functions only approach. No
getName() that could reply with a different value each time you called it. It
became more predictable. Also working with Redux brought new concepts to the
table such as immutability. Not changing data, but reconstructing new data based
on changes.

An example:

Before:

class Person {
  constructor(firstName, lastName, age) {
    super();
    this.firstName = firstName;
    this.lastName = lastName;
    this.age = age;
  }

  getName() {
    return `${this.firstName} ${this.lastName}`;
  }
  getAge() {
    return this.age;
  }
  increaseAge(amount) {
    this.age += amount;
  }
}

const me = new Person("Matthijs", "Groen", 37);
me.getAge(); // 37
me.increaseAge(1);
me.getAge(); // 38
me.getName(); // Matthijs Groen
Enter fullscreen mode Exit fullscreen mode

After:

const me = {
  firstName: "Matthijs",
  lastName: "Groen",
  age: 37,
};

const getName = (person) => `${person.firstName} ${person.lastName}`;
const getAge = (person) => person.age;
const increaseAge = (person, amount) => ({
  ...person,
  age: person.age + amount,
});

getAge(me); // 37
const olderMe = increaseAge(me, 1);
getAge(me); // still 37!
getAge(olderMe); // 38!
getName(me); // Matthijs Groen
Enter fullscreen mode Exit fullscreen mode

There are more benefits:

  • Since a function only relies on its directly provided input and is not allowed to change that input, not handling the result would actually make the function call as if it never happened.
  • When you are not allowed to change things, you know what data you are dealing with. It did not change by itself, unless you got new data after calling a function to change the data.
  • Since this relation of input and output is so strict, you can cache processes better, skipping performance heavy steps.
  • Reusability is very, very high. Functions have less responsibilities, are generalizing data patterns and allow for great composition into larger solutions.

So I wanted to learn more about these concepts, and practise them. I wanted to
understand all "the fuss".

But there is a big gap coming from an object oriented world, where language were
designed to be easily readable and code was written as if they where stories,
and going to a world where stuff is expressed as math and concepts with strange
names are explained by even stranger names.

fmap :: (a -> b) -> f a -> f b
Enter fullscreen mode Exit fullscreen mode

yep.

A colleague pointed me to
"Functors, applicatives and monads in pictures".
By just reading the article it was hard to make something out of it. It started
to make sense to me only after I opened the browser console and started typing
JavaScript to try and follow along. Learning by doing works better than reading
alone.

Opening the browser developer console
for this series is also a great way to follow along what I am building 🙂

I could replicate the concepts, but I did not understand them, or how it would
help me write better software.

I was intrigued by
a video about Parser Combinators
by Scott Wlaschin, because I work and create DSLs in various projects. It looked
like fun to build something like that for myself.

So I decided to create a challenge for myself, to learn more about Functional
Programming and playing around with the concept of Parser Combinators.

The Challenge

Since I'm more of a learning by doing kind of guy and work only in JavaScript
nowadays, I opened my editor and defined the following challenge in a
challenge.js file:

const data = `
{
  "name": "Functional programming challenge",
  "goal": [
    "create a function that takes one argument that is a path into this JSON struct",
    "get(data)(\\"using.disallowed.0\\") should result in \\"Dependencies\\"",
    "get(data)(\\"points.full-json\\") should result in 1000",
    "get(data)(\\"jsonTypes.2\\") should result in false"
  ],
  "using": {
    "allowed": [
      "Only code in this file",
      "Only functions that take one argument",
      "Ternary operator true ? a : b"
    ],
    "disallowed": [
      "Dependencies",
      "Recursion to own function",
      "JSON.parse",
      "Usage of 'if'",
      "Usage of for/while loops",
      "Usage of functions before they are defined",
      "Host object functions (.split, .map, .reduce, etc)",
      "Multiple statements in a lambda"
    ]
  },
  "hints": [
    "Think about function composition and currying",
    "Think about parser combinators",
    "https://fsharpforfunandprofit.com/posts/understanding-parser-combinators/"
  ],
  "points": {
    "not-trying": 0,
    "started": 5,
    "full-json": 1e3,
    "without-recursion": 1e4
  },
  "jsonTypes": [null, true, false, -0.12]
}
`;

/**
 * So, not like this!
 * no function usage of the host language, no dependencies
 *
 * import get from "lodash/get";
 *
 * const ast = JSON.parse(data);
 * const get = ast => path => get(ast, path)
 * console.log(get(ast)("using.disallowed.0"));
 *
 */
Enter fullscreen mode Exit fullscreen mode

Would I be able to create purely functional code to parse the defined JSON?

Yes, I even added a Recursion to own function in there, after watching
"The next great functional programming language"
just to see if it would really be possible.

In this talk, John de Goes argues that a good programming language could do
without:

Pattern matching, Records, Modules, Syntax, Type classes, Nominative typing,
Data, Recursion

https://www.slideshare.net/jdegoes/the-next-great-functional-programming-language
(see slide 11 for the list)

Having the blogpost of Parser Combinators ready at hand, it would be a simple
exercise of following along and implementing it in JavaScript and trying to
apply a few more rules along the way.

But I was wrong!

I never knew I would learn so much in a few days...

In the upcoming posts I would like to take you along the journey I made into
Functional Programming. This post deliberately ends here, so if you would like
to do the challenge yourself, you can do so without spoilers.

Note:

I originally did this challenge in the beginning of 2019. I revisited this
challenge again in 2020, and did a typescript implementation. (to learn
typescript as well 😄) It does not change the concepts used here, but the
implementation will slightly differ. When it makes sense, typescript parts are
included as well. (full typescript and javascript implementations will be
shared at the end of the series).

My Journey in learning Functional Programming #2

Already read part 1? Part 2 and part 3 of this Blog Post series are out now!

Discussion (0)