λ Tony's Blog λ

Understanding Monads using Scala (Part 1)

Posted on June 22, 2010

Below are three exercises using Scala. The instructions for each are in the comments. Exercises 1 and 2 must be completed before Exercise 3 (which is just a thinking exercise – no code).

A follow-on to these exercises will be coming.

Hope this helps!

// A typical data type with a single abstract method
case class Inter[A](f: Int => A) {
  // which is a functor
  def map[B](g: A => B): Inter[B] =
    Inter(n => g(f(n)))

  // and a monad (see unital below)
  def flatMap[B](g: A => Inter[B]): Inter[B] =
    Inter(n => g(f(n)).f(n))
}

// unital: A => F[A]
// Implementations for F=Option and F=Inter
object Unitals {
  def unitalOption[A](a: A): Option[A] =
    Some(a)

  def unitalInter[A](a: A): Inter[A] =
    Inter(_ => a)
}

// Exercises
//
// It is recommended to use only map, flatMap and unital* for
// Option or Inter when implementing the exercises below.
// Any other libraries are acceptable (e.g. List functions).
object Sequencing {
  import Unitals._

  // Exercise 1 of 3
  // ===============
  // Implement a function that returns None if the given list
  // contains any None values, otherwise, all the Some values.
  def sequenceOption[A](x: List[Option[A]]): Option[List[A]] =
    error("todo")

    // SOLUTIONx2 (ROT-13)
    /*
    1)
    k.sbyqEvtug(havgnyBcgvba(Avy: Yvfg[N]))((n, o) => n syngZnc (k => o znc (k :: _)))

    2)
    k zngpu {
      pnfr Avy  => havgnyBcgvba(Avy)
      pnfr u::g => u syngZnc (k => frdhraprBcgvba(g) znc (k :: _))
    }
    */

  // Exercise 2 of 3
  // ===============
  // Implement a function that returns an Inter that applies an Int
  // to all the Inter implementations in the List of Inters and returns
  // all the results.
  def sequenceInter[A](x: List[Inter[A]]): Inter[List[A]] =
    error("todo")

    // SOLUTIONx2 (ROT-13)
    /*
    1)
    k.sbyqEvtug(havgnyVagre(Avy: Yvfg[N]))((n, o) => n syngZnc (k => o znc (k :: _)))

    2)
    k zngpu {
      pnfr Avy  => havgnyVagre(Avy)
      pnfr u::g => u syngZnc (k => frdhraprVagre(g) znc (k :: _))
    }
    */

  // Exercise 3 of 3
  // ===============
  // There is repetition in the above exercises.
  // How might we be rid of it?
  // That is for Part 2.

  def main(args: Array[String]) {
    def assertEquals[A](a1: A, a2: A) {
      if(a1 != a2)
        error("Assertion error. Expected: " + a1 + " Actual: " + a2)
    }

    def assertInterEquals[A](a1: Inter[A], a2: Inter[A]) {
      val testInts = List(1, 2, 0, -7, -9, 113, -2048)
      assertEquals(testInts.map(a1.f(_)), testInts.map(a2.f(_)))
    }

    // sequenceOption
    assertEquals(sequenceOption(List(Some(7),
        Some(8), Some(9))), Some(List(7, 8, 9)))
    assertEquals(sequenceOption(List(Some(7), None, Some(9))),
        None)
    assertEquals(sequenceOption(List()),
      Some(List()))

    // sequenceInter
    assertInterEquals(sequenceInter(List()),
      Inter(_ => List()))
    assertInterEquals(sequenceInter(List(Inter(1+),
        Inter(2*))), Inter(n => List(1+n, 2*n)))
  }
}