Skip to content

Scala for-comprehensions

| scala | video | functional programming |

This is a complete copy of Josh Suereth & Dick Wall talk on Scala World conference: “For: What is it good for? — Josh Suereth & Dick Wall” (see references).

Actually, I have found github repository with Jupiter notebooks. But, I don’t like this format and decided to store these notes here.

References


01 - For “loops”

01.01

for (i <- 1 to 10) println("hello world")

// Output
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world

01.02

for (i <- 1 to 10) println(i * i)

// Output
1
4
9
16
25
36
49
64
81
100

01.03

for (_ <- 1 to 10) println("hello world")

// Output
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world

01.04

for (i <- 1 to 10) {
    print (f"the square of $i%2d")
    println(f" is ${i*i}%3d")
}

// Output
the square of  1 is   1
the square of  2 is   4
the square of  3 is   9
the square of  4 is  16
the square of  5 is  25
the square of  6 is  36
the square of  7 is  49
the square of  8 is  64
the square of  9 is  81
the square of 10 is 100

01.05

(1 to 10)

// Output
res0: scala.collection.immutable.Range.Inclusive = Range 1 to 10

01.06

(1 to 10).foreach { i =>
    print (f"the square of $i%2d")
    println(f" is ${i*i}%3d")
}

// Output
the square of  1 is   1
the square of  2 is   4
the square of  3 is   9
the square of  4 is  16
the square of  5 is  25
the square of  6 is  36
the square of  7 is  49
the square of  8 is  64
the square of  9 is  81
the square of 10 is 100

01.07

for {
    i <- 1 to 5
    j <- 1 to 5
} println(s"$i times $j is ${i*j}")

// Output
1 times 1 is 1
1 times 2 is 2
1 times 3 is 3
1 times 4 is 4
1 times 5 is 5
2 times 1 is 2
2 times 2 is 4
2 times 3 is 6
2 times 4 is 8
2 times 5 is 10
3 times 1 is 3
3 times 2 is 6
3 times 3 is 9
3 times 4 is 12
3 times 5 is 15
4 times 1 is 4
4 times 2 is 8
4 times 3 is 12
4 times 4 is 16
4 times 5 is 20
5 times 1 is 5
5 times 2 is 10
5 times 3 is 15
5 times 4 is 20
5 times 5 is 25

01.08

(1 to 5).foreach { i =>
  (1 to 5).foreach { j =>
    println(s"$i times $j is ${i*j}")
  }
}

// Output
1 times 1 is 1
1 times 2 is 2
1 times 3 is 3
1 times 4 is 4
1 times 5 is 5
2 times 1 is 2
2 times 2 is 4
2 times 3 is 6
2 times 4 is 8
2 times 5 is 10
3 times 1 is 3
3 times 2 is 6
3 times 3 is 9
3 times 4 is 12
3 times 5 is 15
4 times 1 is 4
4 times 2 is 8
4 times 3 is 12
4 times 4 is 16
4 times 5 is 20
5 times 1 is 5
5 times 2 is 10
5 times 3 is 15
5 times 4 is 20
5 times 5 is 25

01.09

val x1 = (1 to 5).foreach { i =>
  (1 to 5).foreach { j =>
    println(s"$i times $j is ${i*j}")
  }
}

val x2 = for {
  i <- 1 to 5
  j <- 1 to 5
} println(s"$i times $j is ${i*j}")

println(x1)
println(x2)

import scala.reflect.runtime.universe._
def typeOf[T: TypeTag](t: T) = typeTag[T]

typeOf(x1)
typeOf(x2)

// Output

1 times 1 is 1
1 times 2 is 2
1 times 3 is 3
1 times 4 is 4
1 times 5 is 5
2 times 1 is 2
2 times 2 is 4
2 times 3 is 6
2 times 4 is 8
2 times 5 is 10
3 times 1 is 3
3 times 2 is 6
3 times 3 is 9
3 times 4 is 12
3 times 5 is 15
4 times 1 is 4
4 times 2 is 8
4 times 3 is 12
4 times 4 is 16
4 times 5 is 20
5 times 1 is 5
5 times 2 is 10
5 times 3 is 15
5 times 4 is 20
5 times 5 is 25
x1: Unit = ()

1 times 1 is 1
1 times 2 is 2
1 times 3 is 3
1 times 4 is 4
1 times 5 is 5
2 times 1 is 2
2 times 2 is 4
2 times 3 is 6
2 times 4 is 8
2 times 5 is 10
3 times 1 is 3
3 times 2 is 6
3 times 3 is 9
3 times 4 is 12
3 times 5 is 15
4 times 1 is 4
4 times 2 is 8
4 times 3 is 12
4 times 4 is 16
4 times 5 is 20
5 times 1 is 5
5 times 2 is 10
5 times 3 is 15
5 times 4 is 20
5 times 5 is 25
x2: Unit = ()

