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)