Skip to content

Instantly share code, notes, and snippets.

@aappddeevv
Created September 11, 2013 22:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aappddeevv/6530787 to your computer and use it in GitHub Desktop.
Save aappddeevv/6530787 to your computer and use it in GitHub Desktop.
scala monoids and for comprehensions examples
// Define functions that return an Option
def f1(x: Int): Option[Int] = Some(x*30)
def f2(x: Int): Option[Int] = Some(x+3)
def f3(x: Int): Option[Int] = Some(x*2)
// Sequence the computation, it will be successful
var answer = for {
x <- f1(30)
y <- f2(x)
z <- f3(y)
} yield (x,y,z)
println("answer: " + answer)
// Sequence the computation, the filter will be used
answer = for {
x <- f1(30)
y <- f2(x) if(x>1000)
z <- f3(y)
} yield (x,y,z)
println("answer with filter: " + answer)
// Define functions that return Either
// Note that e2b returns Left as if it is an error.
def e1(x: Int): Either[String, Int] = Right(x*30)
def e2(x: Int): Either[String, Int] = Right(x+3)
def e2b(x: Int): Either[String, Int] = Left("error")
def e3(x: Int): Either[String, Int] = Right(x*2)
// Sequence the computations, knowing it will succeed
var eanswer = for {
x <- e1(30).right
y <- e2(x).right
z <- e3(y).right
} yield z
println("eanswer: " + eanswer)
// Sequence computations, knowing that it will have an error
eanswer = for {
x <- e1(30).right
y <- e2b(x).right
z <- e3(y).right
} yield z
println("eanswer (with Left()): " + eanswer)
/** Prints:
answer: Some((900,903,1806))
answer with filter: None
eanswer: Right(1806)
eanswer (with Left()): Left(error)
**/
// The following does not compile
// eanswer = for { ... y <- e2b(x).left ... } ...
// Because e3, the function after e2b is called expects an int,
// but Left contains a String so when it is unwrapped to call
// e3 it is the wrong type. Scala compile time checks catch this!
// Either, Left and Right do not have a withFilter method
// so you cannot use if() clauses in for comprehensions directly
// when using Either. Option has a withFilter method.
println("Filter on <200: " + e2(33).right.filter[String](_<200))
println("Filter on >200: " + e2(33).right.filter[String](_ > 200))
/** Prints:
Filter on <200: Some(Right(36))
Filter on >200: None
**/
// Sequencing using just the Either monad and no filter
// criteria used on any of the map lines.
// Generally, when using a filter, check the scala
// docs for the monoid you are using (e.g. Option or
// Either) to understand what map, flatMap and withFilter
// do when applied to the monoid value. If withFilter
// is not present, then you may have difficulty using
// an if-condition on one of the "map" lines.
//
// Note that it does not make sense to have a withFilter
// on an Either. When using withFilter on an Option monoid,
// if the filter selects false (and hence returns a None),
// then a Some(yourValue) becomes a None. Both of which are
// well defined. But if you use either and the Right(yourValue)
// (or left depending on which 1/2 you use) is not selected,
// you need to return Left(someOtherYourValue) but someOtherYourValue
// is never defined automatically so there is no "automatic"
// other value to use. That's why you cannot apply a filter
// using the for-comprehension syntax.
//
// In the for-comprehension below, if a Left appears as a result
// of the function call, the for terminates immediately and returns
// the Left as the value.
var eanswer2 = for {
x <- e1(30).right
y <- e2(x).right
z <- e3(y).right
} yield z
println("eanswer with no filter: " + eanswer2)
/** Prints:
eanswer with no filter: Right(1806)
**/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment