[swift-evolution] Proposal: Remove implicit tuple splat behavior from function applications

James Campbell james at supmenow.com
Wed Jan 27 13:30:21 CST 2016


https://github.com/thoughtbot/Argo

This is the example from their github page

import Argoimport Curry
struct User {
  let id: Int
  let name: String
  let email: String?
  let role: Role
  let companyName: String
  let friends: [User]
}
extension User: Decodable {
  static func decode(j: JSON) -> Decoded<User> {
    return curry(User.init)
      <^> j <| "id"
      <*> j <| "name"
      <*> j <|? "email" // Use ? for parsing optional values
      <*> j <| "role" // Custom types that also conform to Decodable just work
      <*> j <| ["company", "name"] // Parse nested objects
      <*> j <|| "friends" // parse arrays of objects
  }
}
// Wherever you receive JSON data:
let json: AnyObject? = try?
NSJSONSerialization.JSONObjectWithData(data, options: [])
if let j: AnyObject = json {
  let user: User? = decode(j)
}

On second reading it seems to use currying


public func curry<A, B>(function: (A) -> B) -> A -> B {
    return { `a` in function(`a`) }
}
public func curry<A, B, C>(function: (A, B) -> C) -> A -> B -> C {
    return { `a` in { `b` in function(`a`, `b`) } }
}
public func curry<A, B, C, D>(function: (A, B, C) -> D) -> A -> B -> C -> D {
    return { `a` in { `b` in { `c` in function(`a`, `b`, `c`) } } }
}
public func curry<A, B, C, D, E>(function: (A, B, C, D) -> E) -> A ->
B -> C -> D -> E {
    return { `a` in { `b` in { `c` in { `d` in function(`a`, `b`, `c`,
`d`) } } } }
}
public func curry<A, B, C, D, E, F>(function: (A, B, C, D, E) -> F) ->
A -> B -> C -> D -> E -> F {
    return { `a` in { `b` in { `c` in { `d` in { `e` in function(`a`,
`b`, `c`, `d`, `e`) } } } } }
}
public func curry<A, B, C, D, E, F, G>(function: (A, B, C, D, E, F) ->
G) -> A -> B -> C -> D -> E -> F -> G {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in
function(`a`, `b`, `c`, `d`, `e`, `f`) } } } } } }
}
public func curry<A, B, C, D, E, F, G, H>(function: (A, B, C, D, E, F,
G) -> H) -> A -> B -> C -> D -> E -> F -> G -> H {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`) } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I>(function: (A, B, C, D, E,
F, G, H) -> I) -> A -> B -> C -> D -> E -> F -> G -> H -> I {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`) } } } } }
} } }
}
public func curry<A, B, C, D, E, F, G, H, I, J>(function: (A, B, C, D,
E, F, G, H, I) -> J) -> A -> B -> C -> D -> E -> F -> G -> H -> I -> J
{
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`,
`i`) } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K>(function: (A, B, C,
D, E, F, G, H, I, J) -> K) -> A -> B -> C -> D -> E -> F -> G -> H ->
I -> J -> K {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in function(`a`, `b`, `c`, `d`, `e`, `f`,
`g`, `h`, `i`, `j`) } } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L>(function: (A, B,
C, D, E, F, G, H, I, J, K) -> L) -> A -> B -> C -> D -> E -> F -> G ->
H -> I -> J -> K -> L {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in function(`a`, `b`, `c`, `d`,
`e`, `f`, `g`, `h`, `i`, `j`, `k`) } } } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M>(function: (A,
B, C, D, E, F, G, H, I, J, K, L) -> M) -> A -> B -> C -> D -> E -> F
-> G -> H -> I -> J -> K -> L -> M {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in function(`a`, `b`,
`c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `k`, `l`) } } } } } } } } } }
} }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N>(function:
(A, B, C, D, E, F, G, H, I, J, K, L, M) -> N) -> A -> B -> C -> D -> E
-> F -> G -> H -> I -> J -> K -> L -> M -> N {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in function(`a`,
`b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `k`, `l`, `m`) } } } } }
} } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N,
O>(function: (A, B, C, D, E, F, G, H, I, J, K, L, M, N) -> O) -> A ->
B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M -> N -> O {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in { `n` in
function(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `k`, `l`,
`m`, `n`) } } } } } } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O,
P>(function: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) -> P) -> A
-> B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M -> N -> O
-> P {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in { `n` in {
`o` in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `k`,
`l`, `m`, `n`, `o`) } } } } } } } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P,
Q>(function: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) -> Q) ->
A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M -> N ->
O -> P -> Q {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in { `n` in {
`o` in { `p` in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`,
`j`, `k`, `l`, `m`, `n`, `o`, `p`) } } } } } } } } } } } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
R>(function: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) -> R)
-> A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M -> N
-> O -> P -> Q -> R {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in { `n` in {
`o` in { `p` in { `q` in function(`a`, `b`, `c`, `d`, `e`, `f`, `g`,
`h`, `i`, `j`, `k`, `l`, `m`, `n`, `o`, `p`, `q`) } } } } } } } } } }
} } } } } } }
}
public func curry<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q,
R, S>(function: (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)
-> S) -> A -> B -> C -> D -> E -> F -> G -> H -> I -> J -> K -> L -> M
-> N -> O -> P -> Q -> R -> S {
    return { `a` in { `b` in { `c` in { `d` in { `e` in { `f` in { `g`
in { `h` in { `i` in { `j` in { `k` in { `l` in { `m` in { `n` in {
`o` in { `p` in { `q` in { `r` in function(`a`, `b`, `c`, `d`, `e`,
`f`, `g`, `h`, `i`, `j`, `k`, `l`, `m`, `n`, `o`, `p`, `q`, `r`) } } }
} } } } } } } } } } } } } } }
}


*___________________________________*

*James⎥Lead Engineer*

*james at supmenow.com <james at supmenow.com>⎥supmenow.com <http://supmenow.com>*

*Sup*

*Runway East *

*10 Finsbury Square*

*London*

* EC2A 1AF *

On Wed, Jan 27, 2016 at 6:15 PM, Chris Lattner <clattner at apple.com> wrote:

>
> On Jan 26, 2016, at 11:41 PM, James Campbell via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I think libraries like argo use it.
>
> In these cases it's for initlizing a struct with data (from Json).
>
>
> Ok, I’m definitely interested in knowing more.  Please include a code
> sample, showing both the declaration being splatted into and the call
> sites.  Otherwise, I can’t tell how much value this feature is adding, and
> what the pain would be if it were removed.  Thanks!
>
> -Chris
>
>
>
> You could argue that these libraries could implement this better but I
> would like to put forward a few questions:
>
> - if we kept this we could instead add an apply function to closures which
> could take a turple, vardaric array or maybe even allowed you to call it by
> specifying the names arguments (in any order you liked)
>
> That way you could do:
>
> Object.foo //returns a normal closure
> Object.foo.apply(turple) //compiler generated sugar function
>
> A lot of libraries including Lenses I think would be simplified by this as
> they would no longer rely on currying or turple splats as heavily. It also
> allows you to apply the value of something easily.
>
> Or we could define a new syntax to call labelled arguments in any order
>
> This would work well with immutable objects as in certain cases I wish to
> mutate a struc like so:
>
> Struct Person
> {
> Let name: String
>
> Init(name: String)
> {
> self.name = name)
> }
> }
>
> Let person = Person(name:"James")
> person.mutate({
> .name = "Bob"
> }) // this special syntax says to the compiler to take this special object
> and to return a new copy of the original struct with the values changed.
>
> Inside of the {} you can directly access the properties you wish to change.
>
> I use the closure syntax but perhaps it could use another one .
>
> This syntax could be used for apply so you can define the parameters you
> care about:
>
> dependency({
> name:"networking",
> git:""
> })
>
> Sent froml my iPhone
>
> On 27 Jan 2016, at 06:38, Jacob Bandes-Storch via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> +1 from me as well. I don't think I've ever used this feature on purpose.
>
> I look forward to future discussions about splatting, especially as they
> relate to variadic functions.
>
> On Tue, Jan 26, 2016 at 10:36 PM, T.J. Usiyan via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> +1
>>
>> I like the feature quite a bit but avoid it as a result of the naming
>> concerns. If removing this feature can help improve the type checker, the
>> trade is worthwhile, IMO.
>>
>> On Wed, Jan 27, 2016 at 1:23 AM, Chris Lattner via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>>> For discussion: comments appreciated!
>>>
>>>
>>>
>>>
>>> Remove implicit tuple splat behavior from function applications
>>>
>>>    - Proposal: SE-
>>>    <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-name.md>
>>>    TBD
>>>    - Author(s): Chris Lattner
>>>    - Status: *Awaiting review*
>>>    - Review manager: TBD
>>>
>>> <https://github.com/apple/swift-evolution#introduction>Introduction
>>>
>>> Function calls (which include several syntactic forms that apply an
>>> argument list to something of function type) currently have a dual nature
>>> in Swift.  Given something like:
>>>
>>> func foo(a : Int, b : Int) {}
>>>
>>>
>>> You can call it either with with the typical syntactic form that passes
>>> arguments to each of its parameters:
>>>
>>> foo(42, b : 17)
>>>
>>> or you can take advantage of a little-known feature to pass an entire
>>> argument list as a single value (of tuple type):
>>> let x = (1, b: 2)
>>> foo(x)
>>>
>>>
>>> This proposal recommends removing the later form, which I affectionately
>>> refer to as the “tuple splat” form.  This feature is purely a sugar
>>> feature, it does not provide any expressive ability beyond passing the
>>> parameters manually.
>>>
>>> Swift-evolution thread: TBD
>>> <https://github.com/apple/swift-evolution#motivation>Motivation
>>>
>>> This behavior is cute, precedented in other functional languages, and
>>> has some advantages, but it also has several major disadvantages, which are
>>> all related to its syntactic form.
>>> * A call to foo(x) looks like a call to an overloaded version of foo,
>>> both to the compiler and to the human who maintains the code.  This is
>>> extremely confusing if you don’t know the feature exists.
>>> * There are real ambiguities in the syntax, e.g. involving Any arguments
>>> and situations where you want to pass a tuple value as a single parameter.
>>> * The current implementation has a ton of implementation bugs - it
>>> doesn’t work reliably.
>>> * The current implementation adds complexity to the type checker,
>>> slowing it down and adding maintenance burden.
>>> * The current implementation doesn’t work the way we would want a tuple
>>> splat operation to work.  For example, arguably, you should be able to call
>>> foo with:
>>>
>>> func bar() -> (Int, Int) { … }
>>> foo(bar())
>>>
>>> … but this is not allowed, since tuple labels are required to line up.
>>> You have to write:
>>>
>>>
>>> func bar() -> (Int, b: Int)  { … }
>>> foo(bar())
>>>
>>>
>>> This makes this feature very difficult to use in practice, because you
>>> have to _’ize a lot of parameters (violating naming conventions), perform
>>> manual shuffling (defeating the sugar benefits of the feature), or add
>>> parameter labels to the result of functions (which leads to odd tying
>>> between callers and callees).
>>>
>>> The root problem here is that we use exactly the same syntax for both
>>> forms of function application.  If the two forms were differentiated (an
>>> option considered in “alternatives considered” below) then some of these
>>> problems would be defined away.
>>>
>>> From a historical perspective, the tuple splat form of function
>>> application dates back to very early Swift design (probably introduced in
>>> 2010, but possibly 2011) where all function application was of a single
>>> value to a function type.  For a large number of reasons (including default
>>> arguments, variadic arguments, labels, etc) we have completely abandoned
>>> this model, but we never came back to reevaluating the tuple splat behavior.
>>>
>>> If we didn’t already have this feature, we would not add it to Swift 3
>>> (at least in its current form).
>>> <https://github.com/apple/swift-evolution#proposed-solution>Proposed
>>> solution
>>>
>>> The proposed solution is simple, we should just remove this feature from
>>> the Swift 3 compiler.  Ideally we would deprecate it in the Swift 2.2
>>> compiler and remove it in Swift 3.  However, if there isn’t time to get the
>>> deprecation into Swift 2.2, the author believes it would be perfectly fine
>>> to just remove it in Swift 3 (with a fixit + migration help of course).
>>>
>>> One of the interesting aspect of this feature is that some of the people
>>> we’ve spoken to are very fond of it.  However, when pressed, they admit
>>> that they are not actually using it widely in their code, or if they
>>> are using it, they are abusing naming conventions (distorting their code)
>>> in order to use it.  This doesn’t seem like a positive contribution - this
>>> seems like a “clever” feature, not a practical one.
>>> <https://github.com/apple/swift-evolution#detailed-design>Detailed
>>> design
>>>
>>> The design is straight-forward.  In the Swift 3 time frame, we continue
>>> to parse and type check these expressions as we have so far, but produce an
>>> error + fixit hint when it is the tuple splat form.  The migrator would
>>> auto-apply the fixit hint as it does for other cases.
>>> <https://github.com/apple/swift-evolution#impact-on-existing-code>Impact
>>> on existing code
>>>
>>> Any code that uses this feature will have to move to the traditional
>>> form.  In the case of the example above, this means rewriting the code from:
>>> foo(x)
>>>
>>> into a form like this:
>>> foo(x.0, x.b)
>>>
>>> In the case where “x” is a complex expression, a temporary variable will
>>> need to be introduced.  We believe that compiler fixits can handle the
>>> simple cases directly and that this extension is not widely used.
>>>
>>> <https://github.com/apple/swift-evolution#alternatives-considered>Alternatives
>>> considered
>>> The major problem with this feature is that it was not well considered
>>> and implemented properly (owing to its very old age, it has just been kept
>>> limping along).  The alternative then is to actually design a proper
>>> feature to support this.  Since the implicitness and syntactic ambiguity
>>> with normal function application is the problem, the solution is to
>>> introduce an explicit syntactic form to represent this.  For example,
>>> something like this could address the problems we have:
>>>
>>> foo(*x)    // NOT a serious syntax proposal
>>>
>>> However, actually designing this feature would be a non-trivial effort
>>> not core to the Swift 3 mission:
>>>
>>> * It is a pure-sugar feature, and therefore low priority.
>>> * We don’t have an obvious sigil to use.  “prefix-star” should be kept
>>> as unused for now in case we want to use it to refer to memory-related
>>> operations in the future.
>>> * Making the tuple splat operation great requires more than just fixing
>>> the syntactic ambiguities we have, it would require re-evaluating the
>>> semantics of the operation (e.g. in light of parameter labels, varargs and
>>> other features).
>>>
>>> If there is serious interest in pursuing this as a concept, we should do
>>> it as a follow-on proposal to this one.  If a good design emerges, we can
>>> evaluate that design based on its merits.
>>>
>>>
>>> The final alternative is that we could leave the feature in the
>>> compiler.  However, that means living with its complexity “forever” or
>>> breaking code in the Swift 4 timeframe.  It would be preferable to tackle
>>> this breakage in the Swift 3 timeframe, since we know that migration will
>>> already be needed then.
>>>
>>> -Chris
>>>
>>>
>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160127/cd7ac2ea/attachment.html>


More information about the swift-evolution mailing list