If you're looking into functional programming for the first time, the terminology can be really overwhelming. I think one of the easiest ways to learn is to try and map the terms to concepts that you likely already know and then branch out from there.

All of these terms have *laws* that express limitations which ensure that all instances behave reasonably. We shan't go over them here, but it's good to know that - even if we're not ready to look into them yet - they exist, that there is a rich mathematical backing to these concepts. If this piques your curiosity at all, the best resource is probably Typeclassopedia on HaskellWiki.

All of these examples will be written in both Haskell and TypeScript. The latter will be written with the fp-ts library.

For some reason, different languages sometimes call the same concepts different things. For example, Haskell has the `Maybe`

type, whilst Rust and fp-ts have the identical `Option`

type. Equally, Haskell and fp-ts have the `Either`

type, whilst Rust has opted to call it `Result`

. Don't let this discrepancy throw you off, they're otherwise identical.

Without any further ado let's get started!

## Functor

A functor is some sort of container that allows you to map its contents. Arrays are the prototypical functor:

```
(*2) <$> [1, 2, 3] -- [2, 4, 6]
```

```
[1, 2, 3].map(x => x * 2) // [2, 4, 6]
```

Here we've taken each item in our array and applied our function to it. The very same concept applies to types like `Option`

:

```
(*2) <$> (Just 5) -- Just 10
(*2) <$> Nothing -- Nothing
```

```
option.map(some(5), x => x * 2) // Some 10
option.map(none, x => x * 2) // None
```

If the value is `Some`

, then we map the inner value, else if it's `None`

then we short-circuit and essentially do nothing.

There's nothing that technically says that functors have to map over `Some`

in the case of `Option`

, or `Right`

in the case of `Either`

, except it's universally expected behavior and to do otherwise would be very odd.

## Bifunctor

For types with (at least) two variants which you might want to map, for example tuples, or `Either`

with its `Left`

and `Right`

variants, there is the concept of a *bifunctor*. This is the very same as functor, except as the name implies you can map "the other side" as well:

```
first (*2) (Left 5) -- Left 10
first (*2) (Right 5) -- Right 5
second (*2) (Left 5) -- Left 5
second (*2) (Right 5) -- Right 10
```

```
either.mapLeft(left(5), x => x * 2) // Left 10
either.mapLeft(right(5), x => x * 2) // Right 5
either.map(left(5), x => x * 2) // Left 5
either.map(right(5), x => x * 2) // Right 10
```

## Monad

Ah, the scary sounding one, the monad! Monads build atop functors with one important addition, the idea of *joining* or flattening. As with the functor, we'll start by demonstrating how arrays are also monads:

```
join [[1, 2], [3, 4]] -- [1, 2, 3, 4]
```

```
[[1, 2], [3, 4]].flat() // [1, 2, 3, 4]
```

And likewise with nested `Option`

s:

```
join (Just (Just 5)) -- Just 5
join (Just (Nothing)) -- Nothing
join Nothing -- Nothing
```

With this newfound ability to flatten things, we can now also *bind* or chain things.

Let's imagine we have a function `parse`

which takes a `string`

, tries to parse it as a `number`

, and returns `Option<number>`

, and to start with we have an `Option<string>`

. Thus far, the only way we could make this work would be to map with a functor, giving us back `Option<Option<number>>`

, and then join down to `Option<number>`

. That works, but is a bit tedious and we can imagine needing to perform this combination of operations quite often.

This is what bind is for!

```
Just "5" >>= parse -- Just 5
Just "x" >>= parse -- Nothing
Nothing >>= parse -- Nothing
```

```
option.chain(some('5'), parse) // Some 5
option.chain(some('x'), parse) // None
option.chain(none, parse) // None
```

What else do we know in JavaScript-land which is monad-like? The promise! A promise is - imprecisely - a functor, a bifunctor, and a monad, among other things. When we `.then`

, we're either functor mapping or monad binding depending upon whether we're returning another promise (JavaScript handles this implicitly), and when we `.catch`

we're either bifunctor mapping or sort of monad binding over the left side. Promises aren't *really* monads because of these slightly different behaviours, but they absolutely are analagous.

Further, async/await is like a specialised form of Haskell's do notation. In this example in Haskell, `IO`

is just another monad, but *any* monad supports this syntax:

```
f :: String -> IO Int
f x = do
res <- getData x
res * 2
```

```
const f = async (x: string): Promise<number> => {
const res = await getData(x);
return res * 2;
};
```

Before we move on, if you were wondering why JavaScript's promise isn't a proper functor or monad, here's the legacy of that unfortunate decision:

# Comment for #94

**domenic**commented on

Yeah this is really not happening. It totally ignores reality in favor of typed-language fantasy land, making a more awkward and less useful API just to satisfy some peoples' aesthetic preferences that aren't even applicable to JavaScript. It misses the point of promises (modeling synchronous control flow from imperative languages), albeit in a novel way from the usual misunderstandings.

It is also hilariously inaccurate, as the thenable described comes nowhere near satisfying the spec. My guess is that it would pass approximately one of the ~500 tests in our test suite.

Someone more diplomatic than me should probably chime in too.

It hasn't aged particularly well. This also happens to be whence the fantasy-land specification derived its name.

## Semigroup

Semigroups define how to concatenate two items of the same type. For example, arrays are semigroups:

```
[1, 2] <> [3, 4] -- [1, 2, 3, 4]
```

```
[1, 2].concat([3, 4]) // [1, 2, 3, 4]
```

