Skip to content

Instantly share code, notes, and snippets.

@zipcode
Created June 5, 2014 22:52
Show Gist options
  • Save zipcode/95e9e96a864c210027db to your computer and use it in GitHub Desktop.
Save zipcode/95e9e96a864c210027db to your computer and use it in GitHub Desktop.
A shitty explanation of Monads in Scala
import scalaz._
import Scalaz._
object HowMonadsWork {
/*
You may have found yourself asking, what is a Monad, anyway?
Some kinda burrito-spacesuit-magical-wavy thing, if you ask the internet.
The internet isn't very helpful. This probably isn't, either.
"Monad" is an interface which some data structures implement. That's all.
What, exacly, does this interface specify?
1. It implements "Functor"
2. It specifies a join operation
Oh, very helpful. What does a Functor interface specify?
1. It implements map
2. You can put a value in it
Cool!
Anything you can put a value in and map over is a Functor.
*/
def functorExample() {
/* You can map an Option */
val a = Some(10)
val a1 = a map { _*2 }
assert(a.get*2 == a1.get)
/* You can map a List */
val l = List(1, 2)
val l1 = l map { _*2 }
assert(l1 == List(2, 4))
/* You can map a function */
val f = { (_: Int) * 2 }
val f1 = f map { _+1 }
assert(f1(1) == 3)
}
/*
With me so far? Cool!
Onwards, to the Monad interface.
'join' means there's some semantics so that you can turn
Monad[Monad[T]] to Monad[T] for some types Monad and T.
In lists, for example, that means flattening them one level.
join is added by scalaz using implicit magic, if you're wondering.
*/
def joinExample() {
val a: List[List[Int]] = List(List(1, 2), List(3, 4))
assert(a.join == List(1, 2, 3, 4))
val b: Option[Option[Int]] = Some(Some(3))
assert(b.join == Some(3))
}
/*
But usually, we don't actually pay attention to join.
The canonical monad operation is a combination of map and
join, usually known as bind, >>=, or flatMap.
"flatMap" is the most descriptive name for it, and realising
that flatMap *was* bind was one of the critical insights for
understanding this for me, after reading too many Haskell guides.
Anyway, you'll find that flatMap is on most collection-y things
in Scala.
*/
def flatMapExample() {
val a = List(1, 2, 3)
def f(x: Int) = List(x, 100+x)
assert(a.flatMap(f) == List(1, 101, 2, 102, 3, 103))
val b = Some(0)
val b1 = b flatMap { x => if (x == 0) None else Some(1/x) }
assert(b1 == None)
}
/*
And finally, 'for' notation, also known as 'do' notation in Haskell.
'for' is simply syntactic suger for map and flatmap. Anything before
the yield is flatMapped, and the yield is a final map.
*/
def forExample() { // pfft
val a = List(1, 2, 3)
val a1 = for (
v <- a;
x <- List(v, v+100)
) yield x
val a2 = a >>= { v => List(v, v+100) }
assert(a1 == List(1, 101, 2, 102, 3, 103))
assert(a1 == a2)
val b = Option(2)
val b1 = for (
x <- b; // for each x in b (ie, 2)
x1 <- if (x==0) None else Some(1/x) // Give us None if that was 0, otherwise 1/x
) yield x1 // And output that
assert(b1 == Option(1/2))
/* This might be surprising if you're used to this! */
for (x <- 0 to 5) yield x*2
/* But it's just sugar again */
(0 to 5) map { _*2 }
/* So of course this */
val f1 = for (x <- 0 to 2; y <- 0 to 2) yield (x, y)
/* is actually */
val f2 = (0 to 2) flatMap { x => (0 to 2) map { y => (x, y)}}
assert(f1 == f2)
}
def main(args: Array[String]) {
functorExample()
joinExample()
flatMapExample()
forExample()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment