Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to construct Option object: Option(value) vs Some(value)

What is the pros and cons of 2 ways of initiating Option objects:

1.

def getAmount: Option[Int] = {
  val a: Int = 1
  Option(a)
}

2.

def getAmount: Option[Int] = {
  val a: Int = 1
  Some(a)
}

Which should I use?

like image 698
WelcomeTo Avatar asked Apr 11 '15 15:04

WelcomeTo


1 Answers

There are two important differences. First, Option will return a None if its argument is null:

scala> val x: Option[String] = Some(null)
x: Option[String] = Some(null)

scala> val y: Option[String] = Option(null)
y: Option[String] = None

This can be useful, but it's not always what you want, and (just as importantly) it suggests that there's a reasonable chance that the argument may be null in some cases, which can be misleading.

Some is more appropriate for cases where you want to produce an Option around a value that you know isn't null. Unfortunately the second difference is that the return type of Some(foo) is Some[Whatever], not Option[Whatever], which can be really inconvenient in some situations where having Some inferred means you'll get type errors when you try to use None or Option in certain positions later. In these cases you have to use Some(foo): Option[Whatever], which is pretty unpleasant.

For example, suppose we've got a list of strings representing (we hope) integers, and we want to parse and sum them. We want a None if there's a parsing error and a Some(total) otherwise. The following is a fairly reasonable way to do this in a single traversal using the standard library:

List("1", "2", "3").foldLeft(Some(0)) {
  case (acc, item) => for {
    t <- acc
    n <- util.Try(item.toInt).toOption
  } yield t + n
}

Except that this doesn't work—we get a type error:

<console>:10: error: type mismatch;
 found   : Option[Int]
 required: Some[Int]
                  t <- acc
                    ^

We can fix this by writing .foldLeft(Some(0): Option[Int]), but ugh.

This isn't an issue in your specific example because the return type is explicitly Option[Int], so you don't need to worry about type inference. In that case Some(a) is the right choice.

As a side note, Scalaz provides some and none constructors that help you avoid the type inference problem and noisy solutions like Some(foo): Option[Whatever]:

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> some(10)
res0: Option[Int] = Some(10)

scala> none[Int]
res1: Option[Int] = None

Both return types are Option, which makes type inference a lot easier. You can trivially define these yourself if you don't want to use Scalaz:

scala> def some[A](a: A): Option[A] = Some(a)
some: [A](a: A)Option[A]

scala> def none[A]: Option[A] = None
none: [A]=> Option[A]

If you use these instead of Some and None you never have to worry about an inappropriately specific type being inferred.

To summarize: use Option(foo) only in situations where the argument may be null (which ideally should only be for things like interoperability with Java). Use Some(foo) in cases where the value has been explicitly typed as Option. If the inferred type will be Some[Whatever], add a : Option[Whatever] type annotation, or use something like Scalaz's some.

like image 124
Travis Brown Avatar answered Oct 02 '22 20:10

Travis Brown