Proposal for Passing Arrays to Variadic Functions


(Arthur Ariel Sabintsev) #1

Hi,

I have put together a proposal for passing arrays to variadic functions (along the lines of Ruby’s Splay operator).

Proposal: https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this proposal

Best,

Arthur / Sabintsev.com


(Lily Ballard) #2

You can't use * as suggested, because that conflicts with * as a
prefix operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) { bar(xs...) }

I also wonder whether there's a reason Swift doesn't already support
this. Maybe there's some desired future change to variadic arguments to
improve performance (i.e. to avoid having to allocate an array) that
wouldn't be compatible? Although I'm not sure what that would be. Maybe
there's actually no barrier at all and it was just something that wasn't
prioritized. It would be good to know either way.

-Kevin

···

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution wrote:

Hi,

I have put together a proposal for passing arrays to variadic
functions (along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52 Bug
Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com[1]

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

Links:

  1. http://sabintsev.com


(Arthur Ariel Sabintsev) #3

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows
a type T to be a anything, including arrays. The example that was used to
delineate this concept to me was that of "Any...", which from within the
variadic function, could be interpreted as T or [T]. The result is
ambiguous in the current implementation, which is why some identifier
syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use one
that was familiar to other developers, so I chose the one that Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

···

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

You can't use * as suggested, because that conflicts with * as a prefix
operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
    bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this.
Maybe there's some desired future change to variadic arguments to improve
performance (i.e. to avoid having to allocate an array) that wouldn't be
compatible? Although I'm not sure what that would be. Maybe there's
actually no barrier at all and it was just something that wasn't
prioritized. It would be good to know either way.

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution > wrote:

Hi,

I have put together a proposal for passing arrays to variadic functions
(along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com <http://sabintsev.com>

*_______________________________________________*
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


(Joe Groff) #4

You can't use * as suggested, because that conflicts with * as a prefix operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
    bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this. Maybe there's some desired future change to variadic arguments to improve performance (i.e. to avoid having to allocate an array) that wouldn't be compatible? Although I'm not sure what that would be. Maybe there's actually no barrier at all and it was just something that wasn't prioritized. It would be good to know either way.

No fundamental reason, just haven't gotten around to it. We are however planning to rework the type system model for functions not to be tuple-based, since it causes problems with unconstrained polymorphism, default arguments, inout parameters, and other function features. We'll need a replacement feature to support forwarding argument tuples if we do this. cc'ing Chris Willmore, who has been working a plan here.

-Joe

···

On Dec 11, 2015, at 10:54 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution wrote:

Hi,

I have put together a proposal for passing arrays to variadic functions (along the lines of Ruby’s Splay operator).

Proposal: https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this proposal

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

_______________________________________________
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


(Chris Lattner) #5

This hasn’t been done just because it has never risen to the highest priority, not because we’re against it. In addition to Joe Groff’s downstream comment, one change we need to discuss: requiring the formation of a formal Array is somewhat unfortunate from a performance perspective. It would make more sense to pass varargs by allocating the array as a fixed size buffer in the callers stack, and passing a pointer+length down to the callee. The callee could then wrap that up and vend it with a formal type of Array or perhaps just SequenceType. In the case where the sequence doesn’t escape, this would allow the allocation/deallocation + copy to the heap to be eliminated.

-Chris

···

On Dec 11, 2015, at 10:54 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

You can't use * as suggested, because that conflicts with * as a prefix operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
    bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this. Maybe there's some desired future change to variadic arguments to improve performance (i.e. to avoid having to allocate an array) that wouldn't be compatible? Although I'm not sure what that would be. Maybe there's actually no barrier at all and it was just something that wasn't prioritized. It would be good to know either way.


(Lily Ballard) #6

Using any operator at all is a problem. The only way that can work
without conflict is if the operator returns some special type that is
known to the compiler to represent a variadic call, but that doesn't
seem like a great idea. What do you think of the ... suffix? That can't
conflict with operators, and it matches the variadic function
declaration.

-Kevin

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it
allows a type T to be a anything, including arrays. The example that
was used to delineate this concept to me was that of "Any...", which
from within the variadic function, could be interpreted as T or [T].
The result is ambiguous in the current implementation, which is why
some identifier syntax needs be passed along with an array to get this
to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to
use one that was familiar to other developers, so I chose the one that
Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com[1]

__
You can't use * as suggested, because that conflicts with * as a
prefix operator.

You could probably get away with using a trailing ... in the
call, as in

func foo(xs: [Int]...) { bar(xs...) }

I also wonder whether there's a reason Swift doesn't already
support this. Maybe there's some desired future change to variadic
arguments to improve performance (i.e. to avoid having to allocate
an array) that wouldn't be compatible? Although I'm not sure what
that would be. Maybe there's actually no barrier at all and it was
just something that wasn't prioritized. It would be good to know
either way.

-Kevin

Hi,

I have put together a proposal for passing arrays to variadic
functions (along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52 Bug
Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com[2]

_________________________________________________
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

Links:

  1. http://sabintsev.com/
  2. http://sabintsev.com

···

On Fri, Dec 11, 2015, at 11:12 PM, Arthur Ariel Sabintsev wrote:

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution <swift- > evolution@swift.org> wrote:

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift- >> evolution wrote:


(Arthur Ariel Sabintsev) #7

Just to make sure we're on the same page, your suggestion is the following:

// Declaration
func myFunc(v: T...) {}

// Call
myFunc([T]...)

That seems quite ugly, no? I'd prefer the use of a keyword at that point,
kind of like we have @autoclosure, but instead of declaring the keyword in
the function definition, we use the keyword (e.g., @variadic) in the
function call.

Thoughts on that?

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

···

On Sat, Dec 12, 2015 at 2:15 AM, Kevin Ballard <kevin@sb.org> wrote:

Using any operator at all is a problem. The only way that can work without
conflict is if the operator returns some special type that is known to the
compiler to represent a variadic call, but that doesn't seem like a great
idea. What do you think of the ... suffix? That can't conflict with
operators, and it matches the variadic function declaration.

-Kevin

On Fri, Dec 11, 2015, at 11:12 PM, Arthur Ariel Sabintsev wrote:

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows
a type T to be a anything, including arrays. The example that was used to
delineate this concept to me was that of "Any...", which from within the
variadic function, could be interpreted as T or [T]. The result is
ambiguous in the current implementation, which is why some identifier
syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use
one that was familiar to other developers, so I chose the one that Ruby
uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

You can't use * as suggested, because that conflicts with * as a prefix
operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
    bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this.
Maybe there's some desired future change to variadic arguments to improve
performance (i.e. to avoid having to allocate an array) that wouldn't be
compatible? Although I'm not sure what that would be. Maybe there's
actually no barrier at all and it was just something that wasn't
prioritized. It would be good to know either way.

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution > wrote:

Hi,

I have put together a proposal for passing arrays to variadic functions
(along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com <http://sabintsev.com>

*_______________________________________________*
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


(David Waite) #8

I was thinking of a second type (VariadicArray<T>, perhaps) which you received in the body of your function instead of Array, and which was not supported via … sequence (so no args: VariadicArray<T>… allowed). However, it looks like this type would have to be fixed, not allowing for protocol compliance (including Any)

Would it be possible to automatically take a function which uses T… and generate the [T] argument version of the function? Today, I just implement the [T] variant and have the T… function do nothing but call it.

-DW

···

On Dec 12, 2015, at 12:12 AM, Arthur Ariel Sabintsev via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows a type T to be a anything, including arrays. The example that was used to delineate this concept to me was that of "Any...", which from within the variadic function, could be interpreted as T or [T]. The result is ambiguous in the current implementation, which is why some identifier syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use one that was familiar to other developers, so I chose the one that Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?


(Andrey Tarantsov) #9

To summarize, there are 4 options used by other languages:

func(*args) — Ruby & Python

func(args...) — Go

func(...args) — CoffeeScript, I believe (it's been a while)

func(args) — C# does this, I think, and probably some others

I personally believe that:

1. passing an array as a variadic argument list definitely requires some annotation at the call site

2. func(args...) looks awesome and matches the existing declaration syntax, so it should be a clear winner in Swift.

A.


(Lily Ballard) #10

It would actually be pretty neat if we could do this not as a special case of variadic arguments, but as a general case of passing an array to any function. If the array never escapes the function call and it only reads from it, then having the array point to the stack frame of the caller would be great. Even mutation that doesn't increase the array size could be done in-place on the stack. Any action that might cause the array to escape the function (or any mutation that increases its size) could then move it to the heap. Mutation doesn't even need special handling; since the capacity is equal to the size, it'll move to the heap on any size increase automatically (and size decreases is fine, as the array will manage destruction of the elements). All the compiler really has to do is insert a call that forces a move-to-heap prior to any action that may cause the array to escape the function (e.g. assigning to a property, or capturing in an escaping closure).

If that function call is sufficiently cheap (and it should basically just be testing a boolean flag), then hopefully it's cheap enough to use everywhere for arrays passed as parameters to functions. If it turns out to actually be an issue then maybe we could use an attribute to annotate array parameters that allow for stack allocation, but that would mean it's used pretty rarely as people will usually not bother (except when it's automatically added for varargs).

-Kevin Ballard

···

On Sun, Dec 13, 2015, at 03:00 PM, Chris Lattner wrote:

> On Dec 11, 2015, at 10:54 PM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
>
> You can't use * as suggested, because that conflicts with * as a prefix operator.
>
> You could probably get away with using a trailing ... in the call, as in
>
> func foo(xs: [Int]...) {
> bar(xs...)
> }
>
> I also wonder whether there's a reason Swift doesn't already support this. Maybe there's some desired future change to variadic arguments to improve performance (i.e. to avoid having to allocate an array) that wouldn't be compatible? Although I'm not sure what that would be. Maybe there's actually no barrier at all and it was just something that wasn't prioritized. It would be good to know either way.

This hasn’t been done just because it has never risen to the highest priority, not because we’re against it. In addition to Joe Groff’s downstream comment, one change we need to discuss: requiring the formation of a formal Array is somewhat unfortunate from a performance perspective. It would make more sense to pass varargs by allocating the array as a fixed size buffer in the callers stack, and passing a pointer+length down to the callee. The callee could then wrap that up and vend it with a formal type of Array or perhaps just SequenceType. In the case where the sequence doesn’t escape, this would allow the allocation/deallocation + copy to the heap to be eliminated.


(Lily Ballard) #11

Yeah, except you'd never actually write myFunc([T]...), because you can
just say myFunc(T) in that case. So it would really be used like
myFunc(args...)

@autoclosure isn't a keyword, it's an attribute. AFAIK we have no
precedence for attributes on expressions, only on declarations. And I'd
think we'd need to have a really compelling reason to start putting
attributes on expressions, since that's kind of weird.

-Kevin

···

On Fri, Dec 11, 2015, at 11:20 PM, Arthur Ariel Sabintsev wrote:

Just to make sure we're on the same page, your suggestion is the
following:

// Declaration func myFunc(v: T...) {}

// Call myFunc([T]...)

That seems quite ugly, no? I'd prefer the use of a keyword at that
point, kind of like we have @autoclosure, but instead of declaring the
keyword in the function definition, we use the keyword (e.g.,
@variadic) in the function call.

Thoughts on that?

Best,

Arthur / Sabintsev.com[1]

On Sat, Dec 12, 2015 at 2:15 AM, Kevin Ballard <kevin@sb.org> wrote:

__
Using any operator at all is a problem. The only way that can work
without conflict is if the operator returns some special type that is
known to the compiler to represent a variadic call, but that doesn't
seem like a great idea. What do you think of the ... suffix? That
can't conflict with operators, and it matches the variadic function
declaration.

-Kevin

On Fri, Dec 11, 2015, at 11:12 PM, Arthur Ariel Sabintsev wrote:

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it
allows a type T to be a anything, including arrays. The example that
was used to delineate this concept to me was that of "Any...", which
from within the variadic function, could be interpreted as T or [T].
The result is ambiguous in the current implementation, which is why
some identifier syntax needs be passed along with an array to get
this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to
use one that was familiar to other developers, so I chose the one
that Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com[2]

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution >>> <swift-evolution@swift.org> wrote:

__
You can't use * as suggested, because that conflicts with * as a
prefix operator.

You could probably get away with using a trailing ... in the call,
as in

func foo(xs: [Int]...) { bar(xs...) }

I also wonder whether there's a reason Swift doesn't already
support this. Maybe there's some desired future change to variadic
arguments to improve performance (i.e. to avoid having to allocate
an array) that wouldn't be compatible? Although I'm not sure what
that would be. Maybe there's actually no barrier at all and it was
just something that wasn't prioritized. It would be good to know
either way.

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift- >>>> evolution wrote:

Hi,

I have put together a proposal for passing arrays to variadic
functions (along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52 Bug
Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com[3]

_________________________________________________
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

Links:

  1. http://sabintsev.com/
  2. http://sabintsev.com/
  3. http://sabintsev.com


(Marc Knaup) #12

Why not omit the operator:

myFunc([T])

and only require it to disambiguate cases like "Any...":

myFunc([T]...)

Ambiguous cases should be rather uncommon anyway.

···

On Sat, Dec 12, 2015 at 9:03 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

Yeah, except you'd never actually write myFunc([T]...), because you can
just say myFunc(T) in that case. So it would really be used like
myFunc(args...)

@autoclosure isn't a keyword, it's an attribute. AFAIK we have no
precedence for attributes on expressions, only on declarations. And I'd
think we'd need to have a really compelling reason to start putting
attributes on expressions, since that's kind of weird.

-Kevin

On Fri, Dec 11, 2015, at 11:20 PM, Arthur Ariel Sabintsev wrote:

Just to make sure we're on the same page, your suggestion is the following:

// Declaration
func myFunc(v: T...) {}

// Call
myFunc([T]...)

That seems quite ugly, no? I'd prefer the use of a keyword at that point,
kind of like we have @autoclosure, but instead of declaring the keyword in
the function definition, we use the keyword (e.g., @variadic) in the
function call.

Thoughts on that?

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

On Sat, Dec 12, 2015 at 2:15 AM, Kevin Ballard <kevin@sb.org> wrote:

Using any operator at all is a problem. The only way that can work without
conflict is if the operator returns some special type that is known to the
compiler to represent a variadic call, but that doesn't seem like a great
idea. What do you think of the ... suffix? That can't conflict with
operators, and it matches the variadic function declaration.

-Kevin

On Fri, Dec 11, 2015, at 11:12 PM, Arthur Ariel Sabintsev wrote:

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows
a type T to be a anything, including arrays. The example that was used to
delineate this concept to me was that of "Any...", which from within the
variadic function, could be interpreted as T or [T]. The result is
ambiguous in the current implementation, which is why some identifier
syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use
one that was familiar to other developers, so I chose the one that Ruby
uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com <http://sabintsev.com/>

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution < > swift-evolution@swift.org> wrote:

You can't use * as suggested, because that conflicts with * as a prefix
operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
    bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this.
Maybe there's some desired future change to variadic arguments to improve
performance (i.e. to avoid having to allocate an array) that wouldn't be
compatible? Although I'm not sure what that would be. Maybe there's
actually no barrier at all and it was just something that wasn't
prioritized. It would be good to know either way.

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution > wrote:

Hi,

I have put together a proposal for passing arrays to variadic functions
(along the lines of Ruby’s Splay operator).

Proposal:
https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this
proposal

Best,

Arthur / Sabintsev.com <http://sabintsev.com>

*_______________________________________________*
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

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


(Arthur Ariel Sabintsev) #13

Hmm, a VariadicArray<T> sounds like an interesting solution, however, it would break existing implementation. I’m trying to avoid having to do that.

I think the previously discussed solutions, and/or your proposal for strict-typing (e.g., disallowing Any), are steps in the right direction, as they introduce new functionality without breaking existing implementations.

Just my 0.02.

Best,

Arthur / Sabintsev.com

···

On December 12, 2015 at 2:39:26 PM, David Waite (david@alkaline-solutions.com) wrote:

On Dec 12, 2015, at 12:12 AM, Arthur Ariel Sabintsev via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows a type T to be a anything, including arrays. The example that was used to delineate this concept to me was that of "Any...", which from within the variadic function, could be interpreted as T or [T]. The result is ambiguous in the current implementation, which is why some identifier syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use one that was familiar to other developers, so I chose the one that Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?

I was thinking of a second type (VariadicArray<T>, perhaps) which you received in the body of your function instead of Array, and which was not supported via … sequence (so no args: VariadicArray<T>… allowed). However, it looks like this type would have to be fixed, not allowing for protocol compliance (including Any)

Would it be possible to automatically take a function which uses T… and generate the [T] argument version of the function? Today, I just implement the [T] variant and have the T… function do nothing but call it.

-DW


(Arthur Ariel Sabintsev) #14

I think it’s a postfix in coffeescript.

Anyway, I’m with you on both points! As much as I think func(args…) is ugly, it works, and fits the current paradigm.

Best,

Arthur / Sabintsev.com

···

On December 12, 2015 at 3:25:48 PM, Andrey Tarantsov via swift-evolution (swift-evolution@swift.org) wrote:

To summarize, there are 4 options used by other languages:

func(*args) — Ruby & Python

func(args...) — Go

func(...args) — CoffeeScript, I believe (it's been a while)

func(args) — C# does this, I think, and probably some others

I personally believe that:

1. passing an array as a variadic argument list definitely requires some annotation at the call site

2. func(args...) looks awesome and matches the existing declaration syntax, so it should be a clear winner in Swift.

A.

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


(Chris Lattner) #15

Yes, there is some form of this that could be possible to do. The trick with varargs though is that the caller always (at least without the “forwarding” case) knows the number of arguments that will be passed. This means that the buffer allocated on the stack in the caller is a fixed and obviously known size, which is usually very small.

A similar optimization could be done when passing down an array literal, but the calling convention would have to support passing down an array value obtained in other ways (e.g. passed down to the caller or loaded from memory). In that case, it is hard to beat the performance of a single retain: no copy of the elements are implied. Doing the optimization for general unknown number of elements also isn’t profitable in general because the stack is a very finite resource, so you’d have to handle the “large” case somehow.

-Chris

···

On Dec 13, 2015, at 3:53 PM, Kevin Ballard <kevin@sb.org> wrote:

This hasn’t been done just because it has never risen to the highest priority, not because we’re against it. In addition to Joe Groff’s downstream comment, one change we need to discuss: requiring the formation of a formal Array is somewhat unfortunate from a performance perspective. It would make more sense to pass varargs by allocating the array as a fixed size buffer in the callers stack, and passing a pointer+length down to the callee. The callee could then wrap that up and vend it with a formal type of Array or perhaps just SequenceType. In the case where the sequence doesn’t escape, this would allow the allocation/deallocation + copy to the heap to be eliminated.

It would actually be pretty neat if we could do this not as a special case of variadic arguments, but as a general case of passing an array to any function. If the array never escapes the function call and it only reads from it, then having the array point to the stack frame of the caller would be great. Even mutation that doesn't increase the array size could be done in-place on the stack. Any action that might cause the array to escape the function (or any mutation that increases its size) could then move it to the heap. Mutation doesn't even need special handling; since the capacity is equal to the size, it'll move to the heap on any size increase automatically (and size decreases is fine, as the array will manage destruction of the elements). All the compiler really has to do is insert a call that forces a move-to-heap prior to any action that may cause the array to escape the function (e.g. assigning to a property, or capturing in an escaping closure).

If that function call is sufficiently cheap (and it should basically just be testing a boolean flag), then hopefully it's cheap enough to use everywhere for arrays passed as parameters to functions. If it turns out to actually be an issue then maybe we could use an attribute to annotate array parameters that allow for stack allocation, but that would mean it's used pretty rarely as people will usually not bother (except when it's automatically added for varargs).


(Arthur Ariel Sabintsev) #16

You’re right, it’s an attribute. My mistake.

I’d actually prefer to only have to use an operator when the compiler throws a warning stating that the input is ambiguous, as is the case with Any, [Any], AnyObject, and [AnyObject].

Best,

Arthur / Sabintsev.com

···

On December 12, 2015 at 5:44:16 AM, Marc Knaup (marc@knaup.koeln) wrote:

Why not omit the operator:

myFunc([T])

and only require it to disambiguate cases like "Any...":

myFunc([T]...)

Ambiguous cases should be rather uncommon anyway.

On Sat, Dec 12, 2015 at 9:03 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
Yeah, except you'd never actually write myFunc([T]...), because you can just say myFunc(T) in that case. So it would really be used like myFunc(args...)

@autoclosure isn't a keyword, it's an attribute. AFAIK we have no precedence for attributes on expressions, only on declarations. And I'd think we'd need to have a really compelling reason to start putting attributes on expressions, since that's kind of weird.

-Kevin

On Fri, Dec 11, 2015, at 11:20 PM, Arthur Ariel Sabintsev wrote:
Just to make sure we're on the same page, your suggestion is the following:

// Declaration
func myFunc(v: T...) {}

// Call
myFunc([T]...)

That seems quite ugly, no? I'd prefer the use of a keyword at that point, kind of like we have @autoclosure, but instead of declaring the keyword in the function definition, we use the keyword (e.g., @variadic) in the function call.

Thoughts on that?

Best,

Arthur / Sabintsev.com

On Sat, Dec 12, 2015 at 2:15 AM, Kevin Ballard <kevin@sb.org> wrote:

Using any operator at all is a problem. The only way that can work without conflict is if the operator returns some special type that is known to the compiler to represent a variadic call, but that doesn't seem like a great idea. What do you think of the ... suffix? That can't conflict with operators, and it matches the variadic function declaration.

-Kevin

On Fri, Dec 11, 2015, at 11:12 PM, Arthur Ariel Sabintsev wrote:
Thanks for your response.

The reason this doesn't work is due to Swift's generic system as it allows a type T to be a anything, including arrays. The example that was used to delineate this concept to me was that of "Any...", which from within the variadic function, could be interpreted as T or [T]. The result is ambiguous in the current implementation, which is why some identifier syntax needs be passed along with an array to get this to work properly.

As for the choice of operator, I'm open to suggestions. I wanted to use one that was familiar to other developers, so I chose the one that Ruby uses.

Maybe a ~ would be better, or a keyword like @variadic?

Best,

Arthur / Sabintsev.com

On Sat, Dec 12, 2015 at 1:54 AM, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

You can't use * as suggested, because that conflicts with * as a prefix operator.

You could probably get away with using a trailing ... in the call, as in

func foo(xs: [Int]...) {
bar(xs...)
}

I also wonder whether there's a reason Swift doesn't already support this. Maybe there's some desired future change to variadic arguments to improve performance (i.e. to avoid having to allocate an array) that wouldn't be compatible? Although I'm not sure what that would be. Maybe there's actually no barrier at all and it was just something that wasn't prioritized. It would be good to know either way.

-Kevin

On Fri, Dec 11, 2015, at 10:43 PM, Arthur Sabintsev via swift-evolution wrote:
Hi,

I have put together a proposal for passing arrays to variadic functions (along the lines of Ruby’s Splay operator).

Proposal: https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md
Pull Request: https://github.com/apple/swift-evolution/pull/52
Bug Report: https://bugs.swift.org/browse/SR-128 which led to this proposal

Best,

Arthur / Sabintsev.com

_______________________________________________
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

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


(Lily Ballard) #17

Oh sure, when I said doing it in the general case, I just meant for arrays created from array literals (or possibly even arrays constructed using .append(), if the optimizer really wants to go ahead and figure out how many elements the resulting array has). And I was thinking that the calling convention is just pass an array, and instead Array will have the flag internally in its native storage that says "my buffer is on the stack" (as well as having a special method to construct an array from a stack buffer that the compiler will use for array literals / varargs). The biggest complication is having the compiler insert calls to array.moveToHeapIfCurrentlyOnStack() whenever an array parameter escapes the function (arrays returned from functions can't be on the stack, and arrays created in the function can skip the stack if it's known to escape), and the question there is whether this call will have meaningful performance implications.

-Kevin Ballard

···

On Sun, Dec 13, 2015, at 07:42 PM, Chris Lattner wrote:

On Dec 13, 2015, at 3:53 PM, Kevin Ballard <kevin@sb.org> wrote:
>> This hasn’t been done just because it has never risen to the highest priority, not because we’re against it. In addition to Joe Groff’s downstream comment, one change we need to discuss: requiring the formation of a formal Array is somewhat unfortunate from a performance perspective. It would make more sense to pass varargs by allocating the array as a fixed size buffer in the callers stack, and passing a pointer+length down to the callee. The callee could then wrap that up and vend it with a formal type of Array or perhaps just SequenceType. In the case where the sequence doesn’t escape, this would allow the allocation/deallocation + copy to the heap to be eliminated.
>
> It would actually be pretty neat if we could do this not as a special case of variadic arguments, but as a general case of passing an array to any function. If the array never escapes the function call and it only reads from it, then having the array point to the stack frame of the caller would be great. Even mutation that doesn't increase the array size could be done in-place on the stack. Any action that might cause the array to escape the function (or any mutation that increases its size) could then move it to the heap. Mutation doesn't even need special handling; since the capacity is equal to the size, it'll move to the heap on any size increase automatically (and size decreases is fine, as the array will manage destruction of the elements). All the compiler really has to do is insert a call that forces a move-to-heap prior to any action that may cause the array to escape the function (e.g. assigning to a property, or capturing in an escaping closure).
>
> If that function call is sufficiently cheap (and it should basically just be testing a boolean flag), then hopefully it's cheap enough to use everywhere for arrays passed as parameters to functions. If it turns out to actually be an issue then maybe we could use an attribute to annotate array parameters that allow for stack allocation, but that would mean it's used pretty rarely as people will usually not bother (except when it's automatically added for varargs).

Yes, there is some form of this that could be possible to do. The trick with varargs though is that the caller always (at least without the “forwarding” case) knows the number of arguments that will be passed. This means that the buffer allocated on the stack in the caller is a fixed and obviously known size, which is usually very small.

A similar optimization could be done when passing down an array literal, but the calling convention would have to support passing down an array value obtained in other ways (e.g. passed down to the caller or loaded from memory). In that case, it is hard to beat the performance of a single retain: no copy of the elements are implied. Doing the optimization for general unknown number of elements also isn’t profitable in general because the stack is a very finite resource, so you’d have to handle the “large” case somehow.


(Chris Lattner) #18

Ah, I understand the model you’re envisioning.

We’re *extremely* reticent to make the hot paths of Array any slow (things like subscript for example), particularly when you may be talking about [Float] or [Int], because those are types that are important to get vectorized. Right now, the only slowdown vs C that you get on those hot paths on arrays of structs are the array bounds check, and that check is often hoisted out of loops.

-Chris

···

On Dec 14, 2015, at 1:13 PM, Kevin Ballard <kevin@sb.org> wrote:

A similar optimization could be done when passing down an array literal, but the calling convention would have to support passing down an array value obtained in other ways (e.g. passed down to the caller or loaded from memory). In that case, it is hard to beat the performance of a single retain: no copy of the elements are implied. Doing the optimization for general unknown number of elements also isn’t profitable in general because the stack is a very finite resource, so you’d have to handle the “large” case somehow.

Oh sure, when I said doing it in the general case, I just meant for arrays created from array literals (or possibly even arrays constructed using .append(), if the optimizer really wants to go ahead and figure out how many elements the resulting array has). And I was thinking that the calling convention is just pass an array, and instead Array will have the flag internally in its native storage that says "my buffer is on the stack" (as well as having a special method to construct an array from a stack buffer that the compiler will use for array literals / varargs). The biggest complication is having the compiler insert calls to array.moveToHeapIfCurrentlyOnStack() whenever an array parameter escapes the function (arrays returned from functions can't be on the stack, and arrays created in the function can skip the stack if it's known to escape), and the question there is whether this call will have meaningful performance implications.