Given that named tuples types are declared using type aliases, does it make sense to allow recursive types, currently this is invalid code
type Person = (name: String, age: Int, friend: Option[Person])
Given that named tuples types are declared using type aliases, does it make sense to allow recursive types, currently this is invalid code
type Person = (name: String, age: Int, friend: Option[Person])
May also be related, the possibility to directly serialize and deserialize to JSON Objects, which may not be possible if names are totally erased
this one is possible as long as the type is known at the call-site (you can use constValueTuple
method to reflect the names to a value) e.g.
inline def pairs[N <: Tuple, V <: Tuple](nt: NamedTuple[N, V]): Unit =
val names = constValueTuple[N]
names.zip(nt.toTuple).toList.foreach(println)
scala> pairs((x = 3, y = 7))
(x,3)
(y,7)
There are some discussions over here Pre-SIP: a syntax for aggregate literals - #107 by lihaoyi about the usefulness of partially named tuples in relation to scrapping boilerplate of constructors etc. Would the current Named tuples scheme be possible to extend to partially named tuples and be unified with (some of) the ideas in that thread?
I believe it would be a considerable can of worms but thatâs more a gut feeling than backed by data. Iâd not try to extend the named tuples proposal with this at this time. It could be added later if needed.
OK, yes can be considered later, if not to hairy. Thanks.
I really like the fact that my âaggregate literalâ proposal does not add any new first class concepts to the language (which might interact with other language features in all sorts of ways) but only allows us to write the code that weâre already writing today with less boilerplate. So color me quite sceptical about this idea.
the purpose of it is computing structural types, and to give better meaning to temporary return values - neither of which are addressed by that proposal
Iâve read through the current version of the feature. I very much welcome it!
Itâs simple at the core, it prioritizes imho the right things (Tuple :< NamedTuple, with a conversion the other way around), it doesnât make named tuples some weird kind of hashmaps / objects (order matters!) in the first place (this is imho important as tuples are very basic building blocks for data, much closer to âstructsâ than to âobjectsâ; âanonymous objectsâ should be a higher level feature imho), and theyâre even super lightweight when it comes to runtime cost. I really like the design. It will imho also make a great base for other features on top in the future! (Maybe we get better usable CStructs in Scala Native, for example?)
But there is one single thing that cough my attention. I think itâs actually a hot candidate for a all time high-score on Scala Puzzlers. Itâs the renaming in pattern matching. That behaves just completely unexpected. I had to read this part a few times until I believed that I got it. Very unintuitive.
In this regard I fully support @tarsaâs older remarks to which I answer.
I would really hope the syntax for this one tiny peace of functionality gets changed.
I would really like to the see the proposed as
syntax instead.
We established already in some other threads that as
has well formed âmeta meaningâ. It stands for a ârename that introduces a fresh symbolâ. That works for import renaming, that works for naming given instances, that works in the other pattern matching proposal. It would also work for this case here imho.
I understand that this would create some âirregularityâ in a (quite seldom used form of) pattern matching, but it would save the basic meaning of =
, which is clearly assignment.
So please consider this small change request.
Everything else looks great, but this tiny piece looks just odd. It really puzzled me.
It behaves exactly like it should: patterns look the same as expressions, except that in a pattern you can replace a subexpression with an identifier in order to bind said identifier. It would be unfortunate to deviate from this principle.
Actually =
is overloaded in Scala and has more than one meaning. One use of =
is in val
declarations to separate the pattern from the initializer. The named tuple syntax is another, separate thing that just happens to use the same symbol, and this can be clearly seen from languages like JavaScript that use different symbols for the two: =
for pattern binding, i. e. const [a, b] = [1, 2]
, and :
for object literals, e. g. {a: 1, b: 2}
.
Iâm not a fan of either named parameter or named tuple, because they are not hygienic, sometime not hygienic is confusing. For example:
def NamedParameterFunction(a: Int, b: Int) = { ... }
val a = 1, b = 2
NamedParameterFunction(b, a)
NamedParameterFunction(a, b)
NamedParameterFunction(a = b, b = a)
Whatâs the behavior, may be it is too clear for scala expert, but it do increase the learning curve.
Named tuple may intruduce more confusion, for example:
type Something = (a: Int, b: Int)
val something: Something
something match {
case (b, a) => // what's the behavior ? it is too confusing.
// In a hygienic world, both a and b are new identifier here,
// which should be totally different from Something.a or
// Something.b, no confusion at all.
}
A member name of a class should only be valid inside the class, when refering the name outside the class, class name or class object should be used instead of that name solely. So case class is always a better solution.
I saw that Named Tuples will be part of the 3.6 release,and Iâm somewhat surprised as it seems to me that the subtyping direction was not at all in consensus
(If anything it seemed like a majority of people on this thread were in favor of the opposite of what is in the SIP)
Could someone describe how the SIP commity thought through these differences, and why this decision was taken ?
The SIP has a specific section where Martin explains the reasoning.
And what about breaking the meaning of âassignmentâ (=
)?
This issue was also never further discussed.
Introducing new symbols in scope should really use as
, like elsewhere already.
But OK, my issue is âjust syntaxâ. It can be changed after the fact with low effort through automatic rewrite, should it turn out that itâs not only me who thinks that the current syntax created a mayor puzzler.
Thatâs simply not true. Destructuring assignment is still assignment! There is no âoverload of meaningâ.
So we have only this super weird and maximally unintuitive special case in named tuplesâŚ
as
is just an abbreviation for assignment
. Unfortunate that they didnât use ass
.
But speaking of uniformity of assignment syntax for named args, today I wanted:
Welcome to Scala 2.13.15 (OpenJDK 64-Bit Server VM, Java 23).
Type in expressions for evaluation. Or try :help.
scala> def loop(i: Int): Int = if (i < 100) loop(i=i+1) else 42
def loop(i: Int): Int
scala> def loop(i: Int): Int = if (i < 100) loop(i+=1) else 42
^
error: value += is not a member of Int
Expression does not convert to assignment because receiver is not assignable.
I know I asked for it once, some time ago. Probably I didnât get it because I was on the ânaughtyâ list that year.
Because there isnât an issue to begin with.
Itâs not a special case because it works exactly the same as every other pattern in the language: take a pattern, replace the binding variables with expressions, and youâll get an expression that matches the original pattern. E. g. take the pattern (name = x, age = y)
, substitute "Mateusz"
for x
and 5
for y
in the pattern and you get the expression (name = "Mateusz", age = 5)
.
It would be unfortunate to deviate from this scheme, and in fact, languages like JavaScript or Haskell which already support this functionality work exactly the same way as Scala does today.
Iâm not sure how this is even related.
What you propose in your example is to let assignment evaluate to the assigned value instead of Unit
. This was indeed proposed more than once, but it has nothing to do with âuniformity of assignmentâ.
As long as assignment evaluated to Unit
(and also function arguments are final) the current behavior is the only that makes sense. To make your code snipped work a lot of things would need to change in Scala. Because expanding the code under current rules gives:
val $: Int = i + 1
loop(i = $)
and respectively:
val $: Unit = i+=1
loop($)
What should be âuniformâ here?
(post deleted by author)
I agree. The subtyping direction could have done with more discussion, but itâs too late now. The direction that was taken seems like itâs out of convenience for a specific use case (i.e. val Laura: Person = ("Laura", 25)
) but not a lot of thought was given to whether the semantics make sense.
Now, an ordinary (non-named) tuple is a subtype of every named tuple with equivalent types. This is a really weird situation.
In general, subtyping can be modeled by intersection types â i.e. a subtype can be expressed in a type system that lacks subtypes (but has intersection types) by making it the intersection of the type and all its supertypes. Named tuples are now the exception to that â itâs not possible to express types in that fashion unless tuples are excluded from consideration (along with Nothing
).
Just saying, if thereâs such a thing as a âcode smellâ for type systems, it strikes me as one. With all the effort thatâs been put into DOT in order to formalize the type system⌠does this mechanism consistently fit in a formal way?
Edit: In other words, Unnamed <:< Named
requires tuples to be âspecialâ in a way that Named <:< Unnamed
doesnât. What we get in return is just fewer keystrokes in some cases (where itâs arguable whether thatâs even a good thing). Anyway, whatâs done is done.
Itâs still experimental, though.
One could use the hand break, and keep it experimental for the time beingâŚ
Somehow the whole question boils down to whether itâs more annoying to sometimes need to write .toTuple
, or respectively .widen
if the sub-type direction was changed.
The rest is kind of theoretic hair splitting.
I agree that a tuple with names seems more specific than a tuple that has no field names. But our ârawâ tuples have actually (generic, synthetic) field names⌠At that point the discussion starts to be quite murky.
Imho a more interesting question is whether the runtime representation needs to change too if the sub-typing goes in the opposite direction?
My personal mental model of a tuple is that one of a âstructâ, and structs donât have field names at runtime (just offsets). Structs are a compact, not-associative data structure. I think tuples could be used for that in Scala Native, or Scala WASM. But than runtime canât rely on the field names. So itâs important that the ârawâ tuples remain the actual runtime representation.