Pre-SIP: deprecate asInstanceOf, introduce unsafeAsInstanceOf

I never argued the contrary. I merely pointed out the fact that, in Swift, as is safe and that in Scala asInstanceOf isn’t, under my definition of safety. That doesn’t mean an unsafe cast/conversion operation isn’t necessary.

Reading the rest of your post I think we agree on everything here.

AFAICT that is the heart of your argument and I think it doesn’t hold water. It would be counterproductive to ignore all errors that got caught only because similar errors may slip through in other situations.

I want my compiler to catch as many errors as it can. I certainly don’t want it to accept programs that it knows are incorrect in the off chance that I might grow a false sense of security.

The whole point about avoiding nasal demons is that they create seemingly random errors at random places. These are sometimes near impossible to debug. I’ve seen entire libraries be abandoned or rewritten from scratch because of UB.

I would hate with every fiber of my being any system that intentionally introduces them. Knowing that it did so to teach me a lesson would add insult to injury.

4 Likes

Fair enough ^^’

I think given the above, I’m enclined to think the same for warnings, but not for errors
If the compiler throws an error, for me it signals a degree of certainty, not only that this case really is incorrect, but also that it will catch other incorrect cases.

For example “unreachable case” is a warning, and we do not guarantee to catch every unreachable case

For me at least “I won’t let you screw up, ever” and “I caught you this time” are very different messages, and should be clearly distinguished.
The first allows me to relax around that feature, the second only to feel relieved I didn’t have to debug it this time

2 Likes

I wrote a macro for Scala 3 which warns and checks in more cases. Feel free to use it as well: GitHub - tegonal/scala-commons: A library containing utility and helper functions for scala

trait Foo {
  type A

  def foo[T] = {
    val a: Any = 1
    a.as[Double] // runtime check instead of implicit conversion
    a.as[String] // runtime check 
    a.as[List[String]] // warning due to type erasure next to runtime check
    a.as[A] // warning due to type erasure next to runtime check 
    a.as[T] // warning due to type erasure next to runtime check
    a.as[Int & String] // runtime check which checks isInstanceOf[Int] && isInstanceOf[String] fails (asInstanceOf is happy as first type is correct)
    a.as[Float | String] // runtime check which fails (asInstanceOf checks lub of Float | String of which Int is a subtype as well, i.e. does not fail)

    val b: Any = null
    b.as[Double] // runtime check which fails instead of default value 0.0
  }
}