2
\$\begingroup\$

Working on an example from Functional Programming in Scala, I'm working on Option#map2:

override def map2[A, B, C](fa: Option[A],fb: Option[B])(f: (A, B) => C): Option[C] = {
  (fa, fb) match {
    case (None, _) => None
    case (_, None) => None
    case (_, _) => Some(f(fa.get, fb.get)) // runtime-safe get calls
  }
}

Is the above implementation reasonable? I presumed that, if either fa or fb were None, then so should be the returned value. Pattern matching seemed the most clean to me, but perhaps there's a cleaner or more concise way to write this method?

\$\endgroup\$

1 Answer 1

5
\$\begingroup\$

Your code is correct - it should work nicely.

The first thing that I would improve on is the // runtime-safe get calls. This is an anti-pattern in Scala and is generally discouraged. In this case you can use pattern matchings powerful extractors to get the value the Option is storing:

case (Some(a), Some(b)) => Some(f(a, b))

But instead of pattern matching the most elegant way to solve this problem is to use for comprehensions. It is functionally the same as your code:

override def map2[A, B, C](fa: Option[A], fb: Option[B])(f: (A, B) => C): Option[C] = {
  for {
    a <- fa
    b <- fb
  } yield f(a, b)
}

Since Option is essentially a collection with either zero or one element it can be used in for-comprehensions. The variable a will only be populated if fa is a Some. If either is None then nothing will be yielded and the result is None.

\$\endgroup\$
5
  • \$\begingroup\$ Thanks, Akos. Could you please provide a reference for .get being an anti-pattern? It makes sense to me, but I'd appreciate a reference for further reading \$\endgroup\$ Commented Apr 1, 2014 at 15:59
  • 1
    \$\begingroup\$ Sorry, I may have overused the word "anti-pattern" as it is not explicitly stated anywhere. The issue is that .get will throw a NoSuchElementException if the Option is empty. In this case you know that it won't be empty, but what happens when you or someone refactors it? Will they make sure that the .get will be called on a code path that is safe? It is generally encouraged (not only in Scala) to avoid this, because you're just setting yourself up for bugs. I would recommend a talk on this topic, it covers the issue and Scala's solution to it quite well: youtu.be/gVXt1RG_yN0 \$\endgroup\$ Commented Apr 1, 2014 at 18:12
  • \$\begingroup\$ Thanks! I went to NeScala 2014 and was thinking of that video right as I clicked your link! \$\endgroup\$ Commented Apr 1, 2014 at 20:31
  • \$\begingroup\$ When I use it like this Option.map2(Some(3), None)(_+_) it fails why? \$\endgroup\$
    – eguneys
    Commented Aug 27, 2015 at 17:36
  • 1
    \$\begingroup\$ @eguneys The issue is that None is essentially Option[Nothing] and the _ + _ then translates to Int + Nothing. This can't compile as Nothing can't be added with Int, to fix this use Option.empty[Int] instead of None! \$\endgroup\$ Commented Aug 27, 2015 at 21:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.