Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-128] Pass array to variadic function #42750

Open
swift-ci opened this issue Dec 7, 2015 · 23 comments
Open

[SR-128] Pass array to variadic function #42750

swift-ci opened this issue Dec 7, 2015 · 23 comments

Comments

@swift-ci
Copy link
Collaborator

@swift-ci swift-ci commented Dec 7, 2015

Previous ID SR-128
Radar rdar://problem/12134482
Original Reporter arthur (JIRA User)
Type Improvement
Additional Detail from JIRA
Votes 39
Component/s Compiler
Labels Improvement, LanguageFeatureRequest
Assignee None
Priority Medium

md5: fe683ee214998d48375c8fbaf79fbfbc

is duplicated by:

  • SR-1644 Issue with varargs
  • SR-4386 variadic parameters are interpreted as single argument when passed on
  • SR-9733 No Syntax for Array Splat

relates to:

  • SR-8047 compiler should warn when implicitly casting Array to CVarArg

Issue Description:

func a(p: Int...) {
    p is [Int] // true
}

a(1, 2, 3) // Works
a([1, 2, 3]) // error: cannot invoke 'a' with an argument list of type '([Int])'

I want to pass an array into a function that accepts a variadic argument, as within the scope of the function, the argument p, is treated as an array.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Dec 8, 2015

This is a very early request for Swift, though its original form is "can't forward variadic arguments". :-) Most of our thoughts around this suggest using either *array or array... to make the expansion explicit.

As this is a language change, it will need to go through the Swift evolution process.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 8, 2015

Comment by Arthur Sabintsev (JIRA)

Should I put together a Swift Evo proposal?

@belkadan
Copy link
Contributor

@belkadan belkadan commented Dec 8, 2015

Sure, that'd be great!

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 8, 2015

Comment by Radek Pietruszewski (JIRA)

arthur (JIRA User) FWIW, passing an array directly is a problem because it could be ambiguous in some cases when the argument is an array. Ruby solves it by using the same syntax as defining a var-arg: `*arr` — which deconstructs an array into a series of arguments. In Swift, `arr...` would do the job.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 8, 2015

Comment by Arthur Sabintsev (JIRA)

Well, it shouldn't be ambiguous if the function is expecting `Int...`. It should be able to differentiate between an array of Ints, `[1,2,3]`, or a variable length of Ints `1,2,3` that can be turned into an array.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 8, 2015

Comment by Radek Pietruszewski (JIRA)

No, but consider what would happen if the arg type was `[Any]...`

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 10, 2015

Comment by Arthur Sabintsev (JIRA)

Unless I'm missing something, I think the following should/would happen:

For Any...,

  1. Accept a single array of Any, so [Any], or

  2. Convert a variadic list, such as 1, 2.0, "Three" into an an array of [Any] within the scope of the function.

Both yield an array of [Any].

For [Any]...,

  1. Accept a single array of [Any], so [[Any]], or

  2. Convert a variadic list, such as 1, 2.0, "Three", to three arrays of [Any], each with one element.

Both yield an array of [[Any]].

If I'm wrong, please correct me.

@belkadan
Copy link
Contributor

@belkadan belkadan commented Dec 10, 2015

Radek is right, that first case is ambiguous.

func foo(args: Any...) {
  print(args[0])
}

func test() {
  let arr: [Any] = [1, 2, "three"]
  foo(arr)
}

Does this print 1, or [1, 2, "three"]? That's why I said we'd want to make the expansion explicit.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 10, 2015

Comment by Arthur Sabintsev (JIRA)

Ah, ok. I see the error in my thought process.

@belkadan Do you think this is something worth bringing up to the community as a whole?

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 12, 2015

Comment by Arthur Sabintsev (JIRA)

Proposal posted: apple/swift-evolution#52

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 12, 2015

Comment by Kostiantyn Koval (JIRA)

The use-case from real project for me was that I wanted to pass a variadic parameter further.
Now it's passed as a single Array element