You could equally define a semigroup instance for numbers under addition and multiplication, or for booleans under conjunction and disjunction. If the underlying mathematics interest you, you can read more about semigroups on Wikipedia.

We can also define semigroups for arbitrary types! Let's imagine we have the type `Cocktail`

, and we want to be able to combine any two of them together. Given a definition for the type as follows:

```
data Cocktail = Cocktail
{ name :: String
, ingredients :: [String]
}
```

```
type Cocktail = {
name: string;
ingredients: string[];
};
```

We can then define a formal semigroup instance which will allow us to combine any pair of cocktails together:

```
instance Semigroup Cocktail where
a <> b = Cocktail (name a <> " " <> name b) (ingredients a <> ingredients b)
mojito = Cocktail "Mojito" ["rum", "mint"]
robroy = Cocktail "Rob Roy" ["scotch", "bitters"]
combined = mojito <> robroy -- Cocktail { name = "Mojito Rob Roy", ingredients = ["rum", "mint", "scotch", "bitters"] }
```

```
const semigroupCocktail: Semigroup<Cocktail> = {
concat: (a, b) => ({
name: a.name + ' ' + b.name,
ingredients: a.ingredients.concat(b.ingredients),
}),
};
const mojito: Cocktail = { name: 'Mojito', ingredients: ['rum', 'mint'] };
const robroy: Cocktail = { name: 'Rob Roy', ingredients: ['scotch', 'bitters'] };
const combined = semigroupCocktail.concat(mojito, robroy); // { name: 'Mojito Rob Roy', ingredients: ['rum', 'mint', 'scotch', 'bitters'] }
```

## Monoid

Like how the monad derives most of its abilities from the functor, as does the monoid from the semigroup. A monoid is a semigroup with one extra thing - an *identity* element, which means essentially a sort of "default" element which, when concatenated with others of its type, will result in the same output.

Here are some example identity elements in mathematics:

- Addition/subtraction:
`0`

,`5 + 0 == 5`

&`5 - 0 == 5`

- Multiplication/division:
`1`

,`5 * 1 == 5`

&`5 / 1 == 5`

See how when we apply the identity element to an operation alongside *n* we always get said *n* back again. We can do the same thing with types when we're programming. Once again, let's start with arrays:

```
[1, 2] <> [] -- [1, 2]
```

```
[1, 2].concat([]) // [1, 2]
```

If we concatenate an empty array with any other array, we'll get said other array back. The same goes for strings which can be thought of conceptually as arrays of characters, which happens to be exactly what they are in Haskell.

What about our `Cocktail`

type from earlier? Given the two fields are each already monoids, or easy to treat as monoids - a string and an array - this will be quite simple:

```
instance Monoid Cocktail where
mempty = Cocktail mempty mempty
```

```
const monoidCocktail: Monoid<Cocktail> = {
...semigroupCocktail,
empty: { name: '', ingredients: [] },
};
```

This is cool, but truth be told it's relatively rare that we need to concatenate only two items of an arbitrary type. What I find myself wanting to do far more regularly is fold over an array of said items, which is trivially possible using our monoid instance. Here we'll just fold over small arrays, but this can work for arrays of any size at all:

```
mconcat [] -- Cocktail { name = "", ingredients = [] }
mconcat [mojito] -- Cocktail { name = "Mojito", ingredients = ["rum", "mint"] }
mconcat [mojito, robroy] -- Cocktail { name = "Mojito Rob Roy", ingredients = ["rum", "mint", "scotch", "bitters"] }
```

```
fold(monoidCocktail)([]) // { name: '', ingredients: [] }
fold(monoidCocktail)([mojito]) // { name: 'Mojito', ingredients: ['rum', 'mint'] }
fold(monoidCocktail)([mojito, robroy]) // { name: 'Mojito Rob Roy', ingredients: ['rum', 'mint', 'scotch', 'bitters'] }
```

This is equivalent to reducing over an array of items using the semigroup concatenation operation as the function and the monoidal identity element as the starting value.

## Sequence

Here's one that's super useful but you mightn't have heard of. Sequencing is the act of inverting the relationship between two types:

```
sequenceA [Just 5, Just 10] -- Just [5, 10]
sequenceA [Just 5, Nothing] -- Nothing
```

```
const seqOptArr = array.sequence(option);
seqOptArr([some(5), some(10)]) // some([5, 10])
seqOptArr([some(5), none]) // none
```

This is something you've probably done plenty of times but never knew that this is what it was - this is what you're doing when you call `Promise.all`

in JavaScript! Think in terms of types: We take an array of promises, and we convert it to a promise of an array. We inverted the relationship or, as we now know to call it, we sequenced!

As with `Promise.all`

, the sequence will short-circuit to the fail-case if anything fails.

## Traverse

Hot on the heels of sequencing is traversal, which is essentially just a combination of sequencing with a functor map after-the-fact. You'll find that operations which are very common like this often have functions predefined in the likes of Haskell.

```
traverse (fmap (*2)) [Just 5, Just 10] -- Just [10, 20]
traverse (fmap (*2)) [Just 5, Nothing] -- Nothing
```

```
const traverseOptArr = array.traverse(option);
traverseOptArr([some(5), some(10)], option.map(x => x * 2)) // some([10, 20])
traverseOptArr([some(5), none], option.map(x => x * 2)) // none
```

Just like with sequencing, this will short-circuit if the type we're inverting is already in its failure state.

This post can also be found on my personal blog: https://www.samhh.com/blog/js-fp-jargon

## Top comments (0)