Reputation: 28443
In Scala/Java (~ on the JVM), there are some smaller cases where behavior is different like:
/* "Dynamic" cast */
"".getClass.cast("Foo")
//res0: Any = Foo
/* "Static" cast */
classOf[String].cast("Foo")
//res1: String = Foo
/* "Compile time cast" */
"Foo".asInstanceOf[String]
res2: String = Foo
In which languages is the gap between compile time and run time is bigger and are there reasons from a language design POV why this could be a "good thing"?
Going towards the other direction: Is there a (statically typed) language without any differences between compile time and run time types?
Upvotes: 5
Views: 434
Reputation:
C does not have runtime-type information (RTTI). So the static type is the runtime type. More precisely this enables the compiler to eliminate the type information altogether from the object-file and generates faster code (no checking during program execution required). For linking the info is in the headers.
In C++ RTTI exists only with virtual inheritance/abstract base classes, which is a discouraged idiom due to bad performance (by C++'s standards).
In Java (as far as I can recollect) the preferred idiom (at least 7 years ago) was to use a very general interface. This means that there is not much compile-time type information. And of course generics in Java have all Object as static type. So all the information is runtime (with the corresponding overhead). The reason behind that seems to be that static type system are either too rigid (C, Pascal) and need loopholes (void *) or are relatively complex (C++, Haskell).
In Haskell the compile type is usually the runtime type except the case of higher order types and the existential type. GHC for some reason (reflection? type class instantiation?) though does not use this for efficiency gains. So the type info is all runtme.
Upvotes: 1
Reputation: 36051
The reason for the first result is that the getClass
method has the following signature in Java
public final Class<?> getClass()
which Scala inherits. While we know that if we call getClass
on an reference of type T the signature could be
public final Class<? extends T> getClass()
the compiler does not. You could imagine some language extension to provide a special type that represents the static type of the receiver that would enable
public final Class<? extends Receiver> getClass()
or some sort of special casing in the compiler for getClass
. It seems in fact that Snoracle Java indeed special cases getClass
but I'm not sure it's required by the Java Language Specification. However, if you have a reference of some particular static type T, there's no reason you couldn't do the equivalent T.class
(java) or classOf[T]
(scala). In other words such an extension would not bring any greater expressive power but would complicate the implementation of the language.
As for the "compile time cast" versus the "'static' cast", there's really no difference there. It would be correct for a compiler to desugar x.asInstanceOf[T]
to classOf[T].cast(x)
.
Any language with subtyping is going to have a possibility that the statically known type of a reference is less specific than the type of the value to which it refers. Languages with static type systems that do not have subtyping usually do not have a concept of runtime types as there is only one actual type that inhabits a given type name. In these languages types are erased at runtime, much the same way that type parameters are erased on the JVM.
I hope this helps your understanding of static types of references as compared to runtime type of values.
Upvotes: 6