Pre-SIP: deprecate asInstanceOf, introduce unsafeAsInstanceOf

Consider the following:

// it is statically known that 1 is not a subtype of String
1.asInstanceOf[Int & String]

I see confusions regarding asInstanceOf over and over again, believing this inserts all kind of runtime checks where in many cases I guess it does not (or inserts something like cast to Any) or only inserts a cast for the erased type (e.g. for List instead of List[String])

Therefore I propose that we deprecate asInstanceOf and instead introduce a new method, something like unsafeAsInstanceOf[T] or compilerTrustMeIsInstanceOf[T] making it clearer that it might also fail at runtime for certain cases.

Thoughts?

1 Like

asInstanceOf never introduces checks !

If there is widespread confusion, then we should make the above fact more obvious in the material (courses, reference, tutorials, etc)
Changing the actual term would cause worlds of pain, as it is fairly well established

5 Likes

this is the scaladoc for asInstanceOf if you hover

Cast the receiver object to be of type T0.

Note that the success of a cast at runtime is modulo Scala’s erasure semantics. Therefore the expression 1.asInstanceOf[String] will throw a ClassCastException at runtime, while the expression List(1).asInstanceOf[List[String]] will not. In the latter example, because the type argument is erased as part of compilation it is not possible to check whether the contents of the list are of the requested type.

in practice do people not read this? can we make it better

2 Likes

there you go, another confusion, it inserts runtime checks (for the JVM backend, apparently not always for JS – just learned again something new about asInstanceOf) but they don’t correspond to what is expected due to type erasure and lowest upper bound calculation in case of union etc.

Can you elaborate about the pain it would introduce? It would still be there (we could drop it with Scala 4 or the like) and replacing it is a search & replace.

can we make it better

I think this phrase “is modulo Scala’s erasure semantics” might be very difficult to understand for a beginner. At least not use jargon like “modulo”… And perhaps explain that erasure is about type info being lost at runtime.

7 Likes

No it really doesn’t !
It does not add any checks that the JVM then performs, it only generates a cast

And then the JVM just so happens to error, but it does not have to
(And it is outside the power of people working on the scala compiler)

And “semantics” !

1 Like

If we keep both, first we have to maintain both, which may not be a lot of work, but is still work, and second then we have to move users from one to the other, which is not an easy task !
Habits are hard to break

1 Like

I think I see your theoretical stand point, from a language level view it does not, the added checkcast in the jvm byte code is merely there to fulfil the jdk requirements which happens to error. But that’s theory, in practice it adds a check which errors and we will have a hard time to explain, especially to beginners, that in theory this error does not necessarily need to be emitted but the jdk implementation does (as it follows the spec of checkcast). Don’t you think so?

I think it’s fine to teach “There is no guarantee on what happens if you use asInstanceOf wrong. Make absolutely sure that 1) You really need it, and 2) You know exactly what you are doing, before using it”

1 Like

IMO it’s part of language evolvement to improve/fix mistakes which were made and IMO the chosen name for asInstanceOf was a mistake. If we can move from implicit to given/using which is most likely used way more often than asInstanceOf, I don’t see why we cannot do it with asInstanceOf.

I think it is better to have a name which does not need teaching.

1 Like

Note that given/using work (very?) differently than implicit, which justified the work put into the name change

I agree we could theoretically do it, just not that it’s worth it

2 Likes

(I would be more inclined to make isInstanceOf more distinct, but as is, I think it is fine to make people scared of using it even though it is safe, since that will push them toward using pattern matching instead)

In my experience, documentation is only read if the method name is not clear enough. Given that the asInstanceOf is very close to isInstanceOf (in terms of letters) and I guess most developers believe they know how isInstanceOf behaves they probably don’t look at the documentation of asInstanceOf assuming a similar behaviour. IMO another argument why we should rename asInstanceOf maybe compilerTrustMeIsInstanceOf or compilerTreatItAs[T] would be even better than unsafeAsInstanceOf

Maybe people think asInstanceOf is the following (without looking at the documetation)?

if( !this.isInstanceOf[T] ) throw ClassCastException("...")

if it were implemented like that then 1.asInstanceOf[Int & String] would fail.
But isInstanceOf also fails short in case of type erasure but in contrast to asInstanceOf we get a warning for isInstanceOf.
I don’t suggest we add a warning to asInstanceOf as well, I believe this would be a pain and would also not solve that asInstanceOf behaves differently for union and intersection types.


I think the documentation can be improved regarding lowest upper bound. Maybe add an example something along the line of:
The compiler has to resort to a lowest upper bound in some cases because the JVM bytecode doesn’t support intersection nor union types. For instance
t.asInstanceOf[Float | Int] will result in a runtime check for java.lang.Number and t.asInstanceOf[Int | String] will result in a runtime check for java.lang.Object

I think changing names like this is a waste of time. Hugely disruptive with marginal (it any) benefits to reap

Everyone already knows casts are unsafe. They are unsafe in Java. They have been unsafe since C introduced them many decades ago. The name asInstanceOf is just a label, but people already understand it is a cast and should understand that casts are unsafe

The scaladoc already specifies the behavior in detail. We don’t need to squeeze every little factoid into the name of the method (unless you’re writing objective-c)

12 Likes

I like the idea that asInstanceOf gets more robust and safe. (Even it’s inherently unsafe by its nature, and is explicitly meant as to be used as “escape hatch”; that’s not the point.)

Therefore I like the idea to introduce warnings for cases where it can be statically deduced that a cast would fail at runtime. (Who doesn’t use -Werror? :smile:)

C# has “safe casts” (even they are quite annoying there because C# doesn’t have a good type system). But in Scala “safe casts” could work fine I think. Similar warnings work already for all kinds of cases quite well (e.g. comparisons, asInstanceOf, …).

1 Like

apparently not everyone knows it and asInstanceOf is a pitfall from time to time.
Also to be more precise, checked casts are safe in Java only unchecked casts are unsafe

1 Like

surely this will always fail, (i.e. it is not a matching type, unless you want it to not warn for List(1).asInstanceOf[List[String]]?) that’s why you need the cast in the first place (please use : Foo for type widening)

Agree 100%.

I understand that asInstanceOf is meant as an “escape hatch”. But I still think it should warn on obviously failing casts.

A beginner may for example try to “convert” a String to an Int by writing "23".asInstaceOf[Int]. The compiler should give a helpful warning in this case. Explaining what asInsaceOf is good for, and that in most cases something other is actually the proper desired solution. (E.g. “to*”-methods, or type ascriptions for widening).

(Maybe) getting the JVM cast exception at that point when running the code is more confusing imho (and requires to actually run that code path, which is not a given). And when you don’t get a cast exception directly at the wrong cast but later on things are usually quite difficult to debug.

For the cases where the compiler can definitely know that things are going to crash at runtime it should in my opinion tell the user upfront. With a nice explanation, and note on the proper use of asInstanceOf.

2 Likes