func format(params: Int ...) {
  print(params) 
  // params are passed as a [Int]. The print function will receive only 1 argument an [Int]
}

I like the proposal. The mentioned overloading solution is not always possible.
The problem is that print is a function defined in other module and I can't overload it.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Dec 12, 2015

Comment by Arthur Sabintsev (JIRA)

Ah, so it's even more dire for you.

For me, overloading worked, but it added unnecessary code:

https://github.com/ArtSabintsev/Zephyr/blob/79423255489021cfe7dd9ec38c5ce9030a97ae01/Zephyr.swift#L140-L195

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Jun 7, 2017

Comment by Raphael (JIRA)

Any movement here?

For the life of me, I can't figure out how to map a dictionary and get another one. Mapping a dictionary gives an array of key-value pairs (why?!) which I can't feed to any intializer of `Dictionary`.

@ffried
Copy link
Contributor

@ffried ffried commented Jun 7, 2017

Reitzig (JIRA User) The problem you're describing isn't directly related to this SR. It's something Swift 4 solves with mapValues. In Swift 3 you could re-implement it like this:

extension Dictionary {
    func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> Dictionary<Key, T> { 
        return try reduce([:]) {
            var newDict = $0
            newDict[$1.key] = try transform($1.value)
            return newDict
        }
    }
}

Also, in Swift 4 there's a new initializer on Dictionary, that allows passing keys and values: Dictionary(uniqueKeysWithValues: ))

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Jun 7, 2017

Comment by Mark Lewis (JIRA)

arthur (JIRA User) wrote:

Ah, so it's even more dire for you.

For me, overloading worked, but it added unnecessary code:

https://github.com/ArtSabintsev/Zephyr/blob/79423255489021cfe7dd9ec38c5ce9030a97ae01/Zephyr.swift#L140-L195

I know this comment is a bit dated, but if I found it someone else might too.

There's no need to duplicate your code in the overload like that. You could have made the variadic function that takes String... the overload and just call into the function that takes the [String] like this:

https://gist.github.com/ThuggishNuggets/36674089d7d6ce2d00c007e32c332d9b

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Jun 8, 2017

Comment by Arthur Sabintsev (JIRA)

ThuggishNuggets (JIRA User) - Good on you for following up.

I actually did exactly that a while back, as it was the most sensible solution. For reference: https://github.com/ArtSabintsev/Zephyr/blob/master/Sources/Zephyr.swift#L79-L91

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Jun 8, 2017

Comment by Raphael (JIRA)

@ffried Thanks! I should have phrase my comment more carefully. Of course it's possible to write a function that does what I want; I meant how to do it using existing standard API. It seems as if the `dictionaryLiteral` initializer should work, but it doesn't due to this restriction (and maybe a couple of poor API design choices).

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Nov 14, 2017

Comment by Tadeas Kriz (JIRA)

It seems that this makes overriding a method with variadic parameters impossible as we can't pass it down. Any ideas on how to workaround this without changing the superclass itself? Thanks.

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Sep 17, 2018

Comment by Jakub Jelínek (JIRA)

Hi guys. I would appreciate Java-like style, so having an array p I would like to call the method above like this: a(...p)

Edit: I see you made the proposal already 🙂

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Nov 20, 2018

Comment by Ky (JIRA)

is there a Swift Evolution proposal for this yet?

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Nov 20, 2018

Comment by Arthur Sabintsev (JIRA)

I made one years ago - it went nowhere: https://github.com/ArtSabintsev/swift-evolution/blob/master/proposals/array-variadic-function.md

@swift-ci
Copy link
Collaborator Author

@swift-ci swift-ci commented Nov 7, 2019

Comment by Maarten Billemont (JIRA)

Without a resolution for this issue, there is essentially no way to invoke external API that exposes only variadic functions in a non-literal manner.

As an example, consider `os_log`, which takes a `StaticString` format string and a `CVarArg...` argument list but exposes no API to pass arguments in a non-literal manner, making OS Logging all but unavailable for programmatic logging (eg. from third-party loggers).

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants