Skip to content

Instantly share code, notes, and snippets.

@jcoveney
Created October 2, 2014 23:58
Show Gist options
  • Save jcoveney/4e609e6318a5f8037bda to your computer and use it in GitHub Desktop.
Save jcoveney/4e609e6318a5f8037bda to your computer and use it in GitHub Desktop.
Endofunctor attempt
// The following are just some useful category theoretic type classes
// Note that this IS an endofunctor -- it has to be. Our category is all possible types,
// so the type A and T[A] are both in our Category, so it's an endofunctor
trait Functor[T[_]] {
def map[A, B](fn: A => B): T[A] => T[B]
}
trait Monad[T[_]] extends Functor[T] {
// we go with the less common join approach because THIS is where the "monoid"-ness of
// the Monad comes in. Because we take T[T[A]] => T[A], which if you wave your hands
// is like T . T => T, which looks Monoid-y
def join[A](t: T[T[A]]): T[A]
def unit[A](a: A): T[A]
// We implement this to show that a Monad is flatMap/unit, or join/map/unit
def flatMap[A, B](fn: A => T[B]): T[A] => T[B] = { a: T[A] => join(map(fn)(a)) }
}
trait Monoid[T] {
def plus(left: T, right: T): T
def zero: T
}
// We could just use the monad, but to make it explicit we have this split out
implicit val listFunctor: Functor[List] = new Functor[List] {
override def map[A, B](fn: A => B): List[A] => List[B] = { a: List[A] => a.map(fn) }
}
implicit val listMonad: Monad[List] = new Monad[List] {
override def join[A](t: List[List[A]]): List[A] = t.flatten
override def unit[A](a: A): List[A] = List(a)
override def map[A, B](fn: A => B): List[A] => List[B] = listFunctor.map(fn)
}
implicit def monadIsAMonoid[T[_]](implicit m: Monad[T]): Monoid[Functor[T]] = new Monoid[Functor[T]] {
override def plus(left: Functor[T], right: Functor[T]): Functor[T] = new Functor[T] {
override def map[A, B](fn: A => B): T[A] => T[B] = { a: T[A] =>
// This looks a little weird, but if you squint at it, it's just calling join, the structural "plus" of the monad,
// on the right and left mapped together
m.join(right.map(left.map(fn))(m.unit(a)))
}
}
// This is actually useful because it gets at the heart of what we are composing. We are composing the "functor"-iness
// of a monad
override def zero: Functor[T] = new Functor[T] {
override def map[A, B](fn: A => B): T[A] => T[B] = m.map(fn)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment