Open lrhn opened 4 years ago
If we go for "everything like in function definition", probably more like everything in a function type definition, then we have optional parameters (both optional positional and optional named, but not at the same time).
I'm not sure it's the right design for tuple types.
If you do (int, int, [int]) p = tuple;
, I'd expect you to only be able to use the first two entries, but for functions, you get the third entry as well, with a default value. Or alternatively, the assignment is not valid because the third entry needs a default value, so it'd be (int, int, [int = 0]) p = tuple;
.
It's not a slam-dunk, but I guess it can work.
That's probably too ambitious (would make more sense for a language where functions were always unary and tuples were built in from the start, like ML).
Yeah, I think we will eventually hit a wall if we try to push too far in this direction. Dart isn't ML and Dart-y tuples will probably not be 100% like ML tuples.
I look at records in Dart more pragmatically: it's an immutable collection that the type system can see into. Nothing more, nothing less. Spreading a record into an argument list is a natural extension of that, but I don't think we need to reach 100% parity between tuple types and parameter lists for that to work.
If we don't need argument-list parity, then I think we can easily go in two extreme directions:
I think either approach can work.
The "named only" approach might feel overkill if something is really just an unstructured pair, but ... it never is. If a pair occurs as an inherent part of a class or method API (not just a type argument), then each position has a meaning. A MapEntry
is not just a pair, it's a key/value pair. A point is not just a pair, it's x/y coordinates. There is always an underlying meaning, and therefore a name, it's just that the name sometimes so obvious that we want to be able to not have to write it. That isn't necessarily a good choice, though (favor explicit over implicit!).
The "positional only" approach is a move towards a physical representation of the data. Semantics must be supplied from the outside. If you want to assign names to the parts, it's your job, not part of the tuple. (Maybe we can have "static names", so a tuple can be defined as (int x, int y) point = ...; print(point.x + point.y);
, where the x
is part of the point
declaration, not the underlying run-time tuple, which is just (int, int)
.)
Mixing the two muddies the waters. Which one is "the right one" depends on which problem tuples/records are intended to solve.
As I am biased to ML I would love to see a complete equivalence. I know that there are some limitations to it in the context of Dart, tho.
The current proposal for tuples allow named entries and positional entries, and can in many ways be seen as an abstraction over argument lists. It's not complete, because it doesn't cover the empty tuple, but it does have a singleton tuple.
If we allow tuples to work like argument lists, then every function can now be seen as an unary function from a tuple type to its result. (We might not allow
f tupleVariable
to call the function, but that's mainly for syntactic reasons, not because it doesn't make sense, and we can probably allowf(...tupleVariable)
).If argument lists are tuples, then tuple types should be parameter lists. The syntax is already close, a tuple type can be used as the parameter types of a
Function
type, e.g.,(int, int, {int x})
can be used asvoid Function(int, int, {int x})
. It's not a complete match though:void Function(int, int, {required int x})
.Object
members.Still, if we ignore optional parameters for now, we can see tuple types as the shape of a parameter list. We could then allow spreading a tuple type into a parameter list:
That could allow some abstraction over the "width" of a tuple. It would probably be too complicated, but it could (highly speculatively) allow for something like
That's probably too ambitious (would make more sense for a language where functions were always unary and tuples were built in from the start, like ML).