()
()

import scala.reflect.runtime.universe._
typeOf: [T](t: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[T]

res2: reflect.runtime.universe.TypeTag[Unit] = TypeTag[Unit]
res3: reflect.runtime.universe.TypeTag[Unit] = TypeTag[Unit]

02 - For with yield

02.01

val squares = for (i <- 1 to 10) yield (i * i)

// Output
squares: collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

02.02

val squareMap = (for (i <- 1 to 10) yield (i -> (i * i))).toMap

// Output
squareMap: Map[Int, Int] = Map(
  5 -> 25,
  10 -> 100,
  1 -> 1,
  6 -> 36,
  9 -> 81,
  2 -> 4,
  7 -> 49,
  3 -> 9,
  8 -> 64,
  4 -> 16
)

02.03

case class TimesResult(i: Int, j: Int, mult: Int)

val timesTable = for {
    i <- 1 to 5
    j <- 1 to 5
} yield TimesResult(i, j, i * j)

// Output

defined class TimesResult
timesTable: collection.immutable.IndexedSeq[$user.TimesResult] = Vector(
  TimesResult(1, 1, 1),
  TimesResult(1, 2, 2),
  TimesResult(1, 3, 3),
  TimesResult(1, 4, 4),
  TimesResult(1, 5, 5),
  TimesResult(2, 1, 2),
  TimesResult(2, 2, 4),
  TimesResult(2, 3, 6),
  TimesResult(2, 4, 8),
  TimesResult(2, 5, 10),
  TimesResult(3, 1, 3),
  TimesResult(3, 2, 6),
  TimesResult(3, 3, 9),
  TimesResult(3, 4, 12),
  TimesResult(3, 5, 15),
  TimesResult(4, 1, 4),
  TimesResult(4, 2, 8),
  TimesResult(4, 3, 12),
  TimesResult(4, 4, 16),
...

02.04

val squares = (1 to 10).map(i => i * i)

// Output

squares: collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25, 36, 49, 64, 81, 100)

02.05

val squaresMap = (1 to 10).map(i => i -> (i * i)).toMap

// Output
squaresMap: scala.collection.immutable.Map[Int,Int] = Map(
  5 -> 25,
  10 -> 100,
  1 -> 1,
  6 -> 36,
  9 -> 81,
  2 -> 4,
  7 -> 49,
  3 -> 9,
  8 -> 64,
  4 -> 16
)

02.06

val timesTable = (1 to 5).map(i => (1 to 5).map (j => TimesResult(i, j, i * j)))

// Output

timesTable: collection.immutable.IndexedSeq[collection.immutable.IndexedSeq[TimesResult]] = Vector(
  Vector(
    TimesResult(1, 1, 1),
    TimesResult(1, 2, 2),
    TimesResult(1, 3, 3),
    TimesResult(1, 4, 4),
    TimesResult(1, 5, 5)
  ),
  Vector(
    TimesResult(2, 1, 2),
    TimesResult(2, 2, 4),
    TimesResult(2, 3, 6),
    TimesResult(2, 4, 8),
    TimesResult(2, 5, 10)
  ),
  Vector(
    TimesResult(3, 1, 3),
    TimesResult(3, 2, 6),
    TimesResult(3, 3, 9),
    TimesResult(3, 4, 12),
...

02.07

timesTable.flatten

// Output
res5: collection.immutable.IndexedSeq[TimesResult] = Vector(
  TimesResult(1, 1, 1),
  TimesResult(1, 2, 2),
  TimesResult(1, 3, 3),
  TimesResult(1, 4, 4),
  TimesResult(1, 5, 5),
  TimesResult(2, 1, 2),
  TimesResult(2, 2, 4),
  TimesResult(2, 3, 6),
  TimesResult(2, 4, 8),
  TimesResult(2, 5, 10),
  TimesResult(3, 1, 3),
  TimesResult(3, 2, 6),
  TimesResult(3, 3, 9),
  TimesResult(3, 4, 12),
  TimesResult(3, 5, 15),
  TimesResult(4, 1, 4),
  TimesResult(4, 2, 8),
  TimesResult(4, 3, 12),
  TimesResult(4, 4, 16),
...

02.08

val timesTableFlat = (1 to 5).flatMap(i => (1 to 5).map(j => TimesResult(i, j, i*j)))

// Output
timesTableFlat: collection.immutable.IndexedSeq[TimesResult] = Vector(
  TimesResult(1, 1, 1),
  TimesResult(1, 2, 2),
  TimesResult(1, 3, 3),
  TimesResult(1, 4, 4),
  TimesResult(1, 5, 5),
  TimesResult(2, 1, 2),
  TimesResult(2, 2, 4),
  TimesResult(2, 3, 6),
  TimesResult(2, 4, 8),
  TimesResult(2, 5, 10),
  TimesResult(3, 1, 3),
  TimesResult(3, 2, 6),
  TimesResult(3, 3, 9),
  TimesResult(3, 4, 12),
  TimesResult(3, 5, 15),
  TimesResult(4, 1, 4),
  TimesResult(4, 2, 8),
  TimesResult(4, 3, 12),
  TimesResult(4, 4, 16),
...

02.09

val timesTable = for {
    i <- 1 to 5 // flatMap
    j <- 1 to 5 // map
} yield TimesResult(i, j, i * j)

// Output
timesTableFlat: collection.immutable.IndexedSeq[TimesResult] = Vector(
  TimesResult(1, 1, 1),
  TimesResult(1, 2, 2),
  TimesResult(1, 3, 3),
  TimesResult(1, 4, 4),
  TimesResult(1, 5, 5),
  TimesResult(2, 1, 2),
  TimesResult(2, 2, 4),
  TimesResult(2, 3, 6),
  TimesResult(2, 4, 8),
  TimesResult(2, 5, 10),
  TimesResult(3, 1, 3),
  TimesResult(3, 2, 6),
  TimesResult(3, 3, 9),
  TimesResult(3, 4, 12),
  TimesResult(3, 5, 15),
  TimesResult(4, 1, 4),
  TimesResult(4, 2, 8),
  TimesResult(4, 3, 12),
  TimesResult(4, 4, 16),
...

02.10

for {
  i <- 1 to 3 // flatMap
  j <- 1 to 3 // flatMap
  k <- 1 to 3 // map
} yield i*j*k

// Output
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 2, 4, 6, 3, 6, 9, 2, 4, 6, 4, 8, 12, 6, 12, 18, 3, 6, 9, 6, 12, 18, 9, 18, 27)

03 - Options

03.01

val x = 1
val y = 2
val z = 3

// Output
val result = x * y * z
x: Int = 1
y: Int = 2
z: Int = 3
result: Int = 6

03.02

val ox = Some(1)
val oy = Some(2)
val oz = Some(3)

val oResult = for {
  x <- ox
  y <- oy
  z <- oz
} yield {
  x * y * z
}

// Output
ox: Some[Int] = Some(1)
oy: Some[Int] = Some(2)
oz: Some[Int] = Some(3)
oResult: Option[Int] = Some(6)

03.03

val ox = Some(1)
val oy: Option[Int] = None
val oz = Some(3)

val oResult = for {
  x <- ox
  y <- oy
  z <- oz
} yield {
  x * y * z
}

// Output
ox: Some[Int] = Some(1)
oy: Option[Int] = None
oz: Some[Int] = Some(3)
oResult: Option[Int] = None

04 - Future

04.01

import scala.concurrent._
import ExecutionContext.Implicits.global
import duration._

val f1 = Future { Thread.sleep(10000); 6 }
val f2 = Future { Thread.sleep(10000); 7 }

val f3 = for {
    x <- f1
    y <- f2
} yield x * y

f3.value

// Output
f1: Future[Int] = List(scala.concurrent.impl.CallbackRunnable@5e5167e0)
f2: Future[Int] = List()
f3: Future[Int] = List()
res2_3: Option[scala.util.Try[Int]] = None

04.02

Await.result(f3, 11.seconds)
// Output
res3: Int = 42

04.03

val f1 = Future { Thread.sleep(1000); 6 }
val f2 = Future { Thread.sleep(1000); 7 / 0 }

val f3 = for {
    x <- f1
    y <- f2
} yield x * y

f3.value
Await.result(f3, 2.seconds)

// Output
res0: Option[scala.util.Try[Int]] = None

java.lang.ArithmeticException: / by zero
at .$anonfun$f2$1(<console>:18)
at scala.runtime.java8.JFunction0$mcI$sp.apply(JFunction0$mcI$sp.java:23)
at scala.concurrent.Future$.$anonfun$apply$1(Future.scala:658)
at scala.util.Success.$anonfun$map$1(Try.scala:255)
at scala.util.Success.map(Try.scala:213)
at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
at java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1402)
at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

05 - Guards

05.01

for {
    x <- 1 to 10
    y <- 1 to 10
    if x % 4 < y % 5
    if x % 2 == 0
} yield x * y

// Output
res0: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  8,
  16,
  18,
  4,
  8,
  12,
  16,
  24,
  28,
  32,
  36,
  18,
  24,
  48,
  54,
  8,
  16,
  24,
...

05.02

for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0
    k <- 1 to 10
    if i * j * k % 2 == 0
} yield i * j * k

// Output
res1: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  12,
  18,
  24,
  30,
  6,
  12,
  18,
  24,
  30,
  36,
  42,
  48,
  54,
  60,
  18,
  36,
  54,
  72,
...

05.03

(1 to 10).flatMap { i =>
  (1 to 10).withFilter(j => i % 3 == 0 || j % 3 == 0).flatMap { j =>
    (1 to 10).withFilter(k => i * j * k % 2 == 0).map { k =>
      i * j * k
    }
  }
}

// Output
res2: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  12,
  18,
  24,
  30,
  6,
  12,
  18,
  24,
  30,
  36,
  42,
  48,
  54,
  60,
  18,
  36,
  54,
  72,
...

05.04

for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0
    k <- 1 to 10
    if i * j * k % 2 == 0
} yield i * j * k

// Output
res3: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  12,
  18,
  24,
  30,
  6,
  12,
  18,
  24,
  30,
  36,
  42,
  48,
  54,
  60,
  18,
  36,
  54,
  72,
...

06 - Inline Assignments

06.01

for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0
    k <- 1 to 10
    if i * j * k % 2 == 0
} yield i * j * k  // why repeat this?

// Output
res0: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  12,
  18,
  24,
  30,
  6,
  12,
  18,
  24,
  30,
  36,
  42,
  48,
  54,
  60,
  18,
  36,
  54,
  72,
...

06.02

for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0

    k <- 1 to 10

    mult = i * j * k
    if mult % 2 == 0
} yield mult

// Output
res1: collection.immutable.IndexedSeq[Int] = Vector(
  6,
  12,
  18,
  24,
  30,
  6,
  12,
  18,
  24,
  30,
  36,
  42,
  48,
  54,
  60,
  18,
  36,
  54,
  72,
...

06.03

(1 to 10).flatMap { i =>
  (1 to 10).withFilter(j => i % 3 == 0 || j % 3 == 0).flatMap { j =>
    (1 to 10).map { k =>
      val mult = i * j * k
     (k, mult)
    }.withFilter { 
      case (k, mult) => mult % 2 == 0 
    }.map { 
      case(k, mult) => mult
    }
  }
}

// Output
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 12, 18, 24, 30, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 18, 36, 54, 72, 90, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 6, 12, 18, 24, 30, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 18, 36, 54, 72, 90, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 30, 60, 90, 120, 150, 18, 36, 54, 72, 90, 108, 126, 144, 162, 180, 42, 84, 126, 168, 210, 24, 48, 72, 96, 120, 144, 168, 192, 216, 240, 54, 108, 162, 216, 270, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 24, 48, 72, 96, 120, 144, 168, 192, 216, 240, 36, 72, 108, 144, 180, 216, 252, 288, 324, 360, 30, 60, 90, 120, 150, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 90,...

06.04

// what else can we do with assiGnments?
val mults = for {
    i <- 1 to 5
    _ = println(s"i is $i")   // or logger.info(s"$i") - SIDE EFFECTS!
    j <- 1 to 5
} yield i * j

// Output
i is 1
i is 2
i is 3
i is 4
i is 5
mults: collection.immutable.IndexedSeq[Int] = Vector(
  1,
  2,
  3,
  4,
  5,
  2,
  4,
  6,
  8,
  10,
  3,
  6,
  9,
  12,
  15,
  4,
  8,
  12,
  16,
...

07 - Generators

07.01

for (i <- 1 to 5) yield i * i
// Output
res0: collection.immutable.IndexedSeq[Int] = Vector(1, 4, 9, 16, 25)

07.02

val treasureMap = Map(
  1 -> "Go to island",
  2 -> "Find big X on ground",
  3 -> "Dig to find treasure"
)

for ((stepNo, instruction) <- treasureMap) {
    println(s"Step $stepNo: $instruction")
}

// Output
Step 1: Go to island
Step 2: Find big X on ground
Step 3: Dig to find treasure
treasureMap: Map[Int, String] = Map(
  1 -> "Go to island",
  2 -> "Find big X on ground",
  3 -> "Dig to find treasure"
)

07.03

treasureMap.head match {
    case (stepNo, instruction) => println(s"Step $stepNo: $instruction")
}
// Output
Step 1: Go to island

07.04

treasureMap.map {
    case (stepNo, instruction) => println(s"Step $stepNo: $instruction")
}

// Output
Step 1: Go to island
Step 2: Find big X on ground
Step 3: Dig to find treasure
res3: collection.immutable.Iterable[Unit] = List((), (), ())

07.05

val (stepNo, instruction) = treasureMap.head

// Output
stepNo: Int = 1
instruction: String = "Go to island"

07.06

val foo: Any = "foo"
val (stepNo, instruction) = foo

// Output
scala.MatchError: foo (of class java.lang.String) (foo (of class java.lang.String))

07.07

case class Person(first: String, last: String, age: Int)

val people = Seq(
  Person("Harry", "Potter", 30), 
  Person("Hermione", "Weasley", 30), 
  Person("Ginny", "Potter", 28))

for (Person(first, last, age) <- people) println(s"Amazingly, $first $last is now $age years old")

println("Are you feeling old yet?")

// Output
Amazingly, Harry Potter is now 30 years old
Amazingly, Hermione Weasley is now 30 years old
Amazingly, Ginny Potter is now 28 years old
Are you feeling old yet?
defined class Person
people: Seq[$user.Person] = List(
  Person("Harry", "Potter", 30),
  Person("Hermione", "Weasley", 30),
  Person("Ginny", "Potter", 28)
)

07.08

object Even {
  def unapply(x: Int): Boolean = x % 2 == 0
}

for {
  x @ Even() <- 1 to 100
} yield x

// Output
defined object Even
res7_1: collection.immutable.IndexedSeq[Int] = Vector(
  2,
  4,
  6,
  8,
  10,
  12,
  14,
  16,
  18,
  20,
  22,
  24,
  26,
  28,
  30,
  32,
  34,
  36,
  38,
...

08 - For Grep and Glory

08.01

val filesHere = (new java.io.File(".")).listFiles
for (file <- filesHere if file.getName.endsWith(".ipynb"))
    println(file)

08.02

def fileLines(file: java.io.File) =
    scala.io.Source.fromFile(file).getLines.toList

08.03

val grepResults = for {
  file     <- filesHere
  if file.getName.endsWith(".ipynb")
  line     <- fileLines(file)
  trimmed  =  line.trim
  if trimmed.length > 25
  if trimmed.matches(".*for.*")
} yield trimmed.length -> trimmed

grepResults foreach println

09 - Desugaring the fors

09.01

object ForExpansion1 {
  val mults = for {
    i <- 1 to 3 // flatMap
    j <- 1 to 3 // flatMap
    k <- 1 to 3 // map
  } yield i*j*k
}
// Output
$ scalac -Xprint:parser  ForExpansion1.scala 
[[syntax trees at end of                    parser]] // ForExpansion1.scala
package <empty> {
  object ForExpansion1 extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    val mults = 1.to(3).flatMap(((i) => 1.to(3).flatMap(((j) => 1.to(3).map(((k) => i.$times(j).$times(k)))))))
  }
}

09.02

object ForExpansion2 {
  val mults = for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0
    k <- 1 to 10
    if i * j * k % 2 == 0
  } yield i * j * k
}
// Output
$ scalac -Xprint:parser  ForExpansion2.scala 
[[syntax trees at end of                    parser]] // ForExpansion2.scala
package <empty> {
  object ForExpansion2 extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    val mults = 1.to(10).flatMap(((i) => 1.to(10).withFilter(((j) => i.$percent(3).$eq$eq(0).$bar$bar(j.$percent(3).$eq$eq(0)))).flatMap(((j) => 1.to(10).withFilter(((k) => i.$times(j).$times(k).$percent(2).$eq$eq(0))).map(((k) => i.$times(j).$times(k)))))))
  }
}

09.03

object ForExpansion3 {
  val mults = for {
    i <- 1 to 10
    j <- 1 to 10
    if i % 3 == 0 || j % 3 == 0
    k <- 1 to 10
    mult = i * j * k
    if mult % 2 == 0
  } yield mult
}
// Output
$ scalac -Xprint:parser  ForExpansion3.scala 
[[syntax trees at end of                    parser]] // ForExpansion3.scala
package <empty> {
  object ForExpansion3 extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    val mults = 1.to(10).flatMap(((i) => 1.to(10).withFilter(((j) => i.$percent(3).$eq$eq(0).$bar$bar(j.$percent(3).$eq$eq(0)))).flatMap(((j) => 1.to(10).map(((k) => {
  val mult = i.$times(j).$times(k);
  scala.Tuple2(k, mult)
})).withFilter(((x$1) => x$1: @scala.unchecked match {
  case scala.Tuple2((k @ _), (mult @ _)) => mult.$percent(2).$eq$eq(0)
})).map(((x$2) => x$2: @scala.unchecked match {
      case scala.Tuple2((k @ _), (mult @ _)) => mult
    }))))))
  }
}

09.04

$ scalac -Xshow-phases
    phase name  id  description
    ----------  --  -----------
        parser   1  parse source into ASTs, perform simple desugaring
         namer   2  resolve names, attach symbols to named trees
