Skip to content

Instantly share code, notes, and snippets.

@manjuraj
Created July 9, 2015 06:01

Revisions

  1. manjuraj created this gist Jul 9, 2015.
    156 changes: 156 additions & 0 deletions scalaz-applicative.scala
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,156 @@
    //
    // Apply extends the Functor Typeclass by adding a method `ap` which
    // is similar to `map` from Functor in that it takes a functor that
    // that has a function in it and another functor and extracts that
    // function from the first functor and then maps it over the second
    // one
    //
    // Alternatively, you can say `ap` lets you apply a function in a
    // context to a value in a context
    //
    trait Apply[F[_]] extends Functor[F] { self =>
    def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B]

    // Flipped variant of `ap`
    def apF[A,B](f: => F[A => B]): F[A] => F[B] = ap(_)(f)

    // Lift a function of multiple arguments into Applicative context
    def apply2[A, B, C](fa: => F[A], fb: => F[B])(f: (A, B) => C): F[C] =
    ap(fb)(map(fa)(f.curried))

    def ap2[A,B,C](fa: => F[A], fb: => F[B])(f: F[(A,B) => C]): F[C] =
    ap(fb)(ap(fa)(map(f)(_.curried)))

    ...
    }

    //
    // Applicative
    //
    // Whereas a Functor allows application of a pure function to
    // a value in a context, an Applicative allows application
    // of a function in a context to a value in a context (`ap`)
    //
    // Applicative instances come in a few flavours:
    // - All scalaz.Monad are also Applicative
    // - Any scalaz.Monoid can be treated as an Applicative
    //
    // Every scalaz.Monad is an applicative functor (scalaz.Applicative);
    // every applicative functor is a scalaz.Functor. Not every
    // applicative functor is a Monad
    //
    trait Applicative[F[_]] extends Apply[F] { self =>
    // Takes a value of type A and returns F[A] -- an applicative
    // value with that value inside it
    def point[A](a: => A): F[A]

    final def pure[A](a: => A): F[A] = point(a)

    override def map[A, B](fa: F[A])(f: A => B): F[B] =
    ap(fa)(point(f))

    ...
    }

    import scalaz._
    import scalaz.std.AllInstances._

    scala> type EitherS[T] = \/[String, T]
    defined type alias EitherS

    scala> Apply[Option]
    res0: scalaz.Apply[Option] = scalaz.std.OptionInstances$$anon$1@15b60deb

    scala> Apply[List]
    res1: scalaz.Apply[List] = scalaz.std.ListInstances$$anon$1@2fd3c4b5

    scala> Apply[EitherS]
    res3: scalaz.Apply[EitherS] = scalaz.DisjunctionInstances1$$anon$1@f2a853f

    scala> Applicative[Option]
    res4: scalaz.Applicative[Option] = scalaz.std.OptionInstances$$anon$1@15b60deb

    scala> Applicative[List]
    res5: scalaz.Applicative[List] = scalaz.std.ListInstances$$anon$1@2fd3c4b5

    scala> Applicative[EitherS]
    res6: scalaz.Applicative[EitherS] = scalaz.DisjunctionInstances1$$anon$1@f34d301

    //
    // Applicative abstracts out the constructor of higher kinded
    // Applicative types
    //
    import scalaz._
    import scalaz.syntax.applicative._
    import scalaz.std.list._

    scala> 1.point[List]
    res1: List[Int] = List(1)

    import scalaz._
    import scalaz.syntax.applicative._
    import scalaz.std.option._

    scala> 1.point[Option]
    res0: Option[Int] = Some(1)

    import scalaz._
    import scalaz.syntax.applicative._
    import scalaz.std.either._

    scala> type EitherS[T] = \/[String, T]
    defined type alias EitherS

    scala> 1.point[EitherS]
    res4: EitherS[Int] = \/-(1)

    //
    // Usage of `ap` in Applicative
    //
    import scalaz._
    import scalaz.std.option._
    import scalaz.syntax.std.option._
    import scalaz.syntax.apply._

    val intToString: Int => String = _.toString
    val double: Int => Int = _ * 2
    val addTwo: Int => Int = _ + 2

    scala> Apply[Option].ap(2.some)(intToString.some)
    res0: Option[String] = Some(2)

    scala> 9.some <*> {double.some}
    res1: Option[Int] = Some(18)

    scala> 1.some <* 2.some
    res2: Option[Int] = Some(1)

    scala> 1.some *> 2.some
    res3: Option[Int] = Some(2)

    //
    // |@| - "applicative builder" syntax.
    //
    // If you have a function `f` that takes n arguments, then
    // (x1 |@| x2 // |@| ... |@| xn)(f) is that function `f`
    // lifted into an applicative functor
    //
    import scalaz._
    import scalaz.syntax.apply._
    import scalaz.std.option._
    import scalaz.syntax.std.option._

    scala> (1.some |@| 2.some) { _ + _ }
    res1: Option[Int] = Some(3)

    scala> (1.some |@| 2.some |@| 3.some) { _ + _ + _}
    res1: Option[Int] = Some(6)

    import scalaz._
    import scalaz.syntax.applicative._
    import scalaz.std.option._
    import scalaz.syntax.std.option._
    import scalaz.syntax.apply._

    scala> 9.some <*> { (_: Int) + 3 }.some
    res0: Option[Int] = Some(12)