1

I'm reading some articles about ScalaZ and have a question about understanding it. In this article, we generalize the sum function, to abstract away the type to be summed.

def sum[T](xs: List[T])(implicit m: Monoid[T]) = //...

Where

trait Monoid[A] is defined as follows:

trait Monoid[A] {
  def mappend(a1: A, a2: A): A
  def mzero: A
}

Yes, this is pretty clear. Monoid here corresponds to the algebraic monoid structure. Now in this article it abstracts over lists. To do this we define the following trait:

trait FoldLeft[F[_]] {
  def foldLeft[A, B](xs: F[A], b: B, f: (B, A) => B): B
}
object FoldLeft {
  implicit val FoldLeftList: FoldLeft[List] = new FoldLeft[List] {
    def foldLeft[A, B](xs: List[A], b: B, f: (B, A) => B) = xs.foldLeft(b)(f)
  }
}

So now we can define the sum function as follows:

def sum[M[_]: FoldLeft, A: Monoid](xs: M[A]): A = {
  val m = implicitly[Monoid[A]]
  val fl = implicitly[FoldLeft[M]]
  fl.foldLeft(xs, m.mzero, m.mappend)
}

I'm not a theory category expert, but it looks like Applicative functor to me. Is that correct? Can we provide such similarity to category theory.

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
St.Antario
  • 26,175
  • 41
  • 130
  • 318

1 Answers1

1

Applicative functor is a type class with two operations: A => F[A] and F[A => B] => F[A] => F[B]. None of operations you mentioned has such signature. FoldLeft is more like Foldable. It's a different type class, a parent of Traversable (aka Traverse). And Traversable is connected with Applicative.

trait Functor[F[_]] {
  def fmap[A, B](f: A => B)(fa: F[A]): F[B]
}

trait Applicative[F[_]] extends Functor[F] {
  def pure[A](a: A): F[A]
  def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
  override def fmap[A, B](f: A => B)(fa: F[A]): F[B] = ap(pure(f))(fa)
}

trait Foldable[T[_]] {
  def foldr[A, B](op: A => B => B)(seed: B)(ta: T[A]): B =
    (foldMap(op)(ta) _)(seed)
  def foldMap[A, M](f: A => M)(ta: T[A])(implicit monoid: Monoid[M]): M =
    foldr[A, M](a => m => monoid.append(f(a), m))(monoid.empty)(ta)
}

trait Traversable[T[_]] extends Functor[T] with Foldable[T] {
  def traverse[F[_]: Applicative, A, B](k: A => F[B])(ta: T[A]): F[T[B]] = 
    sequence[F, B](fmap[A, F[B]](k)(ta))
  def sequence[F[_]: Applicative, A](tfa: T[F[A]]): F[T[A]] = 
    traverse[F, F[A], A](fa => fa)(tfa)
  override def fmap[A, B](f: A => B)(ta: T[A]): T[B] = traverse[Id, A, B](f)(ta)
  override def foldr[A, B](op: A => B => B)(seed: B)(ta: T[A]): B =
    (traverse[Const[B => B]#λ, A, B](op)(ta) _)(seed)
  override def foldMap[A, M: Monoid](f: A => M)(ta: T[A]): M =
    traverse[Const[M]#λ, A, M](f)(ta)
}

type Id[A] = A

trait Const[C] {
  type λ[A] = C
}

Type class Functor means you have a "container" F[_] and you know how to apply function f: A => B inside this container. Type class Applicative means you know how to pack a value a: A inside this container and how to apply a "function" F[A => B] to a "value" F[A]. Foldable means you know how to fold your container using binary operation A => B => B and starting value B and receiving result B. Traversable means you know how to traverse your container executing applicative effect A => F[B] in every "node".

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Very interesting. Thanks a lot. But I would like to understand if ScalaZ `Functor[F[_]]` is the actual theoretical category functor. If so, it's not obvious to me what is category it acts on. I would say that `f: A => B` is the actual theory category arrow. – St.Antario Nov 16 '17 at 23:51
  • But for any given `a: A` we also need to find `F[A]` to make it the real functor. – St.Antario Nov 16 '17 at 23:58
  • It acts on category of types in Scala. Functor `F[_]` maps objects (types) `A` to objects (types) `F[A]` and morphisms (functions) `f: A => B` to morphisms (functions) `fmap(f): F[A] => F[B]` (plus some laws). – Dmytro Mitin Nov 17 '17 at 06:24
  • So functional-programming functor is a partial case of category-theoretic functor for specific category, category of types in Scala. – Dmytro Mitin Nov 17 '17 at 06:26