Skip to content

Instantly share code, notes, and snippets.

@vsuharnikov
Forked from loicdescotte/Forcomptran.md
Last active August 29, 2015 14:20
Show Gist options
  • Save vsuharnikov/91c3e493b9f015eb2da0 to your computer and use it in GitHub Desktop.
Save vsuharnikov/91c3e493b9f015eb2da0 to your computer and use it in GitHub Desktop.

Scala for comprehension translation helper

"For comprehension" is a another syntax to use map, flatMap and withFilter (or filter) methods.

yield keyword is used to aggregate values in the resulting structure.

This composition can be used on any type implementing this methods, like List, Option, Future...

Try it yourself!

Install jad:

brew install homebrew/binary/jad
  1. Compile the code: scalac -d out Forcomptran.scala

  2. To see the decompiled code, run (for example, Case1): jad -lnc -p -t 2 -v ./out/Case1\$.class

Case 1 (Lists)

case class Book(author: String, title: String)
for { 
  book <- books if book.author startsWith "Ivan"
} yield book.title

is the same as:

books.withFilter(book => book.author startsWith "Ivan")
  .map(book => book.title)

Case 2 (Lists)

case class Book(author: String, title: String)
for { book <- books
  author = book.author.replaceAll("\\s+", "_").toLowerCase
  if author startsWith "ivan"
} yield s"$author: ${book.title}"

is the same as:

books
  .map(book => (book, book.author.replaceAll("\\s+", "_").toLowerCase)) // Tuple2(book, author)
  .withFilter(_._2 startsWith "ivan")
  .map { case (book, author) => s"$author: ${book.title}" }

Case 3 (Lists)

case class Book(authors: List[String], title: String)
for { 
  book <- books
  author <- book.authors if author contains "er" 
} yield book.title

is the same as:

books.flatMap(book => 
    book.authors.withFilter(author => author contains "er")
      .map(author => book.title))

Case 4 (Options)

val optA : Option[String] = Some("a value")
val optB : Option[String] = Some("b value")

I want an option tuple : Option[(String, String) composing this two options, i.e. Some(("a value","b value")) :

for {
  a <- optA
  b <- optB
} yield (a,b)

is the same as:

optA.flatMap(a => 
  optB.map(b => 
    (a, b)))

You can also filter options with if / withFilter:

for { 
  /* 1 */ a <- optA
  /* 2 */ if a startsWith "a"
  /* 3 */ b <- optB
} yield (a,b)

Or:

/* 2 */ optA.withFilter(a => a startsWith "a")
/* 1 */ .flatMap(a => 
/* 3 */   optB.map(b => 
            (a, b)))

If you change "a" to "c" in the condition, the resulting value will be None instead of Some(("a value","b value")).

Case 4

You can mix options and list types in map/flatMap (Option can be seen as a simple collection) :

val optNumbers = List(Some(1), Some(2), None, Some(3))

We want to remove empty values and increment other values : List(2, 3, 4) :

for {
  optNumber <- optNumbers
  value <- optNumber
} yield value + 1

is the same as:

optNumbers.flatMap(optNumber => 
  optNumber.map(value => 
    value + 1))
object Case1 extends App {
case class Book(author: String, title: String)
val books = List(
Book("Nikolay Ostrovsky", "How the Steel Was Tempered"),
Book("Maxim Gorky", "Song of a Falcon"),
Book("Ivan Turgenev", "A Conversation on the Highway"),
Book("Ivan Krylov", "The Hermit and the Bear"))
val r1 = for {
book <- books if book.author startsWith "Ivan"
} yield book.title
var r2 =
books.withFilter(book => book.author startsWith "Ivan")
.map(book => book.title)
println(s"$r1\n$r2")
}
object Case2 extends App {
case class Book(author: String, title: String)
val books = List(
Book("Nikolay Ostrovsky", "How the Steel Was Tempered"),
Book("Maxim Gorky", "Song of a Falcon"),
Book("Ivan Turgenev", "A Conversation on the Highway"),
Book("Ivan Krylov", "The Hermit and the Bear"))
val r1 = for { book <- books
author = book.author.replaceAll("\\s+", "_").toLowerCase
if author startsWith "ivan"
} yield s"$author: ${book.title}"
var r2 = books
.map(book => (book, book.author.replaceAll("\\s+", "_").toLowerCase)) // Tuple2(book, author)
.withFilter(_._2 startsWith "ivan")
.map { case (book, author) => s"$author: ${book.title}" }
println(s"$r1\n$r2")
}
object Case3 extends App {
case class Book(authors: List[String], title: String)
val books = List(
Book(List("Baron Schwartz", "Peter Zaitsev", "Vadim Tkachenko"),
"High Performance MySQL"),
Book(List("Tohmas Cormen", "Charles Leiserson", "Ronald Rivest", "Clifford Stein"),
"Introduction to Algorithms"),
Book(List("Cay Horstmann"),
"Scala for the Impatient"))
val r1 = for {
book <- books
author <- book.authors if author contains "er"
} yield book.title
val r2 =
books.flatMap(book =>
book.authors.withFilter(author => author contains "er")
.map(author => book.title))
println(s"$r1\n$r2")
}
object Case4 extends App {
val optA : Option[String] = Some("a value")
val optB : Option[String] = Some("b value")
val r11 = for {
a <- optA
b <- optB
} yield (a,b)
val r12 =
optA.flatMap(a =>
optB.map(b =>
(a, b)))
println(s"$r11\n$r12")
val r21 = for {
/* 1 */ a <- optA
/* 2 */ if a startsWith "a"
/* 3 */ b <- optB
} yield (a,b)
val r22 =
/* 2 */ optA.withFilter(a => a startsWith "a")
/* 1 */ .flatMap(a =>
/* 3 */ optB.map(b =>
(a, b)))
println(s"$r21\n$r22")
}
object Case5 extends App {
val optNumbers = List(Some(1), Some(2), None, Some(3))
val r1 = for {
optNumber <- optNumbers
value <- optNumber
} yield value + 1
val r2 =
optNumbers.flatMap(optNumber =>
optNumber.map(value =>
value + 1))
println(s"$r1\n$r2")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment