Spread Operator as Shorthand for Map

I believe the dot abbreviation only works for enum cases right now.

Nope :)

I suppose those can be viewed as static members in some sense but they really something different than that. If I am mistaken I would like to be corrected.

I'll do my best!

It works as I was trying to describe earlier. Any static member that returns Self. Try the following in a playground/REPL:

    struct Foo {
        static var bar: Foo {
            return Foo()
        }
        static func baz() -> Foo {
            return Foo()
        }
    }
    let bar: Foo = .bar
    let baz: Foo = .baz()

Here's a SwiftStub demonstrating the behavior: http://swiftstub.com/579553153

Enum cases work with dot abbreviation _because_ they are static members that return Self. Enumerations aren't special-cased for dot abbreviation.

I stand corrected. This is pretty interesting. Is this covered in any documentation? I haven’t seen anything about it nor any code that uses this until now. It would be useful with factory methods. I appreciate your calling this to my attention!

The shadowing is *current* behavior in the language. It is not something I propose.

Is this true? Can you provide a full example that works in the playground and demonstrates this? The hypothetical you paste already has certain ambiguous issues (e.g. `var foo` and `func foo()` cannot compile together because of redefinition).

Here is an example. I tested it in an app rather than a playground (moving the print line into main).

struct Foo {
    //static var bar: Foo -> String = { _ in return "var" }
    static func bar(f: Foo) -> String { return "func" }
}
// prints “func”, but will compile and print “bar” if you uncomment the var
print(Foo.bar(Foo()))

This would not refer to either. It cannot refer to Foo.bar because Foo has nothing to do with the array you are mapping over.

The example I give works in the playground. Try it! :)

You are right here as well. I didn’t read closely enough and didn’t notice the overload of map you added to Array.

If you remove that overload your code will fail to compile. This is because the dot abbreviation is context sensitive. It only works when the type system knows the type the expression is expected to produce.

Because of this context sensitivity no ambiguity would arise from the example you provided. As you note, the type of the static property or method must always be Self, which is never going to be a function type. This means that it is never going to have the same type as an unbound instance method (that its non-self arguments bound).

Because of this the two shorthand notations will never be applicable in the same type context. I can see how the similarity could be confusing but it is not ambiguous. The potential confusion might even be enough to avoid introducing it into the language, but I’m not sure about that.

I still think it’s an interesting idea.

Matthew

···

On Dec 16, 2015, at 1:32 PM, Stephen Celis <stephen.celis@gmail.com> wrote:
On Wed, Dec 16, 2015 at 2:18 PM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

+1 for being able to use cars.map(make)

···

Am 16.12.2015 um 16:23 schrieb Al Skipp via swift-evolution <swift-evolution@swift.org>:

On 16 Dec 2015, at 12:47, Sean Kosanovich via swift-evolution <swift-evolution@swift.org> wrote:

As a Groovy user, I really enjoy the shorthand Spread Operator over the Collect Closure (Map is the equivalent in Swift).

Consider the following Swift code:

struct Car {
    let make: String
    let model: String
}

let cars = [Car(make: "Jeep", model: "Grand Cherokee"), Car(make: "Dodge", model: "Challenger")]
let makes = cars.map() { $0.make }

Now consider the same code using a Spread Operator:

let makes = cars*.make

I think there is a tension in Swift between its object oriented nature and higher-order functions which leads to a lot of repetition of { $0.property }

In this example:
cars.map { $0.make }

I’d like to write the following, but ‘make’ is a member, not a free function:
cars.map(make)

Is anyone else irked by the preponderance of { $0.property } in Swift (or is it just me being grumpy)? Not sure what the solution would be, but I can’t say I’m keen on the suggestion of:

cars*.make

It’s too mysterious.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I see, this consists of a map operator as well as shorthand for referring to members of the type provided to the map function (similar to shorthand for referring to enum cases). The latter does seem like it could be pretty useful if it doesn’t introduce any ambiguities.

A standard map operator might also be useful but I would expect to see it as part of a suite of functional operators if the standard ever adds one (I hope it does).

···

On Dec 16, 2015, at 9:18 AM, Sean Kosanovich <sean7512@me.com> wrote:

Yes, I am proposing this be part of the standard library.

Additional use cases that are possible in Groovy are using it for setting variables or calling methods, like the following (ignore the example of changing a car’s make doesn’t make much since):

cars*.make = “Chevrolet”

or

cars*.notifyOfLowFuel()

On Dec 16, 2015, at 10:14 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote:

You can already define an infix operator that does this. Are you proposing one be in the standard library? I would be opposed to adding new syntax for this.

On Dec 16, 2015, at 6:47 AM, Sean Kosanovich via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As a Groovy user, I really enjoy the shorthand Spread Operator over the Collect Closure (Map is the equivalent in Swift).

Consider the following Swift code:

struct Car {
    let make: String
    let model: String
}

let cars = [Car(make: "Jeep", model: "Grand Cherokee"), Car(make: "Dodge", model: "Challenger")]
let makes = cars.map() { $0.make }

Now consider the same code using a Spread Operator:

let makes = cars*.make

The other distinction in Groovy is that the Spread Operator is null safe, meaning it won’t throw a NPE if an element is null, whereas the Collect Closure would. So in Swift, I would propose the Spread Operator implicitly does this when operating on an array of Optionals:

let makes = cars.map() { $0?.make }

Thanks!
Sean

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Ignore the above. Thought make was a method for some reason. Still, it
would be possible if the parallel proposal for adding method references for
getters were approved.

···

On Wed, Dec 16, 2015, 11:51 AM Dennis Lysenko <dennis.s.lysenko@gmail.com> wrote:

Perhaps cars.map(Car.make)? We already have method references, why define
a new syntax?

On Wed, Dec 16, 2015, 11:18 AM Stephen Celis via swift-evolution < > swift-evolution@swift.org> wrote:

On Wed, Dec 16, 2015 at 10:50 AM, Matthew Johnson via swift-evolution < >> swift-evolution@swift.org> wrote:

If we peel off just the contextual shorthand aspect of this proposal you

could write:

cars.map(.make)

That does seem like a big win if it is feasible (doesn’t introduce
ambiguity or cause other significant challenges in implementation).

The above creates ambiguity if there were a `map` function that takes a
type with a static member `make`.

Alternatively:

    cars.map{.make}

Both create an additional meaning for dot abbreviation, though, which is
confusing. I think I'm "-1" on this proposal as is. The existing form is
clearer and relatively short as is.

Stephen
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

That would actually refer to a static function on ‘Car’, not a method, or property on an instance of ‘Car’.

···

On 16 Dec 2015, at 16:51, Dennis Lysenko via swift-evolution <swift-evolution@swift.org> wrote:

Perhaps cars.map(Car.make)? We already have method references, why define a new syntax?

Worth noting this can be achieved in Ruby using a shorthand syntax that
turns a symbol into a method:

cars.map(&:make)

···

On Wed, Dec 16, 2015 at 9:32 AM Al Skipp via swift-evolution < swift-evolution@swift.org> wrote:

On 16 Dec 2015, at 17:20, Matthew Johnson via swift-evolution < > swift-evolution@swift.org> wrote:

Joe Groff noted syntactic ambiguity caused by omitting $0. Unless that
potential ambiguity could be resolved in some way this wouldn’t actually
work. It does work if you include $0:

cars.map{$0.make}

Hey, we’re back where we started! ; )

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I stand corrected. This is pretty interesting. Is this covered in any documentation? I haven’t seen anything about it nor any code that uses this until now. It would be useful with factory methods. I appreciate your calling this to my attention!

I think it's described, but obviously not clearly enough. Might be worth opening a bug requesting better documentation around the behavior.

