[swift-evolution] Splat

Jonathan Tang jonathan.d.tang at gmail.com
Wed Feb 10 23:34:58 CST 2016


On Wed, Feb 10, 2016 at 9:10 PM, Jonathan Tang <jonathan.d.tang at gmail.com>
wrote:

>
>
> On Wed, Feb 10, 2016 at 8:37 PM, Brent Royal-Gordon <
> brent at architechies.com> wrote:
>
>> > Ah, I see, at least for #1.  I'm assuming that to get the function that
>> takes a tuple, you'd have to explicitly use the parameters: overload:
>>
>> Probably; otherwise this doesn't actually save the type checker much.
>>
>> There's a bit of a complication when talking about type signatures.
>> Currently, the fact that a function operates on a tuple is embedded in our
>> type syntax, and the parentheses around the argument list are optional and
>> represent a tuple—`(Int, String) -> String` is equivalent to
>> `TupleOfIntAndString -> String`. This might now need a change, or at least
>> a clarification. For the purposes of this post, I'll say that `(Int,
>> String) -> String` is the type of a function taking an Int and String,
>> while `((Int, String)) -> String` is the type of a function taking a tuple.
>>
>> For the `concatenate(_:to:)` example, the original function has a
>> signature of `(Int, String) -> String` (once argument labels are fully part
>> of the name, which seems to be the trend). For each of the syntaxes I
>> proposed, the type of this expression is `((Int, String)) -> String`:
>>
>>         concatenate(parameters:)
>>         concatenate(_:to:).apply(to:)
>>         concatenate(_:to: *)
>>
>> > let f = concatenate(_:to:).apply(to:)  // I assume this has type (Int,
>> String) -> String
>> > var g = f.apply(to:)  // Is this legal?  What's the type?
>>
>> That's an interesting question. Swift does not have 1-tuples, so unary
>> functions might not have an `apply(to:)` method.
>>
>> > // This type signature is wrong, g takes an arbitrary argument
>> list...but how do I write that?
>> > func compose<Params, Intermediate, Result>(f: Intermediate -> Result ,
>> g: Params -> Intermediate) -> Result {
>> >   return { args in f.apply(to: g.apply(to: args)) }
>> > }
>>
>> Without something along the lines of the `@splatting` annotation in a
>> parameter list, it would not be possible to write a `compose(_:_:)`
>> function which could operate on non-unary functions directly. Instead of
>> writing `compose(f, g)`, you'd have to write `compose(f.apply, g.apply)`.
>>
>> If you think that's kind of ugly, I don't disagree.
>>
>> > // Here's a function that takes an Array of functions and applies them
>> one after another...
>> > // I don't really know where to start with this...the types of all of
>> its arguments seem inexpressible
>> > func composeMany</* ...arbitrary many intermediate params...
>> */>(functions: [/* What type for arbitrary function? */]) {
>> >   reduce(compose, functions, identity)
>> > }
>>
>> Even with the current implicit splatting, this is impossible to express
>> unless all functions have the same argument and return type. The generics
>> system is simply not equipped to handle arbitrarily many generic types.
>>
>> > But then if it looks like a method, people are going to wonder why they
>> can't pass it to a function or store it in a data-structure.
>>
>> I'm not sure why you think it can't be stored. I think it can.
>>
>>
> Not the result of the splatting operation, but the splatting operation
> itself:
>
> let legal = [f, g)
> let illegal = [f.apply, g.apply]   // But it certainly looks like an
> expression that would normally be legal!
>
>

Actually nevermind, I see that a list of [f.apply, g.apply] would be legal
(modulo the to: keyword) and has the expected semantics, assuming that the
types match up.

Still find it quite confusing, because I expected x.methodName to be a
bound method and here it's a special syntactic form.  What happens if a
protocol defines "func apply(to:)"?  Is that legal?  Would function types
automatically conform to the protocol?


> > I saw that, briefly.  I hope that something like that makes it in, but
>> it has similar issues with "What's the type signature of this operator, and
>> if the only way it can exist is as compiler-supported syntax, how do we
>> make it clear to users that this is a compiler language feature and not a
>> first-class function call?"  IIRC the syntax suggested there made use of
>> the #thisIsACompilerDirective naming convention.  I wonder if that might be
>> appropriate here:
>> >
>> > f(#splat(tuple))
>> > concatenate(_: #splat)
>> >
>> > The latter form also suggests a way this could be used for partial
>> application:
>> >
>> > concatenate(2, #splat) returns a closure where the first argument is
>> always 2, but any remaining arguments are pulled from the provided tuple.
>>
>> I don't really think # is a good fit here. I think it's best used in
>> places where it's performing a relatively straightforward textual
>> substitution; in the `concatenate(_: #splat)` case, that's not really
>> what's being done here.
>>
>> (To tell the truth, in my head I imagine that when we finally get a macro
>> system in Swift N, all macros are marked by a leading #. So I tend to get a
>> little squeamish about # uses which draw heavily from context instead of
>> relying on what you "pass" to them.)
>>
>>
>>
> The "..." syntax that's been batted around here looks fine to me as well.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160210/f6142847/attachment.html>


More information about the swift-evolution mailing list