packageobjects   3  load package objects
         typer   4  the meat and potatoes: type the trees
        patmat   5  translate match expressions
superaccessors   6  add super accessors in traits and nested classes
    extmethods   7  add extension methods for inline classes
       pickler   8  serialize symbol tables
     refchecks   9  reference/override checking, translate nested objects
       uncurry  10  uncurry, translate function values to anonymous classes
     tailcalls  11  replace tail calls by jumps
    specialize  12  @specialized-driven class and method specialization
 explicitouter  13  this refs to outer pointers
       erasure  14  erase types, add interfaces for traits
   posterasure  15  clean up erased inline classes
      lazyvals  16  allocate bitmaps, translate lazy vals into lazified defs
    lambdalift  17  move nested functions to top level
  constructors  18  move field definitions into constructors
       flatten  19  eliminate inner classes
         mixin  20  mixin composition
       cleanup  21  platform-specific cleanups, generate reflective calls
    delambdafy  22  remove lambdas
         icode  23  generate portable intermediate code
           jvm  24  generate JVM bytecode
      terminal  25  the last phase during a compilation run

10 - Other Monads

10.01

val e1: Either[String, Int] = Right(6)
val e2: Either[String, Int] = Right(7)

for {
    x <- e1.right
    y <- e2.right
} yield x * y

// Output
e1: Either[String, Int] = Right(6)
e2: Either[String, Int] = Right(7)
res0_2: Either[String, Int] = Right(42)

10.02

val e1: Either[String, Int] = Right(6)
val e2: Either[String, Int] = Left("Bad Number")

for {
    x <- e1.right
    y <- e2.right
} yield x * y

// Output
e1: Either[String, Int] = Right(6)
e2: Either[String, Int] = Left("Bad Number")
res1_2: Either[String, Int] = Left("Bad Number")

10.03

// add to classpath:
// ----
// classpath.add("org.scalactic" %% "scalactic" % "3.0.0")
// ----

import org.scalactic._

def parseName(input: String): String Or ErrorMessage = {
  val trimmed = input.trim
  if (!trimmed.isEmpty) Good(trimmed) else Bad(s""""${input}" is not a valid name""")
}

def parseAge(input: String): Int Or ErrorMessage = {
  try {
    val age = input.trim.toInt
    if (age >= 0) Good(age) else Bad(s""""${age}" is not a valid age""")
  }
  catch {
    case _: NumberFormatException => Bad(s""""${input}" is not a valid integer""")
  }
}

case class Person(name: String, age: Int)

def parsePerson(inputName: String, inputAge: String): Person Or ErrorMessage =
  for {
    name <- parseName(inputName)
    age <- parseAge(inputAge)
  } yield Person(name, age)

parsePerson("Sally", "25")
parsePerson("     ", "22")
parsePerson("Sally", "twenty eight")

// Output
res5_0: Or[Person, ErrorMessage] = Good(Person("Sally", 25))
res5_1: Or[Person, ErrorMessage] = Bad(
  """
"     " is not a valid name
  """
)
res5_2: Or[Person, ErrorMessage] = Bad(
  """
twenty eight" is not a valid integer
  """
)

10.04

// add to classpath:
// ----
// classpath.add("org.typelevel" %% "cats" % "0.7.2")
// ----

import cats.data._

type IndexState[A] = State[Int, A]
// This returns a next `State` and the index to use for the current node.

def nextIdx: State[Int, Int] =
  State { currentIndex =>
    (currentIndex + 1, currentIndex)
  }

val program: State[Int, (Int, Int, Int)] = 
 for {
   x <- nextIdx
   y <- nextIdx
   z <- nextIdx
 } yield (x,y,z)

program.run(1).value

11 - scala-arm

// add to classpath:
// ----
// classpath.add("com.jsuereth" %% "scala-arm" % "1.4")
// ----

import java.io._
import resource._
// Copy input into output.
for {
  input  <- managed(new java.io.FileInputStream("test.txt"))
  output <- managed(new java.io.FileOutputStream("test2.txt"))
} {
  val buffer = new Array[Byte](512)
  def read(): Unit = input.read(buffer) match {
    case -1 => ()
    case  n =>
      output.write(buffer,0,n)
      read()
  }
  read()
}

12 - Monads don’t mix

12.01

case class Passenger(name: String, cellPhoneNumber: Option[String])
case class Carriage(passengers: List[Passenger])
case class Train(name: String, carriages: List[Carriage])
case class Route(name: String, activeTrain: Option[Train])

val route1 = Route("Glen Gach to Glen Pach",
  Some(Train("The Flying Scotsman",
    List(Carriage(List(
                    Passenger("Rob Roy", Some("121-212-1212")), 
                    Passenger("Connor McCleod", None))),
      Carriage(List(Passenger("Joey McDougall", Some("454-545-4545")))))
  ))
)

val route2 = Route("Defuncto 1", None)

val route3 = Route("Busy Route of Luddites",
  Some(Train("The Tech Express",
    List(Carriage(List(
                    Passenger("Ug", None), Passenger("Glug", None))),
      Carriage(Nil),
      Carriage(List(Passenger("Smug", Some("323-232-3232")))))
  ))
)

val routes = List(route1, route2, route3)

// ---

for {
  route <- routes
  active <- route.activeTrain   // huh!
  carriage <- active.carriages
  passenger <- carriage.passengers
  number <- passenger.cellPhoneNumber
} yield number


// Output
Main.scala:28: type mismatch;
 found   : List[String]
 required: Option[?]
    carriage <- active.carriages
             ^

12.02

routes.flatMap { route =>  // Seq  (flatMap A => Seq[B])
    route.activeTrain.flatMap { active =>  // Option  (flatMap A => Option[B]) // these two
        active.carriages.flatMap { carriage =>  // Seq  (flatMap A => Seq[B])  // are the problem...
            carriage.passengers.flatMap { passenger =>  // Seq
                passenger.cellPhoneNumber.map { number =>  // Option
                    number
                }
            }
        }
    }
}

// Output
Main.scala:27: type mismatch;
 found   : List[String]
 required: Option[?]
        active.carriages.flatMap { carriage =>  // Seq  (flatMap A => Seq[B])  // are the problem...
                                 ^

12.03

for {
    route <- routes
    active <- route.activeTrain.toSeq   // recommended whenever mixing options and seqs
    carriage <- active.carriages
    passenger <- carriage.passengers
    number <- passenger.cellPhoneNumber.toSeq  // unnecessary here, but still clear
} yield number

// Output
res2: List[String] = List("121-212-1212", "454-545-4545", "323-232-3232")

12.04

import scala.concurrent._
import ExecutionContext.Implicits.global
import duration._

val fListONums = Future(List(1,2,3,4,5))
def square(x: Int): Future[Int] = Future(x * x)

for {
    nums <- fListONums
    num <- nums    // doh! - no mixie!
    sq <- square(num)
} yield sq

// Output
Main.scala:43: type mismatch;
 found   : scala.concurrent.Future[Int]
 required: scala.collection.GenTraversableOnce[?]
    sq <- square(num)
       ^
Main.scala:42: type mismatch;
 found   : List[Nothing]
 required: scala.concurrent.Future[?]
    num <- nums    // doh! - no mixie!
        ^

12.05

val fListONums = Future(List(1,2,3,4,5))
def square(x: Int): Future[Int] = Future(x * x)

for {
    nums <- fListONums
    squares <- Future.traverse(nums)(x => square(x))  // Seq[Int] & Int => Future[Int] => Future[Seq[Int]]
} yield squares

// Output
fListONums: Future[List[Int]] = Success(List(1, 2, 3, 4, 5))
res4_2: Future[List[Int]] = Success(List(1, 4, 9, 16, 25))

13 - Emm & M[_]

13.01

// classpath.addRepository("https://dl.bintray.com/djspiewak/maven")
// classpath.add("com.codecommit" %% "emm-cats" % "0.2.1")
// ---

import emm._
import emm.compat.cats._
import cats.std.list._
import cats.std.option._
import cats.std.future._
import scala.concurrent.{Await, Future}
import scala.concurrent.duration._

implicit val ec = scala.concurrent.ExecutionContext.global

type E = Future |: Option |: Base

val effect1 = Option(3).liftM[E]

val effect2 = Option(2).liftM[E]
//val effect2 = (None: Option[Int]).liftM[E]

val effect3 = Future { Thread.sleep(5000); 7}.liftM[E]

val effect4 = for {
  x <- effect1
  y <- effect2
  z <- effect3
} yield x * y * z

effect4.run.value

effect1
effect2
effect3

Await.result(effect4.run, 10 seconds)

14 - How to Option Your Futures

14.01

import scala.concurrent._
import scala.concurrent.ExecutionContext.Implicits.global
import duration._

val f1 = Future(10)
val o2 = Option(20)
val f3 = Future(30)

val result = for {
    x <- f1
    y <- o2
    z <- f3
} yield x * y * z  // Sad Vader...

// Output
Main.scala:44: type mismatch;
 found   : scala.concurrent.Future[Int]
 required: Option[?]
    z <- f3
      ^
Main.scala:43: type mismatch;
 found   : Option[Nothing]
 required: scala.concurrent.Future[?]
    y <- o2
      ^

14.02

val result = for {
    x   <- f1
    z   <- f3
    res =  for (y <- o2) yield x * y * z
} yield res

Await.result(result, 1.second)

// Output
result: Future[Option[Int]] = Success(Some(6000))
res2_1: Option[Int] = Some(6000)

14.03

def multOptions(o1: Option[Int], o2: Option[Int], o3: Option[Int]): Option[Int] =
  for {
    x <- o1
    y <- o2
    z <- o3
  } yield x * y * z

val result = for {
    v1 <- f1
    v3 <- f3
} yield multOptions(Some(v1), o2, Some(v3))

Await.result(result, 1.second)

// Output
result: Future[Option[Int]] = Success(Some(6000))
res3_2: Option[Int] = Some(6000)

15 - Sink

15.01

trait Sink[To] { sink =>
  def apply(t: To): Unit
  final def stage[E]: StagedSink[E, E, To] = StagedSink(StagedSink.stagedIdentity[E], this)
}

final case class StagedSink[First,Current,Final](staged: First => Traversable[Current], sink: Sink[Final]) {
  def map[B, To](f: Current => B)(implicit isDone: CanSink[First, B, Final, To]): To = isDone.result(this, f)
  def flatMap[B, To](f: Current => TraversableOnce[B])(implicit isDone: CanSink[First, B, Final, To]): To = isDone.result2(this, f)
  def withFilter(f: Current => Boolean): StagedSink[First, Current,Final] =
    StagedSink(staged andThen { xs => 
       for(x <- xs; if f(x)) yield x
    }, sink)
}

object StagedSink {
  def stagedIdentity[E]: E => Traversable[E] = (e: E) => List(e)
}

trait CanSink[First, Now, Final, To] {
  def result[E](in: StagedSink[First, E, Final], f: E => Now): To
  def result2[E](in: StagedSink[First, E, Final], f: E => TraversableOnce[Now]): To
}

trait LowPrioritySinkImplicits {
  implicit def sinkChain[First, Now, Final]: CanSink[First, Now, Final, StagedSink[First, Now, Final]] =
    new CanSink[First,Now, Final, StagedSink[First, Now, Final]] {
      def result[E](in: StagedSink[First, E, Final], f: E => Now): StagedSink[First, Now, Final] =
        StagedSink(in.staged andThen (x => x map f), in.sink)
      def result2[E](in: StagedSink[First, E, Final], f: E => TraversableOnce[Now]): StagedSink[First, Now, Final] =
        StagedSink(in.staged andThen (x => x flatMap f), in.sink)
    }
}

object CanSink extends LowPrioritySinkImplicits {
  implicit def finalSink[First, E]: CanSink[First, E,E, Sink[First]] =
    new CanSink[First, E,E, Sink[First]] {
      def result[A](ss: StagedSink[First, A, E], f: A => E): Sink[First] =
        new Sink[First] {
          def apply(in: First): Unit = {
            val staged = ss.staged.andThen { xs => xs map f }
            for { x <- staged(in) } ss.sink(x)
          }
        }
      def result2[A](ss: StagedSink[First, A, E], f: A => TraversableOnce[E]): Sink[First] =
        new Sink[First] {
          def apply(in: First): Unit = {
            val staged = ss.staged.andThen { xs => xs flatMap f }
            for { x <- staged(in) } ss.sink(x)
          }
        }
    }

}

15.02

case class User(name: String, city: String) {
     def livesIn(in: String): Boolean = city == in
  }

  object stdout extends  Sink[String] {
     override def apply(in: String): Unit = System.out.println(in);
  }

  def userSink: Sink[User] =  
    for {
      user <- stdout.stage[User]
      if user livesIn "pittsburgh"
    } yield user.name


  for {
     user <- List(User("josh", "pittsburgh"), User("dick","morgan hill"))
  }  userSink(user)

// Output
josh
defined class User
defined object stdout
defined function userSink