Proposed illustrative implementation from the related thread:
def asInstanceOf[A]: A =
There has always been (since forever) a drumbeat about asInstanceOf. If education and documentation have failed, why isnāt it behind a SIP-18 import?
My version of IntelliJ, which is up to date and working otherwise, does not provide any documentation for asInstanceOf, and I can imagine that many other Scala developers donāt have immediate access to it, and might not even be aware that it exists, given how hard it is to find online.
Googling āscala asinstanceofā does not return any official documentation in the top results.
I can imagine that a dedicated documentation page, with several examples and pitfalls, could be quite helpful to many people.
I agree that fleshing out the docs, fixing the scaladoc in IDEs, etc. is worthwhile. IntelliJ doesnāt properly jump to definition or show Scaladoc too IIRC (not sure if that changed in some recent version). We should fix that. We can add pages on scala-lang.org to help with SEO. I just donāt think changing the name is the right answer here.
I agree with the other comments that regarding naming the ship has likely sailed.
But if I had to name it today Iād call it unsafelyAssumeType[?]. Because the āasā in the current name points indeed a little bit in the direction of a regular āconversionā method. Usually asSomething Methods are perfectly safe; and actually perform some kind of conversion. But asInstanceOf does not! You just tell the compiler that āyou know betterā, and it should assume youāre right, no questions asked (and especially no conversion inserted).
assInstanceOf. Just like āassumeā makes an āassā of āuā and āmeā, it makes an instance of an ass. (Usually we canāt tell unless a good friend and co-worker points it out to us.)
Thereās been many interesting points made and Iād like to address some of those. So Iāll write multiple posts only so that people can more easily agree or disagree with each of them separately.
I can only speak for myself, but yes. I did read this documentation and FWIW I understood what it does. It helps that people coming with experience of a language that has UB at every corner wonāt be shocked by the lack of guarantees.
IMO, as for many other elements of the documentation, the way to make it better is:
Make sure that one of the top 3 Google results when you type āScala asInstanceOfā on a fresh browser without cookies leads to an official source of documentation.
Have a dedicated page for each method. On scala-lang.org you need to click on the method to see the additional notes. I would MUCH prefer a layout like this.
Better explain why one would use the method and when one should not use it.
Specifically on asInstanceOf, having everything written in a single paragraph is slightly discouraging. Personally it makes me feel like the method is harder to understand that it actually is. I would prefer if it had been displayed that way:
Note that the success of a cast at runtime is modulo Scalaās erasure semantics. For example:
1.asInstanceOf[String] throws a ClassCastException at run-time because the Int and String are distinct after erasure; but
List(1).asInstanceOf[List[String]] does not because List[Int] and List[String] are not distinct after erasure.
From there, if youāre not a C++ developer who got used to believe everything not explicitly documented has UB, it would be good to read something along those lines:
Do not rely on thrown exception to determine whether or not a conversion via asInstanceOf was successful. Behavior is undefined In cases where the language isnāt able to catch an invalid conversion because of erasure (as shown above).
And finally I would add some notes about ways to work around the erasure thing.
I canāt agree more. There are things that are nearly impossible to change and then there are the names of dangerous methods that require extra care in reviews and maintenance.
Further, changes of any kind are possible when thereās a smooth migration path. Thatās obviously the case here. The compiler can emit a deprecation note that says "asInstanceOf is deprecated and will be soon replaced by āasUnsafeInstanceOf (or any color you like for the bike shed)ā.
This kind of deprecation has precedent in other languages. I would not surprised if Scala veterans could name examples in Scala too.
Thatās a very good argument.
If I can go off on a little tangent, I would add that I personally believe Scala lacks a lightweight way to safely narrow a type. Pattern matching certainly works, but even if I donāt go crazy with newlines thatās still 3 lines in braceless syntax.
val x = y match
case z: T => Some(z)
case _ => None
I very much agree. compile-time safety is many times better than its run-time counterpart.
Note that I would also emit a warning in the following situation:
val x = Int
val p = x.isInstanceOf[String]
// conversion from Int to String always fails
IMO thereās no point writing predicates that will always fail. So if the compiler is able to make this determination, it should issue a warning.
One issue to consider is that āunsafeā means different things to different people. My personal definition, which comes from C++, is that unsafe means āprone to UBā. In my experience, thatās typically not what unsafe means for JVM enthusiasts, which is one of the main selling points of using a virtual machine in the first place!
Hence, I would argue that asInstanceOf is more unsafe than most āunsafeā (as described by most Scala developers) operations. Crucially, it is unsafe in a way that makes bugs harder to debug than most other bugs one can write in Scala.
To perhaps better illustrate, consider this example in Swift:
let x: Any = 42
let y = x as! String
let z = unsafeBitCast(x, to: String.self)
If you ask a Swift developer, they will say that the cast initializing y is safe whereas the one initializing z isnāt. Thatās because the first has defined behavior. It will invariably trap at run-time. The second will go through and then perhaps summon nasal demons, which are orders of magnitude harder to debug.
The example you gave in Swift behaves the same in Scala too. Casting to a String traps on failure, bit-bashing using sun.misc.Unsafe does not.
There is definitely trickiness around whether things trap in the presence of erasure, but Iād argue thatās not a Scala-specific thing. Iāve certainly hit ācast succeeds, code blows up somewhere downstreamā bugs in Java as well. And for the new gradual type systems on dynamic runtimes, such as Typescript or MyPy, cast-succeeds-code-blows-up-downstream is the norm, as all static types are erased on those platforms.
I donāt disagree that the behavior in the original post is confusing, and that Scala has its own trickiness due to āadditionalā erasure of type constructs that do not exist in Java. But fundamentally I feel like this behavior around casts is really standard. Iām not actually familiar with any platform where casts are guaranteed to fail loudly; maybe C# with its reified generics? Iām unfortunately not familiar enough with Swift to discuss it in detail
Thinking more on this, I actually disagree in this case.
I would go even further, I think 1.asInstanceOf[String] should not throw an error at runtime !
The reason is, if we do some thing in some cases, it creates an expectation that it does it always, that the compiler has your back.
And in this case, it very much does not !
We can see this effect even in this thread:
I am not sure even having a message like
āThis conversion will always fail, did you mean ā¦ IMPORTANT: Note that this error message is only present for some misuse of asInstanceOf, DO NOT rely on itā
will actually diminish that effect
You can make the example demonstrating a case would not trap in Scala if you want.
let x: Any = [1]
let z = x as! [String] // will trap in Swift, won't throw in Scala
My larger point is that there is a difference between an āunsafeā operation that will consistently throw if its preconditions are violated (e.g., division by 0) and one that could cause UB (e.g., asInstanceOf).
Swift will fail loudly for any conversion that cannot be guaranteed correct at runtime. Thatās because as! (and as?, which is the feature returning the optional that I wish Scala had) are safe operations.
I donāt know if such safety guarantees can be offered in Scala but thatās another discussion. The point Iām making is that AFAICT most operations in Scala are safe (under my definition of safety, i.e., they canāt cause UB) but asInstanceOf isnāt. To me, that justifies adding āunsafeā somewhere in its name.
Scala is not Java, and a Scala dev should not be required to know anything about Java!!! Thatās a longstanding misunderstanding in the Scala community: Scala is not just some syntax sugar atop Java. Such a point of view is actively harmful to Scala as a language. (This issue is also something that shows in some other corners like the poor Scala documentation and tutorials which almost always assume that youāre an expert on all Java idiosyncrasies. This needs to stop finally!)
Java is a different language. Itās āunsafeā all the way down, itās casting and NPE hell, and actually the best you could do in case you wanted to arrive at some sane design is to do the exact opposite of almost everything Java does.
Same goes for any other dynamic language. (Java is at its core a dynamic language, just rewatch the advertisements from the 90ās in case you missed that fact; being a dynamic language is core to the problem with Java!). So regarding Python or TS:
Some tacked on unsound pseudo type-system doesnāt change anything. These languages are unsafe and unsound at their core. Imho the result is even bigger trash as not having a static type system in the first place, as in such case you at least not lulled into believing that the ātypesā could safe your ass.
So both examples are not the yardstick for anything here.
Where?
Maybe in the mindset of the 70ās of last centuryā¦
Repeating all the C nonsense over and over, even 60 years later, is just bananas imho.
The new āstandardā are safe conversions with āasā. Itās the year 2024.
Itās actually a kind of joke that of all things I need to point to MS and something out of Apple, two companies that arenāt know for their high technical standards, more the contrary, but both do "the right thing"ā¢ nowadays.
E.g. C#:
using System.Collections.Generic;
public class Program
{
public static void Main()
{
int[] ints = [10, 20, 30];
IEnumerable<int> numbers = ints;
var TheAnser = (int)"42";
var CastedNumbers = (IEnumerable<string>)ints;
}
}
Compilation error (line 10, col 18): Cannot convert type 'string' to 'int'
Compilation error (line 11, col 23): Cannot convert type 'int[]' to 'System.Collections.Generic.IEnumerable<string>'
And that arenāt even the āsafe conversionsā! These are castsā¦
So it shouldnāt compile? (Like in C#?)
Because not throwing an error here at runtime is not an option. This needs to fail.
The only alternative were nasal daemonsā¦ Nobody wants that.
Thatās nothing about Scala. Thatās runtime behavior of the VM. And actually the JVM does here the right thing (as the VM is the only mostly sane part of Javaā¦). Just doing nothing and than exploding with completely random behavior at the other end of the world, like JS does, is imho much worse. Thatās exactly one of the main reasons why JS is dreaded by so many people: If you donāt work with the highest caution things in JS just blow up at ārandomā places.
The issue here is that asInstanceOf is the āunsafeā āhey compiler, look the other wayā operator. Itās not as. Itās the ābitcastā.
And such an operation is needed.
Only that it shouldnāt be completely stupid imho. It should bail out on obviously bad cases. (See C#). But it still needs to work for the cases where the compiler canāt determine whether something is āright or wrongā (thatās where the C# thingy gets annoying because it doesnāt do that, itās too strict, so you need to do nonsense like ādouble castingā going through object, if you really know better but the compiler canāt understand that). Otherwise we wouldnāt have an āescape hatchā for the cases where our type system would reject valid and useful programs. (Every static type system that guaranties that it never accepts invalid, unsound programs will experience cases where it rejects valid programs. Thatās a hard rule, afaik there is a formal prove for it, so āescape hatchesā are strictly needed. But they shouldnāt be maximally āblindā, and there should be some āsafe variantsā also, imo).
Iām not sure you understood me:
(The code = 1.asInstanceOf[String])
The code currently compiles
I believe it should keep compiling (i.e. not emit an error)
I believe it might be dangerous for it to emit a warning
As this might make it seem safer than it is
āThe compiler warned me last time, surely it will warn me again if I mess upā
This might be the case even if the warning is explicit about not being reliable
I believe it might be better if it actually did not return a class cast exception, and instead randomly failed somewhere random
I know this is probably impossible
Users might get burned harder, but this would give the operator a very real sense of danger, and that there is no safeguard
And overall, I feel the most constructive given the current situation is to make the documentation around asInstanceOf better
Only then, if it is still a problem, should we think of renaming it
But that would be the same kind of warning you get with pattern matching (or ==).
It does not tell you that something is safe, but it tells you in case it would fail for sure.
Or would you also remove the other warnings and errors? Should "23" == 23 compile?
Burn down your house to teach the children not to play with matches?
Iām not sure this is a good ideaā¦
Thanks God at least on the JVM you will get a helpful, fast failing exception when itās messed up obviously. Thatās build in into the VM so Scala canāt change that.
I fully agree.
Itās not like asInstanceOf would be a big problem in Scala. You usually donāt use it. (So you usually also donāt see it much in other peopleās code). So itās not the biggest issue. There are more important things. Better docu is always good, but else? I would like to have warnings for obviously bad cases, if itās not to difficult. But I donāt think this has high prio on the wish list.