This is something I realised had been itching at me for a while that a conversation in another thread has prompted me to ask;
Usability is fine if [1, 2, 3, 4] is an IArray. The whole point is that you’re not supposed to care. The main downside is that it doesn’t print nicely.
Hypothesis: Array & IArray are underused because their “default” toString() tells you about their object reference rather than contents? This steers early exploration toward other collection types.
I get why naively printing a large array isn’t a good idea, but it seems to me that an intervention which does some sanity checks (e.g. print contents if <100 elements) would actually be quite a big useability win.
Is there a reason (like - it can’t be overloaded on the JVM) that this isn’t already done? I seem have very naive printArr extension methods lying around quite regularly. Is that just me :-)?
It is not just toString, but also equals and hashCode. Especially problematic if you use IArray as a field in case class. Lots of hidden bugs could be lurking, especially when doing migrations. I do not understand where exactly is the problem, why do we have to inherit these Java quirks. We are in complete control or JVM code generation, we could be inserting any operation we want at any point in code.
I have migrated my codebase to IArray, and the solution is definitely not mature enough. Many things that type checked with Vector, do not with IArray (see my post history). Moreover, the Scala.JS bundle increased by 30 percent due to instanceof chains to check the type of the array.
I am convinced the right solution is ArraySeq when Valhalla is delivered, and if we import it into predef as default. I generally appreciate the sentiment, as I have discovered that I rarely need anything else than a flat array. I only ever transform the data with map, flatMap and filter, and if I ever append, the size is less than 32 elements.
you can only do this if you have a typclass based solution and avoid toString, unless we have a solution that uses a global runtime registry and checks the class to see if its an array (and recompile all toString calls of a generic type as this intercepted version) - but even then you can only tell its an Array, and not IArray (maybe thats ok of course - we acknowlege its an alias).
once you cross the boundary of generic type parameter then the only way to tell is runtime typetest
If you have a default fallback to toString people would just forget adding the typeclass all the time in generic functions, and then be confused by the results.
Arrays are JVM build-in types. You cannot modify them: they are kind-of final and have some special treatment. “Kind of” because there is no Array definition in stdlib, they are defined by JVM itself - https://blogs.oracle.com/javamagazine/java-array-objects/ .
As such, you cannot:
create a custom subtype
override any of its methods
including: toString, equals and hashCode
You could create some sort of wrapper by Scala compiler and/or make the compiler special case it, so that if it sees Array it uses something different than what it uses for other values - but all it takes is to cast it to Any and compiler no longer knows that it would have to special case it.
So it can be reliably handled only in runtime. And you cannot reliably inject such a special runtime handling to:
existing Scala libraries
non-Scala libraries
JVM std lib itself
It would also be quite impractical if you actually did, since you’d have to inject it everywhere, by adding some if-elses based on runtime instance checks for everything that is not proovable to not be an array.
well there is the option of runtime java agent but i guess thats quite “spooky”, and a performance hit (Scala.js and Scala Native have whole program analysis so its less difficult to replace toString calls)
Bah, we just have to abandon the JVM. Then we can correctly redefine toString, equals and hashCode on arrays in Scala Native, Scala.js and Scala.js-on-Wasm. Problem solved.
You can box it, just like you do primitives. We already have a box: ArraySeq.
The question is just whether it’s worth having to think about boxing there too. So far the answer has been “no, handle it yourself”. Which is fair enough–it’s annoying to have to consider boxing with primitives, and having to consider it with arrays would just make it worse. But on the other hand, it’s very easy to accidentally slip an array into some generic context and then things don’t work. It’s already a wart.
Unfortunately, for it to be clean, it would be much better to remove it from the regular type hierarchy. And that would probably break all kinds of things. So we’re probably stuck with it as it is.
However, conceptually, it would make more sense boxed when part of the collections hierarchy. (The semantics of extends AnyVal would probably be fine.)
So maybe IArray is actually out for literals. But it really does leave us in the case where Python is faster, because although creation is slow, once things are in numpy at least the vector operations are on primitives and fast.
About half of what one wants to do for performance comes with this caveat!
I hope it is actually close. But it might be quite some time before we can rely upon it (decades) as our standard solution. Enabling libraries to use it can come much sooner.
If we try to change the language less and make empowering libraries more, this also helps us take advantage of language features sooner. (It does not help JS/native compatibility, however.)