[swift-evolution] Revisiting SE-0110

Vladimir.S svabox at gmail.com
Mon Jun 5 11:20:45 CDT 2017


Yes, double parenthesis for tuple argument destructuring in closure was discussed in 
this thread, and I feel it is one of the best options.

But the main question I can't find the clear answer for, what will be with 
function/closure types in Swift 4. IMO *this* is the main question, not the syntax 
for tuple destructuring.

Chris, could you please help to clarify the situation with SE-0066 and SE-0110?
In Rationale part of the SE-0110 acceptance message you said:
"... The community and core team agree that this proposal is the right thing to do, 
and many agree that this could probably have been treated as a bug fix on a previous 
proposal.
..."
(https://lists.swift.org/pipermail/swift-evolution-announce/2016-July/000215.html)

Were you thinking about SE-0110 just as bug fix for SE-0066/SE-0029?

What is your opinion, can we revisit SE-0110 without revisiting SE-0066?

I mean that for me SE-0066 clearly separated *types* for function with one tuple 
argument and a list of arguments, so (Int,Int)->Void is not the same *type* as 
((Int,Int))->Void, and so we just must to assign a right concrete type for 
closure(depending on its arguments), so we must separate closures that takes one 
tuple argument and a list of arguments. And SE-0110 just clarifies this.

How do you think, what should be a *type* of these constants after SE-0066?:

let closure1 = {(x: Int, y: Int) in}
let closure2 = {(x: (Int,Int)) in }

print(type(of: closure1)) // ?
print(type(of: closure2)) // ?

For me, based on *SE-0066*, they must be typed as (Int, Int)->Void and 
((Int,Int))->Void accordingly, no?

Should type(of: closure1) == type(of: closure2) ?
Can (closure2 is (Int,Int)->Void) == true ?
and (closure1 is ((Int,Int))->Void) == true ?

Having function
func foo(callback: (_ x:(Int,Int))->Void) {..}

, should we be able to just send closure1 to it? I.e.
foo(callback: closure1)  // ?
foo(callback:  {(x: Int, y: Int) in}) // and here?
foo(callback: {x, y in }) // and here?

I even think that we can have implicit conversion between these *types* of 
closures/funcs (one tuple vs arg list), because this seems to be very handy and used 
a lot, but IMO after SE-0066 we should have clear and strict type for each kind of 
func/closure and then, can have clear rule that we can use one *type*, where *another 
type* is required.
Or allow just tuple destructuring by *special* syntax in closure. Again, closure will 
be of correct *type* but contains tuple destructuring syntax.
Or disallow using of different *type* of func/closure when another type is 
requested.(Exactly this was proposed and accepted by SE-0110, that was inspired by 
SE-0066)

Having all these said, I can't understand how we can actually revisit SE-0110, 
because it just clarifies for closures what SE-0066 requires for function types.

Thank you.
Vladimir.

On 04.06.2017 20:16, Chris Lattner via swift-evolution wrote:
> 
>> On Jun 1, 2017, at 3:06 PM, John McCall via swift-evolution 
>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>
>>
>>> On Jun 1, 2017, at 2:39 PM, Pavol Vaskovic <pali at pali.sk <mailto:pali at pali.sk>> wrote:
>>>
>>> On Thu, Jun 1, 2017 at 8:52 PM, John McCall via swift-evolution 
>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>
>>>
>>>     I understand that there are developers who dislike SE-0110's impact on certain
>>>     kinds of functional programming, but that is a very broad complaint that is
>>>     unlikely to reach consensus or acceptance, especially for Swift 4. 
>>>
>>>
>>> The impact of SE-0110 as currently implemented in Swift 4 leads to following 
>>> migration choice wherever you have a closure that takes tuple argument:
>>> * concise but obfuscate code ($0.1, ...)
>>> * readable but verbose code (requiring a ton of boilerplate: intermediate 
>>> argument, expand signature to include return type, desctructure tuple on new line 
>>> using let, add return clause)
>>>
>>> Maybe I misunderstood you, but I don't think this is marginal issue affecting only 
>>> some "developers that dislike the impact on certain kinds of functional programming". 
>>
>> You're misunderstanding me.  I have explicitly said, several times, that I agree 
>> that the impact on tuple destructuring in closures is a serious regression.  There 
>> have *also* been objections to losing argument-splat behavior, and while that does 
>> negatively affect some functional styles, I think it would be a mistake to try to 
>> address that now.
> 
> I agree with both points: we need to fix the type checker semantics+performance 
> regression, but I also sympathize with the beauty regression for closures.  Here are 
> some the examples Gwendal Roué cited up-thread (just to make the discussion concrete):
> 
> Example 1
> -        return columns.index { (column, _) in column.lowercased() == lowercaseName }
> +        return columns.index { $0.0.lowercased() == lowercaseName }
> 
> Example 2 :
> -            .map { (mappedColumn, baseColumn) -> (Int, String) in
> +            .map { (pair) -> (Int, String) in
> +                let mappedColumn = pair.key
> +                let baseColumn = pair.value
> 
> Example 3 :
> -                .map { (table, columns) in 
> "\(table)(\(columns.sorted().joined(separator: ", ")))" }
> +                .map { "\($0.key)(\($0.value.sorted().joined(separator: ", ")))" }
> 
> Example 4 :
> -                dictionary.first { (column, value) in column.lowercased() == 
> orderedColumn.lowercased() }
> +                dictionary.first { $0.key.lowercased() == orderedColumn.lowercased() }
> 
> 
> 
> 
> One way to split the difference here is to eliminate the splatting behavior, but keep 
> the destructuring (irrefutable pattern matching) behavior as well.  In these cases, 
> just require an extra explicit paren for the parameter list.  This would change the 
> diff's to:
> 
> Example 1
> -        return columns.index { (column, _) in column.lowercased() == lowercaseName }
> +       return columns.index { ((column, _)) in column.lowercased() == lowercaseName }
> 
> Example 2 :
> -            .map { (mappedColumn, baseColumn) -> (Int, String) in
> +            .map { ((mappedColumn, baseColumn)) -> (Int, String) in
> 
> Example 3 :
> -                .map { (table, columns) in 
> "\(table)(\(columns.sorted().joined(separator: ", ")))" }
> +                .map { ((table, columns)) in 
> "\(table)(\(columns.sorted().joined(separator: ", ")))" }
> 
> Example 4 :
> -                dictionary.first { (column, value) in column.lowercased() == 
> orderedColumn.lowercased() }
> +                dictionary.first { ((column, value)) in column.lowercased() == 
> orderedColumn.lowercased() }
> 
> 
> What do you think?  Seems like it would solve the type checker problem, uglify the 
> code a lot less, and make the fixit/migration happily trivial.
> 
> -Chris
> 
> 
> 
> 
> 
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
> 


More information about the swift-evolution mailing list