-
-
Save manjuraj/7725849 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// (1). Simplified - A Thing[A] to compute another Thing[B] | |
// Thing[A] is like a simplest posssible container for a value and the | |
// pattern, given a Thing[A], and a function flatMap, pulls a value of | |
// type A out of the Thing[A] and applies the function flatMap() on this | |
// value of type A and returns another new Thing[B] with a value of | |
// type B injected into it by the constructor (apply() method here) | |
// encapsulates a monad in loose sense | |
case class Thing[+A](value: A) { | |
def flatMap[B](f: A => Thing[B]) = f(value) | |
} | |
// Anytime you have a pattern where you start with something which you | |
// pull apart and use it to compute a new thing of the same type, then | |
// you have a monad on your hands | |
// The core idea is of using the value from one thing to compute another | |
// thing of the same type as the thing you started out with | |
// (2). Simplified | |
sealed trait Monad[A] { | |
def map[B](f: A => B): Monad[B] | |
def flatMap[B](f: A => Monad[B]): Monad[B] | |
} | |
case class MyMonad[A](value: A) extends Monad[A] { | |
def map[B](f: A => B): Monad[B] = MyMonad(f(value)) | |
def flatMap[B](f: A => Monad[B]): Monad[B] = f(value) | |
} | |
// (3). from: https://gist.github.com/johnynek/4191761 | |
trait Monad[M[_]] { | |
// in haskell, called return, but that's a reserved word | |
// constructs a Monad instance from the given value, e.g. List(1) | |
def apply[T](v: T): M[T] | |
// flatMap function, in scala: | |
def bind[T,U](m: M[T])(fn: (T) => M[U]): M[U] | |
// Laws these must follow are: | |
// identities: | |
// bind(apply(x))(fn) == fn(a) | |
// bind(m)(apply _) == m | |
// associativity on bind (you can either bind f first, or f to g: | |
// bind(bind(m)(f))(g) == bind(m) { x => bind(f(x))(g) } | |
} | |
object Monad { | |
def apply[M[_]](implicit monad: Monad[M]): Monad[M] = monad | |
def bind[M[_],T,U](m: M[T])(fn: (T) => M[U])(implicit monad: Monad[M]) = monad.bind(m)(fn) | |
// Some instances of the Monad typeclass: | |
implicit val listMonad: Monad[List] = ListMonad | |
implicit val optionMonad: Monad[Option] = OptionMonad | |
// Set up the syntax magic (allow .pure[Int] syntax and >>= like Haskell): | |
implicit def pureOp[A](a: A) = new PureOp(a) | |
implicit def operators[A,M[A]](m: M[A])(implicit monad: Monad[M]) = | |
new MonadOperators(m)(monad) | |
} | |
// This is the enrichment pattern to allow syntax like: 1.pure[List] == List(1) | |
// if we put a pure method in Monad, it would take two type parameters, only one | |
// of which could be inferred, and that' annoying to write Monad.pure[Int,List](1) | |
class PureOp[A](a: A) { | |
def pure[M[_]](implicit monad: Monad[M]) = monad(a) | |
} | |
class MonadOperators[A,M[A]](m: M[A])(implicit monad: Monad[M]) { | |
// This is called fmap in haskell (and in Functor, not Monad) | |
def map[U](fn: (A) => U): M[U] = flatMap { (a: A) => monad(fn(a)) } | |
def flatMap[U](fn: (A) => M[U]): M[U] = monad.bind(m)(fn) | |
// Haskell syntax | |
def >>=[U](fn: (A) => M[U]) = flatMap(fn) | |
def >>[T,U](m: M[U]) = flatMap { _ => m } | |
} | |
////////////////// | |
// Implementations: | |
object ListMonad extends Monad[List] { | |
def apply[T](v: T) = List(v) | |
def bind[T,U](m: List[T])(fn: (T) => List[U]) = m.flatMap(fn) | |
} | |
object OptionMonad extends Monad[Option] { | |
def apply[T](v: T) = Option(v) | |
def bind[T,U](o: Option[T])(fn: (T) => Option[U]) = o.flatMap(fn) | |
} | |
///////////////// | |
// Example: | |
import Monad._ | |
println(List(1,2,3,4) >>= { v => (v * 2).pure[List] }) | |
println(Option(1) >>= { v => (v * 2).pure[Option] }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment