Enter Your Email Address to Watch This Lesson

Your link to unlock this lesson will be sent to this email address.

Unlock this lesson and all 986 of the free egghead.io lessons, plus get JavaScript content delivered directly to your inbox!



Existing egghead members will not see this. Sign in.

A curated collection of Monoids and their uses

1:12 JavaScript lesson by

A gallery of monoid definitions and some use cases

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

A gallery of monoid definitions and some use cases

Avatar
Sean

Having trouble with the foldMap over the stats List.of. Getting acc.concat is not a function, any ideas? Here's my code: http://bit.ly/2hqhXZ8

Avatar
Brian Lonsdorf

Posted on Github, but posting here for others. Data.Either has not implemented concat/traverse yet. Here's our either

const Right = x =>
({
  chain: f => f(x),
  ap: other => other.map(x),
  traverse: (of, f) => f(x).map(Right),
  map: f => Right(f(x)),
  fold: (f, g) => g(x),
  concat: o =>
    o.fold(_ => Right(x),
           y => Right(x.concat(y))),
  inspect: () => `Right(${x})`
})

const Left = x =>
({
  chain: f => Left(x),
  ap: other => Left(x),
  traverse: (of, f) => of(Left(x)),
  map: f => Left(x),
  fold: (f, g) => f(x),
  concat: o =>
    o.fold(_ => Left(x),
           y => o),
  inspect: () => `Left(${x})`
})

const fromNullable = x =>
  x != null ? Right(x) : Left(null)

const tryCatch = f => {
  try {
    return Right(f())
  } catch(e) {
    return Left(e)
  }
}

module.exports = { Right, Left, fromNullable, tryCatch, of: Right }
In reply to Sean
Avatar
Sean

Sweeet, thanks! FWIW I also added isRight: true, isLeft: false to Right and vice versa to Left in order to get First and find working. Really enjoying this course, thank you.

In reply to Brian Lonsdorf
Avatar
Fred

Hi. I am really enjoying this course so far: I generally have the same questions as the little characters attending Pr Frisby 😂

I just have a couple questions:

  1. why is empty() defined in a separate statement instead of within the ({}) like fold, map & concat ?
  2. do you agree that neutral() or identity() would have been more appropriate than empty() if you consider the underlying math

Thank you!

In reply to egghead.io

cue retro music

Example 1

const Sum = x =>
({
    x, 
    concat: ({x: y}) => Sum(x + y)
})

Sum.empty = () => Sum(0)

Example 2

const Product = x =>
({
    x,
    concat: ({x: y}) => Product(x * y)
})

Product.empty = () => Product(1)

Example 3

const Any = x =>
({
    x,
    concat: ({x: y}) => Any(x || y)
})

Any.empty = () => Any(false)

Example 4

const All = x =>
({
    x, 
    concat: ({x: y}) => All(x && y)
})

All.empty = () => All(true)

Example 5

const Max = x =>
({
    x,
    concat: ({x: y}) => Max(x > y ? x : y)
})

Max.empty = () => Max(-Infinity)

Example 6

const Min = x =>
({
    x,
    concat: ({x: y }) => Min(x < y ? x : y)
})

Min.empty = () => Min(Infinity)

Example 7

const Right = x =>
({
    fold: (f, g) => g(x),
    map: f => Right(f(x)),
    concat: o =>
      o.fold(e => Left(e),
             r => Right(x.concat(r)))
})

const Left = x =>
({
    fold: (f, g) => f(x),
    map: f => Left(x),
    concat: o => Left(x)
})

Example 7 Continued (Right)

const stats = List.of({page: 'Home', views: 40},
                      {page: 'About', views: 10},
                      {page: 'Blog', views: 4})
stats.foldMap(x =>
    fromNullable(x.views).map(Sum), Right(Sum(0)))
// Right(Sum(54))

Example 7 Continued (Left)

const stats = List.of({page: 'Home', views: 40},
                      {page: 'About', views: 10},
                      {page: 'Blog', views: null})
stats.foldMap(x =>
    fromNullable(x.views).map(Sum), Right(Sum(0)))
// Left(null)

Example 8

const First = either =>
({
    fold: f => f(either),
    concat: o =>
        either.isLeft ? o : First(either)
})

First.empty = () => First(Left())

Example 8 Continued

const find = (xs, f) =>
    List(xs)
    .foldMap(x =>
        First(f(x) ? Right(x) : Left()), First.empty())
    .fold(x => x)

find([3,4,5,6,7], x => x > 4)
// Right(5)

Example 9

const Fn = f =>
({
    fold: f,
    concat: o =>
        Fn(x => f(x).concat(o.fold(x)))
})

Example 9 Continued

const hasVowels = x => !!x.match(/[aeiou]/ig)
const longWord = x => x.length >= 5

const both = Fn(compose(All, hasVowels))
             .concat(Fn(compose(All, longWord)))

['gym', 'bird', 'lilac']
.filter(x => both.fold(x).x)
// [lilac]

Example 10

const Pair = (x, y) =>
({
    x,
    y,
    concat: ({x: 1, y: 1}) =>
        Pair(x.concat(x1), y.concat(y1))
})
HEY, QUICK QUESTION!
Joel's Head
Why are we asking?