[Proposal] Variadics as Attribute


(Haravikk) #1

Posted a new proposal, feedback appreciated:

https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md

One talking point raised already is the correct location for the attribute (parameter or type); I'm undecided personally as there are arguments for both, probably not an important detail though (core team can make a decision on that I think), since the guidelines on attributes have been debated a few times already.

Also, no matter how much I use it I seem to be the worst at using the Github website, so apologies for my many mistakes on that side of things :wink:

Variadics as Attribute

Proposal: SE-NNNN <https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md>
Author: Haravikk <https://github.com/haravikk>
Status: Awaiting review
Review manager: TBD
<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#introduction>Introduction

This proposal is for a redesign of the variadic function parameter syntax as an attribute for greater flexibility.

<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#motivation>Motivation

Currently Swift variadic functions cannot be called with an array of values, potentially requiring two declarations like so:

func someMethod<C:Collection where C.Iterator.Element == Int>(_ values:C) { … } // Regular method
func someMethod(_ values:Int...) { someMethod(values) } // Variadic method
In some cases this leads to only one being defined, forcing developers to use that particular style. When this is the variadic option this means the method is restricted in how it can be used, and parameters constructed.

<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#proposed-solution>Proposed solution

This proposal is to replace the current form of variadic declaration syntax (trailing elipsis) with a new attribute @variadicthat enables any suitable iterable parameter to be called in variadic form if desired.

<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#detailed-design>Detailed design

Quite simply, instead of a trailing elipsis, a variadic parameter will instead be defined via a new @variadic attribute which can be placed upon any function parameter with a type conforming to ArrayLiteralConvertible, or which is a generic constraint against IteratorProtocol, Sequence or Collection such that a default (such as Array) can be used to fulfil the variadic call. Otherwise variadic parameters can be specified with the same restrictions they have now (must not be ambiguous).

For example, consider the following variadic function:

func someMethod(_ values:Int...) { … }
Under this proposal the above can be rewritten as one of the following:

func someMethod(@variadic _ values:[Int]) { … } // Basic Array solution
func someMethod(@variadic _ values:Foo) { … } // Foo is a custom ArrayLiteralConvertible type
func someMethod<I:IteratorProtocol where I.Element == Int>(@variadic _ values:I) { … } // Flexible, single-pass, generic solution
func someMethod<S:Sequence where S.Iterator.Element == Int>(@variadic _ values:S) { … } // Flexible, (probably) multi-pass, generic solution
func someMethod<C:Collection where C.Iterator.Element == Int>(@variadic _ values:C) { … } // Flexible, definitely multi-pass, indexed, generic solution
In this case the Iterator variation is preferred for greatest flexibility, but it will depend upon the actual requirements of the method. Any of these can be called as follows:

someMethod([1, 2, 3, 4, 5, 6]) // normal array-literal call for any of the above
someMethod(1, 2, 3, 4, 5, 6) // variadic call, synonymous with array-literal call
someMethod(foo) // foo is an existing Array, Foo, Iterator, Sequence or Collection variable as appropriate
This altered declaration syntax has a number of advantages over existing variadics:

No requirement for a custom syntax (see alternatives however)
A single declaration can handle variadic and collection/sequence/iterator invocation (no need for some other workaround such as reflection).
Greater flexibility over the actual type of the variadic collection/sequence/iterator, not restricted to Array or array-like.
Developers are free to choose the syntax used at the call-site based upon preference (or pass a variable instead).
Declaration is more discoverable (option-click the @variadic attribute to view documenation).
<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#impact-on-existing-code>Impact on existing code

This proposal as given would remove the existing variadic syntax (trailing elipsis), but a fix-it should enable easy conversion between the following:

func someMethod(_ values:Int...) { … } // Old style
func someMethod(@variadic _ values:[Int]) { … } // New style
However there is an alternative to consider below that will affect this.

<https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md#alternatives-considered>Alternatives considered

One alternative is to simply have the existing variadic syntax produce a method taking an array of the same type, that is implicitly capable of being used in variadic style (but also used directly with Array values). However this has less flexibility than the above, which permits non-Array types. A compromise could be to allow the existing style to remain as a shorthand, though this may discourage consideration of the most appropriate type (in general developers should be encouraged to accept generic types for greatest utility wherever possible, but if trailing elipsis is easy we may just end up with Array being used most often).

The other main alternative considered was removing variadics completely; while this is actually the preference of some (myself included), it seems a lot developers do not wish this. This proposal is intended as a compromise that coallesces variadics with regular functions, without eliminating the possibility to use either style at the call site as developers prefer.


Explicit array splat for variadic functions
array splatting for variadic parameters
(Leonardo Pessoa) #2

I believe the alternative can be even simpler if you consider the type
of the variadic argument is in fact an array. The different syntax
tell the compiler it can relax on demanding an array for that argument
and allow multiple comma-separated values of the same declared type.
Nothing but the compiler complains if you try and pass an array there,
so the compiler could simply be updated to allow a list of arguments
or an array. Also, the compiler knows the type of the variadic
argument and the argument(s) you're passing so if you have 'Int...' it
should be able to recognise a [Int] as a valid (ans single) value.

One other alternative (or complement) is to use ar simple syntax to
tell the compiler you want to use the values of an array. In Ruby the
equivalent of our "args : String..." is simply "*args" (no types in
Ruby) and I can use the array or pass it as a variadic argument to
another function by passing "*args" instead of simply "args". It may
not be much nice visually but one could be able to use something like
"args..." here (I'm open to other suggestions, of course) just to
explicit/ensure that is what you want to do.

IMHO there is nothing that could be gained here with the change in
syntax (perhaps only a clarification that a variadic argument is an
array of that type for someone new to the language, but that's still
more verbose). Even if we were to change the syntax it would still
require changes on how the compiler perceives the arguments passed to
a function and I think we'd have less effort (and less breaking more
backward compatibility) if we just allow variadic arguments to receive
arrays of its type directly.

L

···

On 7 July 2016 at 18:15, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

Posted a new proposal, feedback appreciated:

https://github.com/Haravikk/swift-evolution/blob/2743411af02e3ac6761fbdd780ede1af4cc34ee7/proposals/0000-variadics-as-attribute.md

One talking point raised already is the correct location for the attribute
(parameter or type); I'm undecided personally as there are arguments for
both, probably not an important detail though (core team can make a decision
on that I think), since the guidelines on attributes have been debated a few
times already.

Also, no matter how much I use it I seem to be the worst at using the Github
website, so apologies for my many mistakes on that side of things :wink:

Variadics as Attribute

Proposal: SE-NNNN
Author: Haravikk
Status: Awaiting review
Review manager: TBD

Introduction

This proposal is for a redesign of the variadic function parameter syntax as
an attribute for greater flexibility.

Motivation

Currently Swift variadic functions cannot be called with an array of values,
potentially requiring two declarations like so:

func someMethod<C:Collection where C.Iterator.Element == Int>(_ values:C) {
… } // Regular method
func someMethod(_ values:Int...) { someMethod(values) } // Variadic method

In some cases this leads to only one being defined, forcing developers to
use that particular style. When this is the variadic option this means the
method is restricted in how it can be used, and parameters constructed.

Proposed solution

This proposal is to replace the current form of variadic declaration syntax
(trailing elipsis) with a new attribute @variadicthat enables any suitable
iterable parameter to be called in variadic form if desired.

Detailed design

Quite simply, instead of a trailing elipsis, a variadic parameter will
instead be defined via a new @variadic attribute which can be placed upon
any function parameter with a type conforming to ArrayLiteralConvertible, or
which is a generic constraint against IteratorProtocol, Sequence or
Collection such that a default (such as Array) can be used to fulfil the
variadic call. Otherwise variadic parameters can be specified with the same
restrictions they have now (must not be ambiguous).

For example, consider the following variadic function:

func someMethod(_ values:Int...) { … }

Under this proposal the above can be rewritten as one of the following:

func someMethod(@variadic _ values:[Int]) { … } // Basic Array solution
func someMethod(@variadic _ values:Foo) { … } // Foo is a custom
ArrayLiteralConvertible type
func someMethod<I:IteratorProtocol where I.Element == Int>(@variadic _
values:I) { … } // Flexible, single-pass, generic solution
func someMethod<S:Sequence where S.Iterator.Element == Int>(@variadic _
values:S) { … } // Flexible, (probably) multi-pass, generic solution
func someMethod<C:Collection where C.Iterator.Element == Int>(@variadic _
values:C) { … } // Flexible, definitely multi-pass, indexed, generic
solution

In this case the Iterator variation is preferred for greatest flexibility,
but it will depend upon the actual requirements of the method. Any of these
can be called as follows:

someMethod([1, 2, 3, 4, 5, 6]) // normal array-literal call for any of the
above
someMethod(1, 2, 3, 4, 5, 6) // variadic call, synonymous with
array-literal call
someMethod(foo) // foo is an existing Array, Foo, Iterator,
Sequence or Collection variable as appropriate

This altered declaration syntax has a number of advantages over existing
variadics:

No requirement for a custom syntax (see alternatives however)
A single declaration can handle variadic and collection/sequence/iterator
invocation (no need for some other workaround such as reflection).
Greater flexibility over the actual type of the variadic
collection/sequence/iterator, not restricted to Array or array-like.
Developers are free to choose the syntax used at the call-site based upon
preference (or pass a variable instead).
Declaration is more discoverable (option-click the @variadic attribute to
view documenation).

Impact on existing code

This proposal as given would remove the existing variadic syntax (trailing
elipsis), but a fix-it should enable easy conversion between the following:

func someMethod(_ values:Int...) { … } // Old style
func someMethod(@variadic _ values:[Int]) { … } // New style

However there is an alternative to consider below that will affect this.

Alternatives considered

One alternative is to simply have the existing variadic syntax produce a
method taking an array of the same type, that is implicitly capable of being
used in variadic style (but also used directly with Array values). However
this has less flexibility than the above, which permits non-Array types. A
compromise could be to allow the existing style to remain as a shorthand,
though this may discourage consideration of the most appropriate type (in
general developers should be encouraged to accept generic types for greatest
utility wherever possible, but if trailing elipsis is easy we may just end
up with Array being used most often).

The other main alternative considered was removing variadics completely;
while this is actually the preference of some (myself included), it seems a
lot developers do not wish this. This proposal is intended as a compromise
that coallesces variadics with regular functions, without eliminating the
possibility to use either style at the call site as developers prefer.

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


(Haravikk) #3

This proposal doesn't just handle the issue of passing an array however, it also allows variadics of any type of your choice (so long as it conforms to ArrayLiteralConvertible) rather than it always being an Array. Likewise it would also support any generic iterator, sequence or collection; while calling the function in variadic style would just result in some default type behind the scenes, you can also pass in anything you like using a variable (just like you can to a regular method).

Also one other advantage I didn't list is that an attribute could support future extensions, for example the ability to set something like @variadic(minimum: 1), causing the compiler to produce an error if a variadic call is empty. Beyond the scope of this initial proposal, but basically this seems a much more flexible way to do things than trying to layer support onto the existing syntax, though it could be retained as a shorthand.

···

On 7 Jul 2016, at 22:39, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think we'd have less effort (and less breaking more
backward compatibility) if we just allow variadic arguments to receive
arrays of its type directly.


(Tino) #4

I'm not sure if the timing of this proposal is coincidence (I recently revived a discussion to remove variadics), but although I stated variadics are not that important, I like this idea much better than the "…"-syntax which looks like a carryover from C…

"@variadic" is not only more explicit, but the proposal also enhances variadics:
I don't remember the details, but there has been a discussion about variadic functions that may alternatively called with an array; afaics, this capability is even extended by allowing other containers as well.

Sadly, I doubt it will be possible to incorporate this change into Swift 3, so I have to propose something I just mildly criticized:
Deprecate variadics now, but add them again later as outlined here.

Tino


(Pyry Jahkola) #5

I agree that we should have a way to call a variadic function with an Array at hand, without needing to take its elements apart (`foo(x[0], x[1], x[2])`) — which indeed is only practical when we statically know its length.

But I fear there's a shortcoming in this proposal that hasn't been addressed: How would the compiler clear the ambiguity in single-argument calls between existentials such as `Any` or `CustomStringConvertible` and sequences thereof (e.g. `[Any]` or `[CustomStringConvertible]`, which both can be treated as `Any` and `CustomStringConvertible` as well?

If you take this function for example,

    func printThem(@variadic _ values: [Any])

then what would `values` be in the following calls, and why?

    printThem() // clearly []
    printThem(1) // clearly [1]
    printThem(1, 2) // clearly [1, 2]
    printThem([1], [2]) // clearly [[1], [2]]
    printThem([]) // [] or [[]], which one?
    printThem([1]) // [1] or [[1]]?
    printThem([1, 2]) // [1, 2] or [[1, 2]]?

I think it would be less painful a change (i.e. can be delayed past Swift 3) if we just augment what we have (the `...` argument declaration syntax) with a way to expand an Array in place of a variadic argument:

    func printThem(_ values: Any...)

    printThem(1) // values == [1]
    printThem([1]) // values == [[1]]
    // Possible expansion postfix syntax:
    printThem([1]...) // values == [1]
    // Expanding a non-array sequence:
    let things: Set<Int> = [1, 2, 3]
    printThem(Array(things)...)

I think variadic functions are intended for human convenience and rarely used in performance-critical code, and it's probably more convenient that they're always passed as an Array, and not as a generic sequence.

— Pyry

Haravikk wrote:

···

For example, consider the following variadic function:

func someMethod(_ values:Int...) { … }
Under this proposal the above can be rewritten as one of the following:

func someMethod(@variadic _ values:[Int]) { … } // Basic Array solution
func someMethod(@variadic _ values:Foo) { … } // Foo is a custom ArrayLiteralConvertible type
func someMethod<I:IteratorProtocol where I.Element == Int>(@variadic _ values:I) { … } // Flexible, single-pass, generic solution
func someMethod<S:Sequence where S.Iterator.Element == Int>(@variadic _ values:S) { … } // Flexible, (probably) multi-pass, generic solution
func someMethod<C:Collection where C.Iterator.Element == Int>(@variadic _ values:C) { … } // Flexible, definitely multi-pass, indexed, generic solution
In this case the Iterator variation is preferred for greatest flexibility, but it will depend upon the actual requirements of the method. Any of these can be called as follows:

someMethod([1, 2, 3, 4, 5, 6]) // normal array-literal call for any of the above
someMethod(1, 2, 3, 4, 5, 6) // variadic call, synonymous with array-literal call
someMethod(foo) // foo is an existing Array, Foo, Iterator, Sequence or Collection variable as appropriate


Variadic Parameters that Accept Array Inputs
(Justin Jia) #6

Maybe we can introduce #variadic compiler magic to expand
array to varidict parameters.

e.g.

func sum(x: Int...)

let x = [1, 2, 3]

sum(#variadic(x))


(Tino) #7

The last post in this thread is nearly a week ago... did you already apply for review?
It would be a pity if this topic isn't finished, and I think a reduced variant that merely replaces "values: Int…" with "@variadic _ values: [Int]" shouldn't be a problem for Swift 3, leaving the possibility of adding all other features later.


(Leonardo Pessoa) #8

It is definitely not hard to solve any issues here (if you consider only
the basic variadics). Int... is nothing more than [Int], so essentially

   func doSomething(args : Int...)

is also just

   func doSomething(args : [Int])

We just have to think of it like some form of function overloading and the
compiler can automatically handle that.

As for enabling usage of other types as the result of variadics, I don't
see how that can be a benefit (unless you're just trying to write little
bit less on your own code). You would have to add some extra code on the
compiler to check whether you can use that type for your variadics argument
and may incur in more changes to enable handling different classes
possible. I would also expect to be able to use dictionaries as variadics
with this syntax, and that would be confusing too.

Should this pass like it is, it would not surprise me if my apps take more
time to compile. Now, if the problem is just being able to pass an array to
a variadics argument, my solution is cleaner, incur in less changes to the
language and is backwards compatible.

L

···

On Friday, 8 July 2016, Haravikk via swift-evolution < swift-evolution@swift.org> wrote:

On 8 Jul 2016, at 10:31, Pyry Jahkola <pyry.jahkola@iki.fi > <javascript:_e(%7B%7D,'cvml','pyry.jahkola@iki.fi');>> wrote:

If you take this function for example,

    func printThem(@variadic _ values: [Any])

then what would `values` be in the following calls, and why?

    printThem() // clearly []
    printThem(1) // clearly [1]
    printThem(1, 2) // clearly [1, 2]
    printThem([1], [2]) // clearly [[1], [2]]
    printThem([]) // [] or [[]], which one?
    printThem([1]) // [1] or [[1]]?
    printThem([1, 2]) // [1, 2] or [[1, 2]]?

I think it would be less painful a change (i.e. can be delayed past Swift
3) if we just augment what we have (the `...` argument declaration syntax)
with a way to expand an Array in place of a variadic argument:

    func printThem(_ values: Any...)

    printThem(1) // values == [1]
    printThem([1]) // values == [[1]]
    // Possible expansion postfix syntax:
    printThem([1]...) // values == [1]
    // Expanding a non-array sequence:
    let things: Set<Int> = [1, 2, 3]
    printThem(Array(things)...)

Good point, but actually these don't seem mutually exclusive; the three
problem examples you gave could be resolved either by requiring the extra
set of square brackets for clarity, and/or some kind of expansion like you
suggest, either an operator or even possibly repurposing the attribute at
the call-site for consistency. For example:

printThem([1]) // warning, ambiguous
printThem([[1]])// non-variadic
printThem(@variadic [1]) // absolutely definitely 100% a variadic call
printThem([1],) // alternative to an operator/attribute for
variadic disambiguation?

Thoughts?

On 8 Jul 2016, at 10:42, Tino Heth <2th@gmx.de > <javascript:_e(%7B%7D,'cvml','2th@gmx.de');>> wrote:
I'm not sure if the timing of this proposal is coincidence (I recently
revived a discussion to remove variadics)

It is definitely not a coincidence :wink:
I participated in the discussion on removal in the past and actually
posted a preliminary version of this there, so the recent posts reminded me
to put it into a proper proposal. Personally like some I'd just remove the
feature completely, but it seems enough people want to keep it that it's
worth exploring alternatives as there seems a fairly even mix of those for
and against the feature.

--
L


(Charlie Monroe) #9

I like this proposal. I was a bit against when the discussion around the removal of variadics arose, but I fee that this proposal takes a good whack at the problem.

I have struggled with array vs. variadic arguments for several methods, ending up implementing both overloads and the variadic method only passed the arguments to the overload that took an array as an argument. This would nicely eliminate the need for such "hacks".

···

On Jul 8, 2016, at 12:03 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 7 Jul 2016, at 22:39, Leonardo Pessoa <me@lmpessoa.com> wrote:

I think we'd have less effort (and less breaking more
backward compatibility) if we just allow variadic arguments to receive
arrays of its type directly.

This proposal doesn't just handle the issue of passing an array however, it also allows variadics of any type of your choice (so long as it conforms to ArrayLiteralConvertible) rather than it always being an Array. Likewise it would also support any generic iterator, sequence or collection; while calling the function in variadic style would just result in some default type behind the scenes, you can also pass in anything you like using a variable (just like you can to a regular method).

Also one other advantage I didn't list is that an attribute could support future extensions, for example the ability to set something like @variadic(minimum: 1), causing the compiler to produce an error if a variadic call is empty. Beyond the scope of this initial proposal, but basically this seems a much more flexible way to do things than trying to layer support onto the existing syntax, though it could be retained as a shorthand.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #10

If you take this function for example,

    func printThem(@variadic _ values: [Any])

then what would `values` be in the following calls, and why?

    printThem() // clearly []
    printThem(1) // clearly [1]
    printThem(1, 2) // clearly [1, 2]
    printThem([1], [2]) // clearly [[1], [2]]
    printThem([]) // [] or [[]], which one?
    printThem([1]) // [1] or [[1]]?
    printThem([1, 2]) // [1, 2] or [[1, 2]]?

I think it would be less painful a change (i.e. can be delayed past Swift 3) if we just augment what we have (the `...` argument declaration syntax) with a way to expand an Array in place of a variadic argument:

    func printThem(_ values: Any...)

    printThem(1) // values == [1]
    printThem([1]) // values == [[1]]
    // Possible expansion postfix syntax:
    printThem([1]...) // values == [1]
    // Expanding a non-array sequence:
    let things: Set<Int> = [1, 2, 3]
    printThem(Array(things)...)

Good point, but actually these don't seem mutually exclusive; the three problem examples you gave could be resolved either by requiring the extra set of square brackets for clarity, and/or some kind of expansion like you suggest, either an operator or even possibly repurposing the attribute at the call-site for consistency. For example:

  printThem([1]) // warning, ambiguous
  printThem([[1]])// non-variadic
  printThem(@variadic [1]) // absolutely definitely 100% a variadic call
  printThem([1],) // alternative to an operator/attribute for variadic disambiguation?

Thoughts?

···

On 8 Jul 2016, at 10:31, Pyry Jahkola <pyry.jahkola@iki.fi> wrote:

On 8 Jul 2016, at 10:42, Tino Heth <2th@gmx.de> wrote:
I'm not sure if the timing of this proposal is coincidence (I recently revived a discussion to remove variadics)

It is definitely not a coincidence :wink:
I participated in the discussion on removal in the past and actually posted a preliminary version of this there, so the recent posts reminded me to put it into a proper proposal. Personally like some I'd just remove the feature completely, but it seems enough people want to keep it that it's worth exploring alternatives as there seems a fairly even mix of those for and against the feature.


(Haravikk) #11

You would have to add some extra code on the compiler to check whether you can use that type for your variadics argument and may incur in more changes to enable handling different classes possible.

Not really; the variadic call just needs to be treated as if it is an array literal, at which point the compiler will either match a method or it won't. The only real difference is that when called as a variadic the compiler will only match functions with the @variadic attribute. In other words the following resolve in much the same way:

  someMethod([1, 2, 3, 4, 5, 6]) // Looks for a declaration of someMethod() that can take an array literal
  someMethod(1, 2, 3, 4, 5, 6) // Looks for a declaration of someMethod() that can take an array literal, and has a @variadic parameter

Treating the trailing ellipsis as a shorthand for [Foo] is no different in that respect, it's just limited to Array only. In other words, if an array literal cannot be accepted by the parameter, then it cannot have the @variadic attribute, we'd need a compiler expert to comment but I don't think that should be that hard to check (concrete types will be checked for conformance to ArrayLiteralConvertible, and generics will be checked to see if they can be fulfilled by an Array or some kind of ArrayLiteral type).

Really what it comes down to is a choice between two methods of solving the array passing problem:

Variadic function treated as regular function with array parameter.
Regular function gains ability to be called (optionally) in variadic style at call site.

But my preference is for the latter as it eliminates the variadic function declarations as being some kind of special case, and moves it into a feature of regular function declarations.

I would also expect to be able to use dictionaries as variadics with this syntax, and that would be confusing too.

This should only be the case I think if you've extended Dictionary with an ArrayLiteralConvertible initialiser, or you declared your function to take a generic iterator/sequence/collection with elements of type (Key, Value) in which case, yes, a Dictionary could fulfil the requirements.

···

On 8 Jul 2016, at 12:03, Leonardo Pessoa <me@lmpessoa.com> wrote:


(Haravikk) #12

I'm hoping to update the proposal and submit it in the next few days. I don't think there should be any need to restrict it to Int… to [Int]; unless there are technical challenges I don't believe there should be any reason that arbitrary iterator/sequence/collection types can't be supported as proposed, the main issues seem to be around how to resolve ambiguity when the type is Any, and whether the ability to call with an array should just be bolted onto the existing syntax (not my preference). I may move discussion of other collection types to its own section though, to make the core proposal as simple as possible, and leave it up to the core team whether to do that part.

···

On 15 Jul 2016, at 20:14, Tino Heth <2th@gmx.de> wrote:

The last post in this thread is nearly a week ago... did you already apply for review?
It would be a pity if this topic isn't finished, and I think a reduced variant that merely replaces "values: Int…" with "@variadic _ values: [Int]" shouldn't be a problem for Swift 3, leaving the possibility of adding all other features later.


(Kristóf Liliom) #13

Hi!

I read through this proposal, and I have the feeling that we are trying to solve an issue with Swift from the wrong angle. IMHO Swift has one of the best implementations of variadics which has only one major downfall: passing an array as a variadic argument is not possible. Maybe it would be a better idea to leave method declarations as is and only change the call-site as something like this:

func someMethod(_ values:Int...) { /* values: 1, 2, 3, 4 */ }

func someMethod2(_ values:Int...) {
    someMethod(#unpack(values)) // Or #variadic
}

someMethod2(1, 2, 3, 4)

This would tell the compiler to pass the array's values. Maybe in a more advanced scenario, even this could be supported:

func someMethod(_ values:Int...) { /* values: -1, -2, 1, 2, 3, 4, -3, -4 */ }

func someMethod2(_ values:Int...) {
    someMethod(-1, -2, #unpack(values), -3, -4) // Or #variadic
}

someMethod2(1, 2, 3, 4)

I know this is not a well defined idea, but please give it a thought, could it be a feasible solution to this problem?

Best,
Kristóf

···

On 08 Jul 2016, at 13:43, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 8 Jul 2016, at 12:03, Leonardo Pessoa <me@lmpessoa.com <mailto:me@lmpessoa.com>> wrote:
You would have to add some extra code on the compiler to check whether you can use that type for your variadics argument and may incur in more changes to enable handling different classes possible.

Not really; the variadic call just needs to be treated as if it is an array literal, at which point the compiler will either match a method or it won't. The only real difference is that when called as a variadic the compiler will only match functions with the @variadic attribute. In other words the following resolve in much the same way:

  someMethod([1, 2, 3, 4, 5, 6]) // Looks for a declaration of someMethod() that can take an array literal
  someMethod(1, 2, 3, 4, 5, 6) // Looks for a declaration of someMethod() that can take an array literal, and has a @variadic parameter

Treating the trailing ellipsis as a shorthand for [Foo] is no different in that respect, it's just limited to Array only. In other words, if an array literal cannot be accepted by the parameter, then it cannot have the @variadic attribute, we'd need a compiler expert to comment but I don't think that should be that hard to check (concrete types will be checked for conformance to ArrayLiteralConvertible, and generics will be checked to see if they can be fulfilled by an Array or some kind of ArrayLiteral type).

Really what it comes down to is a choice between two methods of solving the array passing problem:

Variadic function treated as regular function with array parameter.
Regular function gains ability to be called (optionally) in variadic style at call site.

But my preference is for the latter as it eliminates the variadic function declarations as being some kind of special case, and moves it into a feature of regular function declarations.

I would also expect to be able to use dictionaries as variadics with this syntax, and that would be confusing too.

This should only be the case I think if you've extended Dictionary with an ArrayLiteralConvertible initialiser, or you declared your function to take a generic iterator/sequence/collection with elements of type (Key, Value) in which case, yes, a Dictionary could fulfil the requirements.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tino) #14

imho this is a good idea: Its increased power is a major argument for the proposal, but the schedule seems to be very tight already… and I guess the discussion about possible problems caused by variadic functions which can be called with an explicit collection could be a real distraction, whereas the basic idea is so clear that there shouldn't be any valid reasons to not accept it.

···

Am 17.07.2016 um 18:31 schrieb Haravikk <swift-evolution@haravikk.me>:

I may move discussion of other collection types to its own section though, to make the core proposal as simple as possible, and leave it up to the core team whether to do that part.


(Leonardo Pessoa) #15

Kristof, this is the closest to what I've been proposing but I'm
really unable to understand why it is to had to just change the
compiler to recognise an array passed as argument to a variadic
argument. No need for any extra keywords or complicated solutions,
just make the compiler recognise that "Int... === [Int]" and we have
the issue solved: it accepts both and array of ints or multiple ints
in its arguments.

L

···

On 8 July 2016 at 17:18, Kristóf Liliom <kristof.liliom@mattakis.com> wrote:

Hi!

I read through this proposal, and I have the feeling that we are trying to
solve an issue with Swift from the wrong angle. IMHO Swift has one of the
best implementations of variadics which has only one major downfall: passing
an array as a variadic argument is not possible. Maybe it would be a better
idea to leave method declarations as is and only change the call-site as
something like this:

func someMethod(_ values:Int...) { /* values: 1, 2, 3, 4 */ }

func someMethod2(_ values:Int...) {
    someMethod(#unpack(values)) // Or #variadic
}

someMethod2(1, 2, 3, 4)

This would tell the compiler to pass the array's values. Maybe in a more
advanced scenario, even this could be supported:

func someMethod(_ values:Int...) { /* values: -1, -2, 1, 2, 3, 4, -3, -4 */
}

func someMethod2(_ values:Int...) {
    someMethod(-1, -2, #unpack(values), -3, -4) // Or #variadic
}

someMethod2(1, 2, 3, 4)

I know this is not a well defined idea, but please give it a thought, could
it be a feasible solution to this problem?

Best,
Kristóf

On 08 Jul 2016, at 13:43, Haravikk via swift-evolution > <swift-evolution@swift.org> wrote:

On 8 Jul 2016, at 12:03, Leonardo Pessoa <me@lmpessoa.com> wrote:
You would have to add some extra code on the compiler to check whether you
can use that type for your variadics argument and may incur in more changes
to enable handling different classes possible.

Not really; the variadic call just needs to be treated as if it is an array
literal, at which point the compiler will either match a method or it won't.
The only real difference is that when called as a variadic the compiler will
only match functions with the @variadic attribute. In other words the
following resolve in much the same way:

someMethod([1, 2, 3, 4, 5, 6]) // Looks for a declaration of someMethod()
that can take an array literal
someMethod(1, 2, 3, 4, 5, 6) // Looks for a declaration of someMethod() that
can take an array literal, and has a @variadic parameter

Treating the trailing ellipsis as a shorthand for [Foo] is no different in
that respect, it's just limited to Array only. In other words, if an array
literal cannot be accepted by the parameter, then it cannot have the
@variadic attribute, we'd need a compiler expert to comment but I don't
think that should be that hard to check (concrete types will be checked for
conformance to ArrayLiteralConvertible, and generics will be checked to see
if they can be fulfilled by an Array or some kind of ArrayLiteral type).

Really what it comes down to is a choice between two methods of solving the
array passing problem:

Variadic function treated as regular function with array parameter.
Regular function gains ability to be called (optionally) in variadic style
at call site.

But my preference is for the latter as it eliminates the variadic function
declarations as being some kind of special case, and moves it into a feature
of regular function declarations.

I would also expect to be able to use dictionaries as variadics with this
syntax, and that would be confusing too.

This should only be the case I think if you've extended Dictionary with an
ArrayLiteralConvertible initialiser, or you declared your function to take a
generic iterator/sequence/collection with elements of type (Key, Value) in
which case, yes, a Dictionary could fulfil the requirements.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kristóf Liliom) #16

I think if Int... === [Int] would be true, then it would introduce a new set of ambiguity and defaulting to a specific behaviour would be a "good guess" rather than a reliable logic.

Best,
Kristóf

···

On 08 Jul 2016, at 22:24, Leonardo Pessoa <me@lmpessoa.com> wrote:

Kristof, this is the closest to what I've been proposing but I'm
really unable to understand why it is to had to just change the
compiler to recognise an array passed as argument to a variadic
argument. No need for any extra keywords or complicated solutions,
just make the compiler recognise that "Int... === [Int]" and we have
the issue solved: it accepts both and array of ints or multiple ints
in its arguments.

L

On 8 July 2016 at 17:18, Kristóf Liliom <kristof.liliom@mattakis.com> wrote:

Hi!

I read through this proposal, and I have the feeling that we are trying to
solve an issue with Swift from the wrong angle. IMHO Swift has one of the
best implementations of variadics which has only one major downfall: passing
an array as a variadic argument is not possible. Maybe it would be a better
idea to leave method declarations as is and only change the call-site as
something like this:

func someMethod(_ values:Int...) { /* values: 1, 2, 3, 4 */ }

func someMethod2(_ values:Int...) {
   someMethod(#unpack(values)) // Or #variadic
}

someMethod2(1, 2, 3, 4)

This would tell the compiler to pass the array's values. Maybe in a more
advanced scenario, even this could be supported:

func someMethod(_ values:Int...) { /* values: -1, -2, 1, 2, 3, 4, -3, -4 */
}

func someMethod2(_ values:Int...) {
   someMethod(-1, -2, #unpack(values), -3, -4) // Or #variadic
}

someMethod2(1, 2, 3, 4)

I know this is not a well defined idea, but please give it a thought, could
it be a feasible solution to this problem?

Best,
Kristóf

On 08 Jul 2016, at 13:43, Haravikk via swift-evolution >> <swift-evolution@swift.org> wrote:

On 8 Jul 2016, at 12:03, Leonardo Pessoa <me@lmpessoa.com> wrote:
You would have to add some extra code on the compiler to check whether you
can use that type for your variadics argument and may incur in more changes
to enable handling different classes possible.

Not really; the variadic call just needs to be treated as if it is an array
literal, at which point the compiler will either match a method or it won't.
The only real difference is that when called as a variadic the compiler will
only match functions with the @variadic attribute. In other words the
following resolve in much the same way:

someMethod([1, 2, 3, 4, 5, 6]) // Looks for a declaration of someMethod()
that can take an array literal
someMethod(1, 2, 3, 4, 5, 6) // Looks for a declaration of someMethod() that
can take an array literal, and has a @variadic parameter

Treating the trailing ellipsis as a shorthand for [Foo] is no different in
that respect, it's just limited to Array only. In other words, if an array
literal cannot be accepted by the parameter, then it cannot have the
@variadic attribute, we'd need a compiler expert to comment but I don't
think that should be that hard to check (concrete types will be checked for
conformance to ArrayLiteralConvertible, and generics will be checked to see
if they can be fulfilled by an Array or some kind of ArrayLiteral type).

Really what it comes down to is a choice between two methods of solving the
array passing problem:

Variadic function treated as regular function with array parameter.
Regular function gains ability to be called (optionally) in variadic style
at call site.

But my preference is for the latter as it eliminates the variadic function
declarations as being some kind of special case, and moves it into a feature
of regular function declarations.

I would also expect to be able to use dictionaries as variadics with this
syntax, and that would be confusing too.

This should only be the case I think if you've extended Dictionary with an
ArrayLiteralConvertible initialiser, or you declared your function to take a
generic iterator/sequence/collection with elements of type (Key, Value) in
which case, yes, a Dictionary could fulfil the requirements.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Haravikk) #17

These are a possible compromise certainly, but it's not exactly pretty, as I'm not sure it would eliminate the desire to define collection-based methods anyway, as it'll still be neater to just define both so you can do someMethod(values) directly (when it's not ambiguous), in which case this capability only really benefits us when only the variadic option is defined and we have no choice but to use it which feels a little like solving the wrong problem (we'd be working around a developer's unwillingness to define flexible options).

Part of my aim with this proposal is to remove variadic function declarations as being this weird kind of special function type, so we can just use a standard function declaration and enable the part that actually makes them different; the way they're called.

While the issue of ambiguity is a real one, it seems to me that the problem is specific to Any…, or any other type that can represent both a collection and the elements it can contain. Another possible solution could just be a parameter on this proposal's attribute so we can solve the issue as we do now, for example:

  func print(@variadic(only) _ values:[Any]) { print(contentsOf: values) } // Can only be called variadically
  func print(contentsOf:[Any]) { … } // Can only be called with an array

Here we resolve the ambiguity exactly as we do right now; with one variadic-only declaration, and one array-only declaration. However, for cases where the type isn't Any (and thus should be unambiguous) we will only need a single function to handle both cases.

I know that Any… is necessary for print-style methods like the above example, but other than that how common is it?

···

On 8 Jul 2016, at 21:18, Kristóf Liliom <kristof.liliom@mattakis.com> wrote:

I read through this proposal, and I have the feeling that we are trying to solve an issue with Swift from the wrong angle. IMHO Swift has one of the best implementations of variadics which has only one major downfall: passing an array as a variadic argument is not possible. Maybe it would be a better idea to leave method declarations as is and only change the call-site as something like this:

func someMethod(_ values:Int...) { /* values: 1, 2, 3, 4 */ }

func someMethod2(_ values:Int...) {
    someMethod(#unpack(values)) // Or #variadic
}

someMethod2(1, 2, 3, 4)

This would tell the compiler to pass the array's values. Maybe in a more advanced scenario, even this could be supported:

func someMethod(_ values:Int...) { /* values: -1, -2, 1, 2, 3, 4, -3, -4 */ }

func someMethod2(_ values:Int...) {
    someMethod(-1, -2, #unpack(values), -3, -4) // Or #variadic
}

someMethod2(1, 2, 3, 4)

I know this is not a well defined idea, but please give it a thought, could it be a feasible solution to this problem?


(Haravikk) #18

I've created a new pull request for this, you can view the updated file here:
https://github.com/Haravikk/swift-evolution/blob/a13dc03d6a8c76b25a30710d70cbadc1eb31b3cd/proposals/nnnn-variadics-as-attribute.md

Hopefully it's still clear; I know I have a nasty tendency to be overly verbose with wording and stuff, though the first example should keep the meat of the proposal straightforward =)

···

On 20 Jul 2016, at 14:55, Tino Heth <2th@gmx.de> wrote:

Am 17.07.2016 um 18:31 schrieb Haravikk <swift-evolution@haravikk.me>:

I may move discussion of other collection types to its own section though, to make the core proposal as simple as possible, and leave it up to the core team whether to do that part.

imho this is a good idea: Its increased power is a major argument for the proposal, but the schedule seems to be very tight already… and I guess the discussion about possible problems caused by variadic functions which can be called with an explicit collection could be a real distraction, whereas the basic idea is so clear that there shouldn't be any valid reasons to not accept it.


(Leonardo Pessoa) #19

I meant only for the sake of variadics. That would "teach" the
compiler to read "func x(args : Int...)" from our code and interpret
it too as "func x(args ; [Int])" thus accepting lists of arguments or
an array with the arguments with no additional effort from our part.

L

···

On 8 July 2016 at 17:29, Kristóf Liliom <kristof.liliom@mattakis.com> wrote:

I think if Int... === [Int] would be true, then it would introduce a new set of ambiguity and defaulting to a specific behaviour would be a "good guess" rather than a reliable logic.

Best,
Kristóf

On 08 Jul 2016, at 22:24, Leonardo Pessoa <me@lmpessoa.com> wrote:

Kristof, this is the closest to what I've been proposing but I'm
really unable to understand why it is to had to just change the
compiler to recognise an array passed as argument to a variadic
argument. No need for any extra keywords or complicated solutions,
just make the compiler recognise that "Int... === [Int]" and we have
the issue solved: it accepts both and array of ints or multiple ints
in its arguments.

L

On 8 July 2016 at 17:18, Kristóf Liliom <kristof.liliom@mattakis.com> wrote:

Hi!

I read through this proposal, and I have the feeling that we are trying to
solve an issue with Swift from the wrong angle. IMHO Swift has one of the
best implementations of variadics which has only one major downfall: passing
an array as a variadic argument is not possible. Maybe it would be a better
idea to leave method declarations as is and only change the call-site as
something like this:

func someMethod(_ values:Int...) { /* values: 1, 2, 3, 4 */ }

func someMethod2(_ values:Int...) {
   someMethod(#unpack(values)) // Or #variadic
}

someMethod2(1, 2, 3, 4)

This would tell the compiler to pass the array's values. Maybe in a more
advanced scenario, even this could be supported:

func someMethod(_ values:Int...) { /* values: -1, -2, 1, 2, 3, 4, -3, -4 */
}

func someMethod2(_ values:Int...) {
   someMethod(-1, -2, #unpack(values), -3, -4) // Or #variadic
}

someMethod2(1, 2, 3, 4)

I know this is not a well defined idea, but please give it a thought, could
it be a feasible solution to this problem?

Best,
Kristóf

On 08 Jul 2016, at 13:43, Haravikk via swift-evolution >>> <swift-evolution@swift.org> wrote:

On 8 Jul 2016, at 12:03, Leonardo Pessoa <me@lmpessoa.com> wrote:
You would have to add some extra code on the compiler to check whether you
can use that type for your variadics argument and may incur in more changes
to enable handling different classes possible.

Not really; the variadic call just needs to be treated as if it is an array
literal, at which point the compiler will either match a method or it won't.
The only real difference is that when called as a variadic the compiler will
only match functions with the @variadic attribute. In other words the
following resolve in much the same way:

someMethod([1, 2, 3, 4, 5, 6]) // Looks for a declaration of someMethod()
that can take an array literal
someMethod(1, 2, 3, 4, 5, 6) // Looks for a declaration of someMethod() that
can take an array literal, and has a @variadic parameter

Treating the trailing ellipsis as a shorthand for [Foo] is no different in
that respect, it's just limited to Array only. In other words, if an array
literal cannot be accepted by the parameter, then it cannot have the
@variadic attribute, we'd need a compiler expert to comment but I don't
think that should be that hard to check (concrete types will be checked for
conformance to ArrayLiteralConvertible, and generics will be checked to see
if they can be fulfilled by an Array or some kind of ArrayLiteral type).

Really what it comes down to is a choice between two methods of solving the
array passing problem:

Variadic function treated as regular function with array parameter.
Regular function gains ability to be called (optionally) in variadic style
at call site.

But my preference is for the latter as it eliminates the variadic function
declarations as being some kind of special case, and moves it into a feature
of regular function declarations.

I would also expect to be able to use dictionaries as variadics with this
syntax, and that would be confusing too.

This should only be the case I think if you've extended Dictionary with an
ArrayLiteralConvertible initialiser, or you declared your function to take a
generic iterator/sequence/collection with elements of type (Key, Value) in
which case, yes, a Dictionary could fulfil the requirements.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chris Lattner) #20

I’m sorry I’m late to this thread, but I’m personally strongly opposed to this. The problem being solved here is so minor that I don’t see a reason to make a change. Further, the proposed syntax is so heavy weight that it will adversely affect readability of the API.

-Chris

···

On Jul 20, 2016, at 11:37 AM, Haravikk via swift-evolution <swift-evolution@swift.org> wrote:

On 20 Jul 2016, at 14:55, Tino Heth <2th@gmx.de> wrote:

Am 17.07.2016 um 18:31 schrieb Haravikk <swift-evolution@haravikk.me>:

I may move discussion of other collection types to its own section though, to make the core proposal as simple as possible, and leave it up to the core team whether to do that part.

imho this is a good idea: Its increased power is a major argument for the proposal, but the schedule seems to be very tight already… and I guess the discussion about possible problems caused by variadic functions which can be called with an explicit collection could be a real distraction, whereas the basic idea is so clear that there shouldn't be any valid reasons to not accept it.

I've created a new pull request for this, you can view the updated file here:
https://github.com/Haravikk/swift-evolution/blob/a13dc03d6a8c76b25a30710d70cbadc1eb31b3cd/proposals/nnnn-variadics-as-attribute.md

Hopefully it's still clear; I know I have a nasty tendency to be overly verbose with wording and stuff, though the first example should keep the meat of the proposal straightforward =)