The shadowing is *current* behavior in the language. It is not something I propose.

Is this true? Can you provide a full example that works in the playground and demonstrates this? The hypothetical you paste already has certain ambiguous issues (e.g. `var foo` and `func foo()` cannot compile together because of redefinition).

Here is an example. I tested it in an app rather than a playground (moving the print line into main).

struct Foo {
    //static var bar: Foo -> String = { _ in return "var" }
    static func bar(f: Foo) -> String { return "func" }
}
// prints “func”, but will compile and print “bar” if you uncomment the var
print(Foo.bar(Foo()))

Ah, yeah, the fact that there are arguments disambiguates (which is interesting in itself). If you make a variable and then a function without arguments, per your earlier example, it won’t compile:

    struct Foo {
        //static var bar: String = "var"
        static func bar() -> String { return "func" }
    }
    // prints "func" and won't compile if you uncomment the var
    print(Foo.bar(Foo()))

This would not refer to either. It cannot refer to Foo.bar because Foo has nothing to do with the array you are mapping over.

The example I give works in the playground. Try it! :)

You are right here as well. I didn’t read closely enough and didn’t notice the overload of map you added to Array.

If you remove that overload your code will fail to compile. This is because the dot abbreviation is context sensitive. It only works when the type system knows the type the expression is expected to produce.

Of course :) And if the overload stays and your suggested syntax were added, it would be ambiguous and also fail to compile.

Because of this context sensitivity no ambiguity would arise from the example you provided. As you note, the type of the static property or method must always be Self, which is never going to be a function type. This means that it is never going to have the same type as an unbound instance method (that its non-self arguments bound).

Because of this the two shorthand notations will never be applicable in the same type context. I can see how the similarity could be confusing but it is not ambiguous. The potential confusion might even be enough to avoid introducing it into the language, but I’m not sure about that.

This was my exact point earlier. It's unlikely to be ambiguous (because context _generally_ disambiguates), but in the examples I give, they certainly are ambiguous (and thus can be).

My general stance remains that, while an interesting idea, adding a second behavior around dot abbreviation causes more confusion than it's worth (especially given that the behavior is not well understood—this thread being an example), and that {$0.whatever} is not a big burden over (.whatever).

Stephen

···

On Dec 16, 2015, at 11:28 PM, Matthew Johnson <matthew@anandabits.com> wrote:

+1 for being able to use cars.map(make)

Check out the thread "[Proposal Idea] dot shorthand for instance members” which discusses an idea that gets as close to that as possible.

Something like this is also mentioned in the "Alternatives considered” section of Joe Groff’s “Removing currying func declaration syntax” proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0002-remove-currying.md

···

On Dec 28, 2015, at 11:10 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 16.12.2015 um 16:23 schrieb Al Skipp via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

On 16 Dec 2015, at 12:47, Sean Kosanovich via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

As a Groovy user, I really enjoy the shorthand Spread Operator over the Collect Closure (Map is the equivalent in Swift).

Consider the following Swift code:

struct Car {
    let make: String
    let model: String
}

let cars = [Car(make: "Jeep", model: "Grand Cherokee"), Car(make: "Dodge", model: "Challenger")]
let makes = cars.map() { $0.make }

Now consider the same code using a Spread Operator:

let makes = cars*.make

I think there is a tension in Swift between its object oriented nature and higher-order functions which leads to a lot of repetition of { $0.property }

In this example:
cars.map { $0.make }

I’d like to write the following, but ‘make’ is a member, not a free function:
cars.map(make)

Is anyone else irked by the preponderance of { $0.property } in Swift (or is it just me being grumpy)? Not sure what the solution would be, but I can’t say I’m keen on the suggestion of:

cars*.make

It’s too mysterious.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Seems sensible; here's my $0.02:

I don't know whether that kind of thing has been proposed, but it would be interesting to explore two different language features and their interaction with first-class getters and setters. This way, I believe, we could avoid introducing non-orthogonality and special cases into the language.

The first feature is uniform function call syntax:
- for structs and final classes (where there's no inheritance), if a function is declared with the struct type as its first parameter, e.g. some_property(self: MyStruct) {…}, it could be called as if it was a method on the struct (cf. Go's behavior). With this behavior would be consistent the ability to simply map getters and setters over a collection of structs (or otherwise treat technically free functions as methods if the types match).
- for non-final classes, inheritance kicks in. In this case, I think it's a little too magical (albeit certainly not hard to implement) to have one function name map to several functions (i.e. have inheritance semantics), especially without visual indication of the object being manipulated. There's a source of mental tension here, though: specifically, this behavior would however be consistent with the notion of overridden functions at the same time.

The second feature is macros - I know that they have been discussed and postponed for now, but theoretically they'd be capable of solving the kinds of syntactic repetition that can't otherwise be refactored into functions. The standard library could then contain macros which would expand to appropriate getters and setters - pseudocode follows:

    macro get(name: Identifier) {
        return { $0.`name` }
    }

    macro set(name: Identifier) {
        return { $0.`name` = $1 }
    }

What do you think about this?

···

Sent from my iPhone

On 28 Dec 2015, at 18:10, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

+1 for being able to use cars.map(make)

Am 16.12.2015 um 16:23 schrieb Al Skipp via swift-evolution <swift-evolution@swift.org>:

On 16 Dec 2015, at 12:47, Sean Kosanovich via swift-evolution <swift-evolution@swift.org> wrote:

As a Groovy user, I really enjoy the shorthand Spread Operator over the Collect Closure (Map is the equivalent in Swift).

Consider the following Swift code:

struct Car {
    let make: String
    let model: String
}

let cars = [Car(make: "Jeep", model: "Grand Cherokee"), Car(make: "Dodge", model: "Challenger")]
let makes = cars.map() { $0.make }

Now consider the same code using a Spread Operator:

let makes = cars*.make

I think there is a tension in Swift between its object oriented nature and higher-order functions which leads to a lot of repetition of { $0.property }

In this example:
cars.map { $0.make }

I’d like to write the following, but ‘make’ is a member, not a free function:
cars.map(make)

Is anyone else irked by the preponderance of { $0.property } in Swift (or is it just me being grumpy)? Not sure what the solution would be, but I can’t say I’m keen on the suggestion of:

cars*.make

It’s too mysterious.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

My general stance remains that, while an interesting idea, adding a second behavior around dot abbreviation causes more confusion than it's worth (especially given that the behavior is not well understood—this thread being an example), and that {$0.whatever} is not a big burden over (.whatever).

Who says it’s a different behavior? If `var foo: Bar` implies `static func foo(self: Self) -> Bar`, then this falls out of that behavior.

As for conflicts, the same issue exists with the unbound methods declared on a type, and in practice it doesn’t cause any problems.

···

--
Brent Royal-Gordon
Architechies

If you remove that overload your code will fail to compile. This is because the dot abbreviation is context sensitive. It only works when the type system knows the type the expression is expected to produce.

Of course :) And if the overload stays and your suggested syntax were added, it would be ambiguous and also fail to compile.

This is not true. They would have different types and therefore would not be ambiguous.

But it’s not central to your position anyway since you are opposed on the grounds of potential for confusion rather than actual ambiguity. :) That is a fair and reasonable position with or without ambiguity.

···

My general stance remains that, while an interesting idea, adding a second behavior around dot abbreviation causes more confusion than it's worth (especially given that the behavior is not well understood—this thread being an example), and that {$0.whatever} is not a big burden over (.whatever).

My general stance remains that, while an interesting idea, adding a second behavior around dot abbreviation causes more confusion than it's worth (especially given that the behavior is not well understood—this thread being an example), and that {$0.whatever} is not a big burden over (.whatever).

