Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Try's toOption method returns Some(null)

Tags:

scala

Should Success (the child of Try) return Some(null) when converting it to Option?

E.g. Try(null).toOption in scala 2.11.7 returns Some(null) Success's toOption method implemented simply by returning Some(value). Why doesn't it return None when converting Try to option using toOption method if underlying value of try is null?

like image 556
maks Avatar asked Dec 18 '15 11:12

maks


2 Answers

Here's the source of toOption from Scala 2.11.7:

def toOption: Option[T] = if (isSuccess) Some(get) else None

I agree with Bruno that this is confusing, it should probably read

def toOption: Option[T] = if (isSuccess) Option(get) else None
like image 198
josephpconley Avatar answered Nov 03 '22 17:11

josephpconley


Absolutely not. Option is NOT a null-safety container, it is a Functor, a Monad, and a Collection of size 0 or 1. It can contain null, just like a List can. Just like Try can.

Although it is often used for null-safety, due to Option.apply converting null to None and general Scala best practices avoiding the use of null.

Furthermore, the language does not allow you to define a type parameter that is not null, so Try[A] is implicitly saying that A can be null, according to the language, and Option[A] means that the value can be null too. It is only by convention that A usually is not null.

It would be incorrect to convert a Try containing a null value to None, as it would be truncating the range of A implicitly. Some[A](null) is valid, for any A that is an AnyRef

The language design is at odds with its own best practices here. The language does next-to-nothing to help enforce it. Null is a valid bottom type. An ideal world would allow users to define non-nullable types and the compiler to track null introduction and elimination, which would eliminate these sorts of surprises.

Note that if we made toOption convert null values to None, then the following equality would not hold:

val t: Try = ...
t.toOption.map(f) == t.map(f).toOption

which could lead to other surprises in your code when seemingly harmless refactorings were done, perhaps some minor for comprehension rearrangement that suddenly changes a result from None to something else because of the order of operations that appear to be order independent.

Scala should have had two types for this, one which is a null safety container (Maybe ?) and breaks the law above in general, because it can not contain null, and another that is just like today's Option, but without Option.apply -- an optional value.

Option.apply is the odd man out, and should be removed, IMO.

For example, consider the following, all of which evaluate to Some(null):

Some(Some(null)).flatten
List("a", null).find(s != "a")
List(null, "blah").headOption
Try(null).toOption

Option is used all across the collections library to be a collection of size 0 or 1, and not a null safety device. It makes no sense for these to return None as long as Option is a primarily collection of size 0 or 1, and not a 'non-null' wrapper. If Option was such a thing, then it would not be used across the collections library to represent optional values, such as with find and headOption above.

If anyone truly wants to get into the weeds on the topic, have fun: https://groups.google.com/forum/#!msg/scala-internals/1DXdknjt9pY/_moEcbNd4noJ

5 years ago I thought it was a great idea to get rid of Some(null). Now I would propose getting rid of Option.apply and creating a different type for non-nullness. Call it Maybe. Then one could have Try.toMaybe along side Try.toOption

like image 1
Scott Carey Avatar answered Nov 03 '22 17:11

Scott Carey