I am curious:
scala> Some(null) == None
res10: Boolean = false
Why isn\'t Some(null)
transformed to None
?
You should use Option(null)
to reach the desired effect and return None
.
Some(null)
just creates a new Option
with a defined value (hence Some
) which is actually null
, and there are few valid reasons to ever create one like this in real code.
Unfortunately, null
is a valid value for any AnyRef
type -- a consequence of Scala's interoperability with Java. So a method that takes an object of type A
and, internally, store it inside an Option
, might well need to store a null
inside that option.
For example, let's say you have a method that takes the head of a list, checks if that head correspond to a key in a store, and then return true if it is. One might implement it like this:
def isFirstAcceptable(list: List[String], keys: Set[String]): Boolean =
list.headOption map keys getOrElse false
So, here's the thing... if the that inside list
and keys
come from some Java API, they both may well contain null
! If Some(null)
wasn't possible, then isFirstAcceptable(List[String](null), Set[String](null))
would return false
instead of true
.
I think the others in the thread do a good job explaining why Some(null)
"should" exist, but if you happen to be getting Some(null)
somewhere and want a quick way to turn it into None
, I've done this before:
scala> val x: Option[String] = Some(null)
x: Option[String] = Some(null)
scala> x.flatMap(Option(_))
res8: Option[String] = None
And when the starting Option
is a legit non-null value things work as you probably want:
scala> val y: Option[String] = Some("asdf")
y: Option[String] = Some(asdf)
scala> y.flatMap(Option(_))
res9: Option[String] = Some(asdf)
As a simple thought experiment, consider two lists of Strings, one of length 5 and one of length 20.
Because we're running on the JVM, it's possible to insert null
as a valid element into one of these lists - so put that in the long list as element #10
What, then, should the difference be in the values returned from the two following expressions?
EDIT: Exchanged get
for lift
, I was thinking of maps...
shortList.lift(10) //this element doesn't exist
longList.lift(10) //this element exists, and contains null
Much of Scala's WTFs can be attributed to its need for compatibility with Java. null
is often used in Java as a value, indicating, perhaps the absence of a value. For example hashMap.get(key)
will return null
if the key is not matched.
With this in mind, consider the following possible values from wrapping a null returning method in an Option
:
if (b) Some(hashMap.get(key)) else None
// becomes -->
None // the method was not invoked;
Some(value) // the method was invoked and a value returned; or
Some(null) // the method was invoked and null was returned.
Some(null)
seems sufficiently distinct from None
in this case to warrant allowing it in the language.
Of course if this is not desirable in your case then simply use:
if (b) Option(hashMap.get(key)) else None
// becomes -->
None // the method was not invoked or the mapped value was null; or
Some(value) // the method was invoked and a value returned
Because Option is considered to be a Functor and being a Functor means:
unit
function (apply
or just Option("blah")
in Scala)map
function which transforms value from T=>B
but not a contextIn this topic the main part is #2 - Option(1).map(t=>null)
can not transform context. Some
should remain. Otherwise it brakes associative law!
Just consider the following laws example:
def identity[T](v: T) = v
def f1(v: String) = v.toUpperCase
def f2(v: String) = v + v
def fNull(v: String): String = null
val opt = Option("hello")
//identity law
opt.map(identity) == opt //Some(hello) == Some(hello)
//associative law
opt.map(f1 _ andThen f2) == opt.map(f1).map(f2) //Some(HELLOHELLO) == Some(HELLOHELLO)
opt.map(fNull _ andThen f2) == opt.map(fNull).map(f2) //Some(nullnull) == Some(nullnull)
But what if Option("hello").map(t=>null)
produced None
? Associative law would be broken:
opt.map(fNull _ andThen f2) == opt.map(fNull).map(f2) //Some(nullnull) != None
That is my thought, might be wrong