Liskov substitution principle:
B is a subtype of A iff anything one can do with A one can do with B.
So why is Null a subtype of all classes in Scala even though by the above definition it isn't a subtype of any class? Couldn't Null have been defined as implementing all possible methods, each returning null? What are the reasons Null was defined the way it is in Scala?
Null is pretty much unavoidable on the JVM. References can be null. Scala can't stop that.
All Scala did was include Java's null into the unified type system. It exists for Java interop, nothing more. Null was wrong in Java, and it's also wrong in Scala.
Yes, null references are the ultimate LSP violation. No, there's no way to fix that. Evaluating method invocations on null to null instead of to throwing NullPointerException wouldn't be any more or less correct, just equally nonsensical.
There's really nothing interesting to see here. Wrap anything nullable in Option(...), forget about this dark pit of incorrectness, and move on.
Couldn't Null have been defined as implementing all possible methods, each returning null?
No. 1. Methods returning primitive types can't return null; 2. the only reason Scala has null is because JVM has it; and the JVM null doesn't behave this way. This would require basically adding an if before every method call unless the compiler can prove the receiver isn't null, which it normally can't.
Related
As the question suggest, what are the different ways to ensure that someone, does not pass null to some of the field of let say a case class.
I know of require, but not a fan, because it blows an unchecked exception.
You can't force it in Scala 2 because of JVM limitations. However, in Scala 3/Dotty there is an interesting opt-in feature called Explicit Nulls.
You can check for more details here: http://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html and here: http://dotty.epfl.ch/docs/internals/explicit-nulls.html
Essentially, you enable this feature with a compiler flag -Yexplicit-nulls and then your code doesn't compile if you violate non nullness:
val x: String = null // error: found `Null`, but required `String`
val x: String | Null = null // ok - Union type
Furthermore, they have additional type improvements for Null union types like Flow Typing and Equality.
The way it works is that compiler makes Null a subtype of Any, and not AnyRef like previously which forces compile time checks. But after erasure due to how JVM is implemented Null becomes a subtype of all reference types.
New hierarchy:
After erasure:
Regual practice in Scala is just not to use null and assume you have well behaved developers who do the same. Use Option instead. On the periphery if you don't trust users of your library/module/etc you can do runtime checks with require.
I was trying to understand some keywords in scala and I found these two keywords looks different in understanding.
What is Nothing Keyword in Scala. Can we create a instance for this?
Where is it useful?
Similarly in Scala, Null and null are same or different?
Nothing is a type and it is at the bottom of the Scala type hierarchy. What this means is that Nothing is a subtype of any other type. Also you cannot create instances of this type.
A well known example in the standard library is the type Nil which is really List[Nothing] under the hood.
Null is a type but null is an instance of the Null type.
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?
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
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
def myMethod(dog: Dog) = {
require (dog != null) // is it possible to already constraint it in the `Dog` type?
}
Is there a way to construct Dog such that it would be an ADT which would never be able to accept a null thus eliminate any null check? (I don't want an Option here, otherwise all my code would turn to have Option based, I want to already constraint the Dog class such that null is never possible, this is why type system is for to allow me to specify constraints in my program).
There was an attempt to provide such functionality (example I'm running in 2.10.4):
class A extends NotNull
defined class A
val x: A = null
// <console>:8: error: type mismatch;
// found : Null(null)
// required: A
// val x: A = null
^
Though it was never complete and eventually got deprecated. As for the time of writing, I don't think it's possible to construct ones hierarchy in a way that prevent you from nulls, without additional nullity checking analysis.
Check out comments in relevant ticket for an insight
I don't think this is possible, generally, because Java ruins everything. If you have a Java method that returns Dog, that could give you a null no matter what language/type features you add to Scala. That null could then be passed around, even in Scala code, and end up being passed to myMethod.
So you can't have non-null types in Scala without losing the interoperability property that Scala objects are Java objects (at least for the type in question).
Unfortunately inheritance makes it very difficult for the computer to know in the general case whether a method could be passed an object that originated from Java - unless everything is final/sealed, you can always subclass a class that handled the object at some point, and override the Dog-returning method. So it requires hairy full-program analysis to figure out the concrete types of everything (and remember, which concrete types are used can depend on runtime input!) just to establish that a given case could not involve Java code.
I have this code:
class MyLinkedList[T](h: T, tail: MyLinkedList[T]) {
def prepend(v: T): MyLinkedList[T] = new MyLinkedList(v, this)
}
I wonder how it comes that I can pass the second parameter as null and it works:
val l: MyLinkedList[Int] = new MyLinkedList(1, null)
null is an instance of MyLinkedList[Int]?? It seems no:
println(null.isInstanceOf[MyLinkedList[Int]])
outputs false.
So why?
This blog post nicely explains null in Scala (together with Null, Nil, Nothing and None):
Null is a trait, which (if you’re not familiar with traits) is sort of like an abstract class in Java. There exists exactly one instance of Null, and that is null. Not so hard. The literal null serves the same purpose as it does in Java. It is the value of a reference that is not refering to any object. So if you write a method that takes a parameter of type Null, you can only pass in two things: null itself or a reference of type Null.
Thus null is not an instance of any type in the Scala type system other than Null:
scala> null.isInstanceOf[Any]
res1: Boolean = false
OK, so it must be an instance of Null then, right? Well...
scala> null.isInstanceOf[Null]
<console>:8: error: type Null cannot be used in a type pattern or isInstanceOf test
null.isInstanceOf[Null]
Overall, as others have noted, Null and null exists solely for backward compatibility with Java. Otherwise, Null is in the same status as Nothing, but the latter has no instances, and it is a more organic part of the Scala type system, as it has well defined roles in specific usage scenarios, such as defining an empty collection, or abnormally exiting from a function call.
null is something supported by Scala for reverse compatibility with other languages that run on the JVM and .NET framework. The language designers aren't particularly thrilled about it but yes, it compiles just like Java code. You should never use it in native Scala situations. Scala provides alternatives, most idiomatically, such as Option with instances Some and None which is essentially a Nullable wrapper, for when you do need to allow a null option.
Look at this
class A
println(null.isInstanceOf[A]) // false
Although I cannot tell why but it seems that isInstanceOf does not work as we expect on null. Another way to do type checking is
null: MyLinkedList[Int]
which works.