Pre-SIP: A Syntax for Collection Literals

it sounds like people mostly pick Seq as the default collection type because it’s the shortest to type and read, not because it’s the most performant, and then everyone has to write Seq everywhere for no good reason. this is what we’re talking about replacing: taking away the need for this kind of boilerplate when it just doesn’t really matter

in your matrix case, a target type is given (as you have greater mathematical needs) and so the compiler could avoid creating a tuple entirely

so i’m not saying performance is wholly dispensable. just that in context of the original problems it’s generally pretty irrelevant, and if does become relevant then with tuple syntax the solution is dead simple: pick the type that gives the best performance and write out its name. no further change required

with different brackets, you’d have to also change the brackets. with a prefix or suffix you’d have to delete something first. not onerous for sure but slightly more annoying

also, is creation and conversion of tuples (in the subset of cases where this becomes relevant) really that non-performant compared to Seq anyway? do we have any real numbers? maybe you’d sway me if it was glaringly atrocious :laughing:

it wouldn’t be anything new that if you rely on type inference, context matters.

fair. it wouldn’t be the end of the world or kill the whole idea so let’s change ‘need to support’ to ‘should / would be nice to support’. i think your example is confusing to a beginner as well and perhaps should be resolved somehow but on that i have no opinions right now :grin:

I think we would end up converging on the realization that without inference, the actual correct default type is IArray because it stores primitives as primitives.

Once you can start doing [1, 2, 3, 4] and you get beaten by numpy and the lore is, “Use Python, not Scala; Python is faster”, I think we’d be motivated to change.

We could just accept that Scala has no place in machine learning or data analysis, but I don’t think that’s the best strategy. 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.

And then with inference, [...] would be whatever it needs to be.

But, anyway, I think the parens-based syntax is better; but in that case obviously the default has to be tuple because it already is.

Actually, the compiler could help a lot to unify tuple and collection literals, for example:

val list: List[Int] = [123, 456, 789] // infers to `List[Int]`

val vec: Vector[Double] = [12.3, 45.6, 78.9] // infers to `Vector[Double]`

val tuple = [123, "456", 78.9] // infers to `Tuple[Int, String, Double]` by default

The last example makes sense, because the compiler has no idea what exactly the user is trying to reduce the source type to, and therefore captures everything into the tuple literally.

The help from the compiler might be necessary to avoid the intermediate tuple-to-collection conversion – in order to infer the target type directly from the literals. And that would be a really big deal.

That way, these bracket-based literals could combine best of both worlds and ultimately unify collections and tuples. I think it would be a great shift from Scala2 mindset where collections and tuples were basically different things, to Scala3+ mindset where tuples are just a generalization of regular collections (which they really are).

If we could make it working, then the original parentheses based syntax for tuple literals could be deprecated and over time retired – for a real good.

2 Likes

the minor wrinkle is that Tuple.map is a polyfunction, not normal function, so [1,2,3].map([T] => (t: T) => ???) is not “portable syntax”.

Potentially (with pain) we can migrate tuples map to mapPoly and re-introduce map as def map[B](Tuple[Union[this.type]] => B): Tuple.Map[this.type, [_] =>> B]

1 Like

But this is mostly an issue because we’re missing polymorphic eta-expansion, with it, it would be solved, no ?

Well it depends on what we go with but the more recent (to my knowledge, which dates back) proposal by @smarter would allow x => x.toString to eta-expand to something with type [T] => T => String

We might need to update map so its [T] is instead [T <: <don't remember the syntax, the union of all tuple element types>]
For things like (1, 2, 3).map(_ + 1)

2 Likes

well not really because (1,2,3).map[[X] =>> Int]([T <: Int] => (t: T) => 100 + t) doesnt even type check (edit: i didnt read the whole comment)

if this is compatible change then i think so :slight_smile: thanks for suggesting