text: Summary
  Scala has an Option type that provides a type-safe alternative to using null to represent optional values. In this blog post, I show how to replace nested conditionals involving Option with a for-expression.
  Scala's
  Option type has only two possible subtypes,
  Some[T] and
  None. If an
  Option is a
  Some, the optional value does indeed exist. If it is a
  None, however, the value does not exist. In other words,
  None indicates in Scala what
  null often indicates in Java.
  Idiomatic Scala APIs use
  Option to represent optional values. For example, here's a
  find method that looks for a specified character,
  c, in a specified string,
  s:
  def find(c: Char, s: String): Option[Int] = s.indexOf(c) match { case -1 => None case c => Some(c) }
  If the string contains the character,
  find returns the index of the first occurence of the character wrapped in a
  Some.
  Otherwise, it returns
  None to indicate the character does not exist
  in the string.
  Here's another example. The
  superChar method returns the character at the specified index,
  i, of the string, "supercalifragilisticexpialidotious", wrapped in
  a
  Some, or
  None if
  i is greater than or equal to the length of "supercalifragilisticexpialidotious":
  def superChar(i: Int): Option[Char] = { val song = "supercalifragilisticexpialidotious" if (i < song.length) Some(song(i)) else None }
  In the unlikely event that you want to determine the character in supercalifragilisticexpialidotious at the same index as the first occurrence of a particular character in a particular string, you
  could first call
  find. If it's result is a
  Some, you have
  the index of that character in the string. You can then pass that index to
  superChar. If it returns a
  Some, then you have the
  character in supercalifragilisticexpialidotious that sits at the same index as
  your chosen character in your chosen string. Now imagine further you want to find
  the index of the first occurrence of this result character in your
  original string. You could invoke
  find again, this time passing in the result character
  and the original string.
  Now three things could go wrong here:
  The traditional Java-style approach to deal with this situation with a nested if-else construct, as is done in the following
  percolate1 method. If all goes well, it returns the desired index, wrapped in a
  Some. But if any of the three potential problems happen, it returns a
  None:
  def percolate1(c: Char, s: String) = { // (Not idiomatic Scala) val index = find(c, s) if (index.isDefined) { // isDefined is true for Some, false for None val resultChar = superChar(index.get) // get grabs the value out of the Some if (resultChar.isDefined) { find(resultChar.get, s) } else { None } } else { None } }
  A more idiomatic way to accomplish this in Scala is with a nested pattern match. That would look like this:
  def percolate2(c: Char, s: String) = { find(c, s) match { case Some(index) => superChar(index) match { case Some(resultChar) => find(resultChar, s) case None => None } case None => None } }
  While this is a bit more concise and leaves less room for error than the nested if-else construct, Scala has an even better way to accomplish this. You can use a for-expression, like this:
  def percolate3(c: Char, s: String) = for { index <- find(c, s) resultChar <- superChar(index) result <- find(resultChar, s) } yield result
  Using any of these approaches, a
  None will percolate
  out no matter where the failure occurs.
  Here are some examples in the Scala interpreter. Given
  c and
  "cats",
  percolate3 will return
  Some(3) because
  c is at index 0 in
  cats, index 0 in supercalifragilisticexpialidotious contains
  s, and
  s in
  "cats" appears at index 3:
  scala> percolate3('c', "cats") res0: Option[Int] = Some(3)
  The previous example made it all the way through. If any of the three potential problems occurs, a
  None will occur at that step and percolate out all the way to the result. Here the first call to
  find results in
  None, so
  None percolates out as the result of
  percolate3:
  scala> percolate3('d', "cats") res1: Option[Int] = None
  Here the initial call to
  find returns
  Some(68), but because 68 is greater than the length of supercalifragilisticexpialidotious, the subsequent call to
  superChar results in a
  None, which percolates out:
  scala> percolate3('c', "--------------------------------------------------------------------cats") res2: Option[Int] = None
  And here the first call to
  find results in
  Some(2), and
  superChar results in
  Some(u), but because
  "cats" doesn't contain a
  u, the second call to
  find results in a
  None, which is returned:
  scala> percolate3('a', "cats") res2: Option[Int] = None
  Using a for expression for this can seem non-intuitive because we're used to doing
  this kind of thing to iterate over collections.
  One way to think of an
  Option is as a collection that can contain either
  zero or one value--like a special kind of
  List that can either be an
  empty
  List or a
  List with only one value in it. The nice thing is you can compose up to as many operations like this as you need and be confident
  Nones that happen at any step along the way will simply percolate through to the end.
  Have an opinion? Readers have already posted 4 comments about this weblog entry. Why not add yours?
  If you'd like to be notified whenever Bill Venners adds a new entry to his weblog, subscribe to his RSS feed.