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}
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