Reputation: 8109
Suppose I have a method session.get(str: String): String
but you don't know whether it will return you a string or a null, because it comes from Java.
Is there an easier way to treat this in Scala instead of session.get("foo") == null
? Maybe some magic apply like ToOption(session.get("foo"))
and then I can treat it in Scala way like
ToOption(session.get("foo")) match {
case Some(_) =>;
case None =>;
}
Upvotes: 122
Views: 53681
Reputation: 45
This is a very old topic but a nice one!
It's true that converting any Non-exception result of Try to Option will result in a Some...
scala> Try(null).toOption
res10: Option[Null] = Some(null)
...because Try is not about nullability checking but just a way to functionally handle exceptions.
Using Try to catch an exception and converting that to an Option for convenience will only show None in case an exception happens.
scala> Try(1/0).toOption
res11: Option[Int] = None
You want to preserve the values that come out of Try. That may be null.
But it is also true that the standard lib is quite confusing sometimes...
scala> Try(null).toOption
res12: Option[Null] = Some(null)
scala> Option(null)
res13: Option[Null] = None
This behaviour is a bit inconsistent but it kind of reflects the intented usage of both Try and Option.
You use try to get whatever comes out of an expression that may throw exceptions, and you don't care about the exception itself.
The value that may come out may very well be a null. If toOption gave None, you could not differenciate between an exception and a null, and that is not pretty!
Standalone, you use Option to encapsulate the existence or not of something. So in that case Some(null) is None, and that makes sense, because null in that case represents the absence of something. There's no ambiguity here.
It's important to remark that in any case referencial transparency is not broken since .toOption is not the same as Option()
If you really need to enforce BOTH exception safety AND null safety, and your code really really doesn't need to differenciate between null and an exception, you just need to combine both paradigms! Because well, that's what you want, right?
You can do it in one way...
scala> Try(Option(null)).getOrElse(None)
res23: Option[Null] = None
scala> Try(Option(3/0)).getOrElse(None)
res24: Option[Int] = None
scala> Try(Option(3)).getOrElse(None)
res25: Option[Int] = Some(3)
... or another ...
scala> Try(Option(null)).toOption.flatten
res26: Option[Null] = None
scala> Try(Option(3/0)).toOption.flatten
res27: Option[Int] = None
scala> Try(Option(3)).toOption.flatten
res28: Option[Int] = Some(3)
... or the ridiculously ugliest of them anothers ...
scala> Option(Try(null).getOrElse(null))
res29: Option[Null] = None
scala> Option(Try(3/0).getOrElse(null))
res30: Option[Any] = None
scala> Option(Try(3).getOrElse(null))
res31: Option[Any] = Some(3)
Upvotes: -1
Reputation: 61
Notice that when working with Java objects it won't work as expected:
val nullValueInteger : java.lang.Integer = null
val option: Option[Int] = Option(nullValueInteger)
println(option) // Doesn't work - zero value on conversion
val nullStringValue : String = null
val optionString: Option[String] = Option(nullStringValue)
println(optionString) // Works - None value
Upvotes: 5
Reputation: 38441
The Option
object has an apply
method that does exactly that:
var myOptionalString = Option(session.get("foo"));
Upvotes: 20
Reputation: 31619
The Option
companion object's apply
method serves as a conversion function from nullable references:
scala> Option(null)
res4: Option[Null] = None
scala> Option(3)
res5: Option[Int] = Some(3)
Upvotes: 213