for-comprehensions

Przemysław Piotrowski

@ppiotrow

Codepot 2015

purpose

  • encouragement to use FC in daily work
  • standard scala 'containers' overview
  • fun with Scala syntax

syntax

for {
  x <- List(1,2,3..10)
  y = x * x
  if y < 9
} yield y //List(1,2)

generator

guard

definition

set-builder notation

S=\{\,x^2|\ x \in [0..10], \ x^2<9\,\}
S={x2 x[0..10], x2<9}S=\{\,x^2|\ x \in [0..10], \ x^2<9\,\}

List[T]

T1.0

Task to do

Option[T]

class M[A] {
    def map[B](f: A => B): M[B]
    def flatMap[B](f: A => M[B]): M[B]
    def filter(p: M => Boolean): M[A]
}
val a: Option[Int] = Some(1)
val b: Option[Int] = None

a.map(_ * 2) //Some(2)
b.map(_ * 2) //None

//def flatMap[B](f: A => Option[B]): Option[B]
a.flatMap{ value => Some(value * 2)} //Some(2)

a.foreach(println) //> "1"

T2.[0-3]

code readability

  • no 'val'
  • let compiler decide (map or flatMap or foreach..)
  • less boilerplate
  • better syntax for anonymous functions
  • less nesting
  • alternative for pattern matching
  • deconstructor

T3.[0-2]

drawbacks

  • only one 'exit point' form for-comprehension
  • more objects to create in Runtime 
    • Tuple
    • Conversion (eg. Option -> List)

syntactic sugar

for {
  i <- List(1,2,3,4)
} yield i*2
List(1,2,3,4)
  .map(i => i*2)
for {
  i <- List(1,2,3,4)
  if i % 2 == 0 
} yield i
List(1,2,3,4)
  .withFilter(i=>i%2==0)
for {
  i <- List(1,2,3,4)
} print(i*2)
List(1,2,3,4)
  .foreach(i => i*2)

T3.3

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#for-comprehensions-and-for-loops

Try

  • Container for object potentially failed
val input: Try[Int] = Try(StdIn.readLine("Number: ").toInt)
val result: Try[Int] = i.map(123/_)

result match {
    case Success(x) => print(s"Result is $x")    
    case Failure(_) => print("Please try again")
}

T4.[0-3]

Future[T]

  • container for object available 'in the future'
  • future might be finished with Failure or Success
val accBalance = Future {
    Thread.sleep(10*1000)
    5}

val newAccBalance = accBalance.map(_ * 1.035)

newAccBalance
    .recover{case ex: Exception => MILION }
    .onSuccess{balance => println(balance)}
//def onComplete[U](f: Try[T] => U): Unit

for {
    i <- Future.successful(1)
} yield i * 3

T5.[0-5]

Either[L,R]

  • Left[L] - failure
  • Right[R] - success
def espresso(water: Water, beans: Beans): Either[String, Espresso]={
    if (water.temperature < 93)
      Left("Water too cold")
    else if (!beans.kind.equals("Robusta"))
      Left("Wrong type of beans")
    else
      Right(Espresso())
  }
val coffee = espresso(water,beans)

coffee match {
    case Left(errorMsg) => displayError(errorMsg)
    case Right(espresso) => drink(espresso)
}

val result: Either[Alert, Espresso] = coffee.right
                                       .map(coffee.sweeten)
                                           .left
                                       .map(Alert _)

Either[_,Tiramisu]

Tiramisu with validation

T6.0

Some(1).map{i => 
  val j = i+1
  (i, j)
}.map( case (i, j) => j)
for {
    i <- Some(1)
    j = i + 1
} yield j
CoffeeMachine.espresso(water, beans).right.map{ espresso =>
       val espressoSweeten = espresso.sweeten
       (espresso, espressoSweeten)	
    }.map(case(e, s) => s)
for {
    espresso <- CoffeeMachine.espresso(water, beans).right
    sweeten = espresso.sweeten
} yield sweeten

T6.[1-3]

No .map() on Either

syntax

do we always need same 'shape'?

def flatMap

class M[A] { 
    def flatMap[B](f: A => M[B]): M[B]
    ...
    def foreach(f: A => Unit)
}

T7.1

anti-patterns

val someFuture = Future.successful(1)
val someOption = Option(1)
for {
      a <- someFuture
    } yield for {
      b <- someOption
    } yield a + b
//sigle generator
for(a <- generator) yield a + 1


//multiple generators in parentheses
for(a <- generator; if a >3; b <- gen2) 
    yield a + b
Opinon

summary

List, Option, Future, Either, Try - good for daily work

for-comprehension syntax make work with containers easy and declarative.

 

refactoring 

 

code is readable

Links

  • http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#for-comprehensions-and-for-loops
  • http://arosien.github.io/lovely-for-comps/#slide1
  • https://www.youtube.com/watch?v=n_j2hzHQlNI
  • http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.html
  • http://danielwestheide.com/blog/2013/01/02/the-neophytes-guide-to-scala-part-7-the-either-type.html
  • http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html

Links 2

  • https://issues.scala-lang.org/browse/SI-7515
  • http://loicdescotte.github.io/posts/scala-compose-option-future/
  • https://tersesystems.com/2014/07/10/composing-dependent-futures/
  • http://debasishg.blogspot.co.uk/2011/07/monad-transformers-in-scala.html
  • http://docs.scala-lang.org/tutorials/FAQ/yield.html
  • https://en.wikipedia.org/wiki/Set-builder_notation

StackOverflow

  • http://stackoverflow.com/questions/10866710/using-eithers-with-scala-for-syntax
  • http://stackoverflow.com/questions/18769540/chaining-validation-in-scala
  • http://stackoverflow.com/questions/23265227/using-for-comprehension-try-and-sequences-in-scala
  • http://stackoverflow.com/questions/4730842/how-to-transform-scala-collection-of-optionx-to-collection-of-x
  • http://stackoverflow.com/questions/18954808/for-comprehensions-with-if-guard

Images

  • http://katethebake.blogspot.com/2010/02/gluten-free-tiramisu-daring-bakers.html
  • https://www.flickr.com/photos/59023810@N00/455252569/
  • public MacGyver photos

for-comprehensions

Przemysław Piotrowski

@ppiotrow

Codepot 2015

codepot

By Przemek Piotrowski

codepot

For-comprehensions macgyver

  • 548