Who says it’s a different behavior? If `var foo: Bar` implies `static func foo(self: Self) -> Bar`, then this falls out of that behavior.

Ah, maybe my brain was just wired to look at things differently :) Where the rule right now is:

    where (Self) is expected
        accept .foo where (Self).foo -> (Self)

The rule suggested here is:

    where (Self -> T) is expected
        accept .foo where (Self).foo -> (Self -> T)

Really close, but the method signature needs to unravel to resolve, since `.foo` isn't on type (Self -> T). Not to say that it shouldn't, just that it doesn't.

As for conflicts, the same issue exists with the unbound methods declared on a type, and in practice it doesn’t cause any problems.

Yup, that's because those unbound methods take arguments. The upstream discussion around a `.foo#get` makes sense for disambiguation, and type resolution generally avoids ambiguity.

I'm interested in more complex examples than mapping to a property. This kind of function unraveling could have interesting applications.

Stephen

···

On Dec 17, 2015, at 12:20 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

If you remove that overload your code will fail to compile. This is because the dot abbreviation is context sensitive. It only works when the type system knows the type the expression is expected to produce.

Of course :) And if the overload stays and your suggested syntax were added, it would be ambiguous and also fail to compile.

This is not true. They would have different types and therefore would not be ambiguous.

It's true in my example, but my example is an unlikely, type-ambiguous situation and is unlikely to occur in usage.

But it’s not central to your position anyway since you are opposed on the grounds of potential for confusion rather than actual ambiguity. :) That is a fair and reasonable position with or without ambiguity.

I'm not opposed to the idea if it has broad applications and the type resolution is predictable. I think I'm probably doing a bad job of predicting.

Stephen

···

On Dec 17, 2015, at 12:33 AM, Matthew Johnson <matthew@anandabits.com> wrote:

If you remove that overload your code will fail to compile. This is because the dot abbreviation is context sensitive. It only works when the type system knows the type the expression is expected to produce.

Of course :) And if the overload stays and your suggested syntax were added, it would be ambiguous and also fail to compile.

This is not true. They would have different types and therefore would not be ambiguous.

It's true in my example, but my example is an unlikely, type-ambiguous situation and is unlikely to occur in usage.

I finally managed to see the ambiguity in your example and now I feel foolish for not seeing it right away. Thanks for sticking with it until I understood your example correctly. :) I think it’s because I just kept thinking of the usual signature of map and maybe also because I was thinking more in terms of grammatical ambiguity.

The overload in your example is possible but doesn’t really make any sense. Nonetheless, it does show that a certain kind of ambiguity is possible.

The good news is that this is a kind of ambiguity that already exists in the language today. The compiler catches it and just requires you to resolve it manually when it arises. The same would be true if we adopt the instance method shorthand.

struct Foo {
    static var ambiguous: Foo = Foo()
}

struct Bar {
    static var ambiguous: Bar = Bar()
}

func overloaded(foo: Foo) { }
func overloaded(bar: Bar) { }

overloaded(.ambiguous) // compile error

But it’s not central to your position anyway since you are opposed on the grounds of potential for confusion rather than actual ambiguity. :) That is a fair and reasonable position with or without ambiguity.

I'm not opposed to the idea if it has broad applications and the type resolution is predictable. I think I'm probably doing a bad job of predicting.

Glad to hear you’re not completely opposed. I think it would have pretty broad applicability. It wouldn’t do anything you couldn’t achieve with a closure: { $0.method() }, but it would improve clarity and readability by removing syntactic noise.

I’m going to start a new thread to bring more people into the discussion about this.

Matthew

···

On Dec 17, 2015, at 6:47 AM, Stephen Celis <stephen.celis@gmail.com> wrote:

On Dec 17, 2015, at 12:33 AM, Matthew Johnson <matthew@anandabits.com <mailto:matthew@anandabits.com>> wrote: