Make "tuple syntax" a shorthand for initializing a contextual type

I don’t think a pitch thread is the right place for reviews of the proposal.

It’s fine to mention objections and hurdles that exist, but by and large the pitch thread should be focused on developing the pitch. That does include counter-proposals and consideration of alternatives, but it should not devolve into arguments about whether the proposal ought to be accepted.

That’s what review threads are for.

3 Likes

I assume the core team takes community sentiment into consideration when determining whether or not to move a pitch into review, but I do see your point. It can be difficult to draw a line between comments that prompt additional ideas and ones that simply disapprove, though.

The difference is in Control flow visibility-something that's generally been stressed as important for the language.

In many circumstances init is very basic, but in other cases it could be significantly transforming or changing things where things like threading and memory ownership are important. Making it look similar to a Tuple at the call site sight loses this indication of the control flow.

FWIW, stating a general rule without addressing the specifics of what you see as a problem is kind of vague. It's unclear who or what that's addressed to.

Isn't one alternative to not do something? The whole point of a pitch thread is to bring something from idea phase to review phase. If something is unclear (e.g. involving "Tuples") or deemed as a problem (e.g. sacrificing clarity) shouldn't it be brought up and addressed as soon as possible?

I think that part of a pitch is also to gauge community reaction as to whether a feature is worth pursuing. I would say that it's reasonable for people to raise their constructive concerns about whether the feature in question is the right thing to do at all.

2 Likes

I've been wanting something like this for a while. The OP has some good examples of the types of situations where it would make a significant difference in readability, and I think making it a shorthand for .init rather than involving tuples is a smart, simple solution.

3 Likes

Sure, we could add typealiases like V2F = SIMD2<Float> , V4F = SIMD4<Float> , RF = Rect<Float> , ... and/or we could add tuple-arg-variants of each and every frequently used initializer and method (which in my case would be something like 100+ tuple-arg-variants). There are all sorts of ways to piece together alternative and to my mind more cumbersome solutions.
: )

I don't understand what is cumbersome about defining a typealias when you need it... IMO this is one of the main use cases for typealiases! When I have a type that is too long and I need to spell out repeatedly because it is being used in lots of places, I define a convenient shorthand to refer to it using typealias. This preserves the ability to reuse the methods and properties for the type without having to thread them through individually (like you would have to do with a newtype wrapper).

1 Like

I'm simply saying that I'd prefer the pitched solution, ie that the following would just work (without me having to write anything extra at all):

foo(bounds: (origin: (1, 2), size: (3, 4)), barPoint: (5, 6))
(Since the compiler would see that as equivalent to this.)
foo(bounds: .init(origin: .init(1, 2), size: .init(3, 4)), barPoint: .init(5, 6))

IMHO the above would be much nicer than eg defining short but still not short enough typealiases:

typealias RF = Rect<Float>
typealias V2F = SIMD2<Float>

to be able to write:

foo(bounds: RF(origin: V2F(1, 2), size: V2F(3, 4)), barPoint: V2F(5, 6))

Such RF, V2F, .init and whatever are just obscuring the stuff we actually care about in code like that, and it should be up to the programmer and code reviewer to decide when to leave the types out or not, just like they are already doing when they use .init(…) instead of eg Rect<Float>(…).

2 Likes

What happens when there are more than 7 arguments in an initializer? Swift currently supports up to 7 elements in a tuple. Or, maybe it doesn't matter because the shorthand isn't actually a tuple?

I don't think that there is a considerable upper limit on the allowed number of tuple elements. Maybe you're considering the implementation of == on tuples?

2 Likes


Exactly, at least that's the way I think about it (not knowing anything about how it would be implemented though).

1 Like

Yep my bad. I misremembered and thought tuples had a limit on element count.

This seems great for all kinds of declarative code. I would use it all the time.

3 Likes

I would actually prefer being able to explicitly mark a type as supporting this (initializers, more than the type, really) but if this is the best version that we can actually get people to agree to… I'm for it.

3 Likes

One potential concern that it might be good to think through is what happens if somebody uses the proposed shorthand syntax in a place where they thought a contextual type would be in effect, but they turned out to be wrong.

If you write .init(...) and there turns to be no contextual type, a diagnostic can be tailored to your mistake.

If you write (...) and there is no contextual type, the compiler would seemingly have to assume you meant to form a tuple (for backwards compatibility). If things subsequently don’t type-check, will diagnostics be able to cover both the “you meant a tuple, but one isn’t expected here” and the “you meant a contextual .init, but there is no contextual type” cases?

To be clear, I’m not saying this argues against the feature, but I’d want a full proposal to address the “what if people make mistakes” issue.

5 Likes

I think the initializer shorthand path gets us most of the way to where we want to be, but I think there needs to be some additional constraints so that it doesn't entirely blur the line between an unlabeled tuple and an initializer.

  1. Keep the leading .
  2. Labels, if present, must match.

So CGSize(width:height:) would become .(width:height).

To my mind this keeps most of the readability of the original initializer while still sugaring the call site. I'm not sure it's tenable to make a sugared initializer syntax drop the parameter labels. Once you go that far, I think you're much closer to ExpressibleByTupleLiteral than an initializer.

3 Likes

Since it's simply .init(…) becoming (…) by skipping the .init-part, I don't think anybody has suggested dropping any parameter labels, and I wouldn't want that to be possible.

Let me also say that I welcome anyone who feel like writing a formal proposal and/or implementation to do so! (I won't be doing either. I just started this pitch by citing previous discussions, because I like the idea.)

1 Like

Sorry, didn't realize everyone used so many unlabeled initializers.

I'm not sure if we misunderstand each other. I think that the labels should stay where they were, if there were any. Here's this example again (which is something we can write with current Swift):

foo(bounds: .init(origin: .init(1, 2), size: .init(3, 4)), barPoint: .init(5, 6))

this would in the proposed shorthand form (after simply removing ".init") be OK to write like this:

foo(bounds: (origin: (1, 2), size: (3, 4)), barPoint: (5, 6))

(There are labels there, origin and size.)

Had it been:

foo(bounds: .init(origin: .init(x: 1, y: 2), size: .init(width: 3, height: 4)), barPoint: .init(x: 5, y: 6))

then the shorthand form would of course be:

foo(bounds: (origin: (x: 1, y: 2), size: (width: 3, height: 4)), barPoint: (x: 5, y: 6))

I understand. I think we agree on everything but the leading ..

1 Like
Terms of Service

Privacy Policy

Cookie Policy