Skip to content

Instantly share code, notes, and snippets.

@manjuraj
Created July 9, 2015 06:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save manjuraj/f0c832584131ac345f92 to your computer and use it in GitHub Desktop.
Save manjuraj/f0c832584131ac345f92 to your computer and use it in GitHub Desktop.
scalaz applicative
//
// 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment