"you can pass an autoclosure to an autoclosure”

This is very surprising to me! I think that should be an error… When I write an 
argument of type `@autoclosure () -> T`, only arguments of type `T` ought to be 
accepted.

> On Jul 1, 2017, at 3:50 PM, Adrian Zubarev <[email protected]> 
> wrote:
> 
> It’s not about whether I should use these like this or not. It’s about that 
> you can pass an autoclosure to an autoclosure (where the metatype is the same 
> as a normal closure), but you cannot pass a normal closure to an autoclosure. 
> @escaping making things even worse. When using Void as a return type Xcode 
> will provide you an additional completion option which only will result in an 
> error!
> 
> func foo(_: @autoclosure () -> Void) { }
> 
> func bar(_ test: @autoclosure () -> Void) {
>     foo(test) // works
> }
> 
> func baz(_ test: @autoclosure @escaping () -> Void) {
>     print(type(of: test)) // prints `() -> ()`
>     foo(test) // error because it's `@escaping`
> }
> 
> let closure: () -> Void = {}
> 
> bar(())
> 
> bar(closure) // error
> 
> // Suggested autocompletion by Xcode which results in an error
> bar {
>     <#code#>
> }
> Long story short autoclosure is bugged and deserves this fix.
> 
> Here a few examples where I’m using the mentioned non-generic extension 
> instead of an if statement:
> 
> self.shouldPop.whenTrue(execute: self.toView.isUserInteractionEnabled = false)
> 
> ($0 == .end).whenTrue(execute: completion)
> (!option.isInteractive).whenTrue(execute: sendEvents)
> There are other cases where I’d use it, but I cannot because the generic 
> autoclosure function simply does not work as I’d expect it to work:
> 
> @discardableReuslt
> func whenTrue<T>(execute closure: @autoclosure () -> T) -> T? {
>    if self { return closure() }
>    return nil
> }
> 
> 
> 
> -- 
> Adrian Zubarev
> Sent with Airmail
> 
> Am 2. Juli 2017 um 00:23:43, Jaden Geller ([email protected] 
> <mailto:[email protected]>) schrieb:
> 
>> I feel strongly that you shouldn’t be using autoclosure in these cases. 
>> Instead, write `true.whenTrue { … }` and `true.whenTrue(myClosure)`.
>> 
>>> On Jul 1, 2017, at 3:17 PM, Adrian Zubarev <[email protected] 
>>> <mailto:[email protected]>> wrote:
>>> 
>>> I clearly disagree with your point. Autoclosure supposed to be a 
>>> syntactically convenience feature to omit braces, which as a consequence 
>>> needs to disable arguments. However it is not said that you cannot pass a 
>>> closure with the same signature to the autoclosure, which currently is not 
>>> possible unless it’s another autoclosure. This doesn’t feel right at all.
>>> 
>>> func foo(_: @autoclosure () -> Void) {}
>>> 
>>> func bar(_ test: @autoclosure () -> Void) {
>>>    foo(test) // works     
>>> }
>>> 
>>> let closure: () -> Void = {}
>>> 
>>> foo(closure) // error
>>> Here is another example where autoclosure takes over and produces false 
>>> result even when the correct overload is present but the resolution ends up 
>>> picking an autoclosure.
>>> 
>>> extension Bool {
>>> 
>>>     /// #1
>>>     func whenTrue(execute closure: () -> Void) {
>>>         if self { closure() }
>>>     }
>>> 
>>>     /// #2
>>>     func whenTrue(execute closure: @autoclosure () -> Void) {
>>>         if self { closure() }
>>>     }
>>> 
>>>     /// #3
>>>     func whenTrue<T>(execute closure: @autoclosure () -> T) -> T? {
>>>         if self { return closure() }
>>>         return nil
>>>     }
>>> }
>>> 
>>> let test: () -> Void = { }
>>> // #3 wins and produces a wrong type () -> (() -> Void)?, but I expect #1 
>>> here
>>> // () -> Void?
>>> true.whenTrue(execute: test)   
>>> A syntactical convenience feature should not disable explicitness!
>>> 
>>> 
>>> 
>>> 
>>> -- 
>>> Adrian Zubarev
>>> Sent with Airmail
>>> 
>>> Am 1. Juli 2017 um 19:46:55, [email protected] 
>>> <mailto:[email protected]> ([email protected] 
>>> <mailto:[email protected]>) schrieb:
>>> 
>>>> 
>>>> 
>>>> On Jun 30, 2017, at 1:48 AM, Adrian Zubarev via swift-evolution 
>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>> 
>>>>> Well as Jordan Rose said on the linked SR, option (1) will probably never 
>>>>> happen. Option (3) only makes sense if all of the options are supported 
>>>>> (in that case there wouldn’t be any need for explicit @autoclosure, which 
>>>>> could simply be merged into the closure type), or (2) is NOT supported so 
>>>>> that one could pass a default autoclosure.
>>>>> 
>>>>> It leaves us only with (2), which is potentially a (small) breaking 
>>>>> change, but it also feels more like a fix. I cannot imagine anyone is 
>>>>> wrapping whole closures with auto closure, nor do I think a ‘convenience’ 
>>>>> operation should disable the explicit ability to pass in a closure with 
>>>>> the same signature. The latter feels like a bug. Furthermore I think most 
>>>>> code that relies on this is already doing something like.
>>>>> 
>>>>> func bar(_ closure: @autoclosure () -> Int) { foo(closure)}
>>>>> 
>>>>> func foo(_ closure: () -> Int)
>>>>> But this is only an assumption of mine.
>>>>> 
>>>>> Theoretically it suppose to work the other way around, right? Again 
>>>>> @autoclosure supposed to be a syntactical convenience feature which 
>>>>> implies that it won’t disable *too* much from the closure type. 
>>>>> Disallowing arguments is logical consequence but not the other issues I 
>>>>> mentioned here and in the SR.
>>>>> 
>>>>> —
>>>>> 
>>>>> One question: Do we need to go through a full evolution process for pitch 
>>>>> (2) or is a bug report enough here?
>>>>> 
>>>> Surely the former—I'm fully against this change, and imagine others are 
>>>> also. Autoclosure exists to provide opt-in lazy evaluation of values by 
>>>> wrapping them in a closure. I think it's semantically incorrect to accept 
>>>> an already wrapped value here, and adding this sort of implicit conversion 
>>>> can introduce potential ambiguity when used with generic functions.
>>>> 
>>>> Very large -1.
>>>>> 
>>>>> 
>>>>> -- 
>>>>> Adrian Zubarev
>>>>> Sent with Airmail
>>>>> 
>>>>> Am 30. Juni 2017 um 00:59:45, Beta ([email protected] 
>>>>> <mailto:[email protected]>) schrieb:
>>>>> 
>>>>>> These are all interesting ideas at first blush, but introduce some 
>>>>>> oddities into the type system
>>>>>> 
>>>>>> 1. We accept this 😳.  If we were to take this as an official language 
>>>>>> change it would mean that we would allow coercing T to (_) -> T by 
>>>>>> emitting a closure that takes an argument list (of arity given by the 
>>>>>> contextual type) that we throw away anyways.  I would much prefer we 
>>>>>> diagnose this instead.  @autoclosure is a syntactically convenient way 
>>>>>> to ask for laziness - that’s it.
>>>>>> 
>>>>>> 2. Doing this collapses overloads on @autoclosure
>>>>>> 
>>>>>> func foo(_ f : @autoclosure () -> String) {}
>>>>>> func foo(_ f : () -> String) {}
>>>>>> 
>>>>>> 
>>>>>> Which is fine by me except for the code you would break that relies on 
>>>>>> this.  I don’t see a reasonable migration path here - perhaps you have 
>>>>>> one in mind.
>>>>>> 
>>>>>> 3. @autoclosure is a parameter attribute.  Allowing it to appear in 
>>>>>> other positions is redundant and doesn’t actually accomplish anything 
>>>>>> outside of maintaining consistency with the first point.
>>>>>> 
>>>>>> I hope I don’t come off as too harsh.  It’s just a little shocking to me 
>>>>>> that we accept the code in the linked SR.
>>>>>> 
>>>>>> ~Robert Widmann
>>>>>> 
>>>>>>> On Jun 24, 2017, at 9:10 AM, Adrian Zubarev via swift-evolution 
>>>>>>> <[email protected] <mailto:[email protected]>> wrote:
>>>>>>> 
>>>>>>> Hello folks,
>>>>>>> 
>>>>>>> Here is a quick and straightforward pitch about @autoclosure. Currently 
>>>>>>> the attribute indicates that the caller has to pass an expression so 
>>>>>>> that the braces can be omitted. This is a convenient behavior only, but 
>>>>>>> it also has it’s shortcomings.
>>>>>>> 
>>>>>>> I would like to propose an extension of that behavior.
>>>>>>> 
>>>>>>> 
>>>>>>> 1. Allow access to arguments and shorthand argument names:
>>>>>>> // Bug: https://bugs.swift.org/browse/SR-5296 
>>>>>>> <https://bugs.swift.org/browse/SR-5296>
>>>>>>> func foo(_ test: @autoclosure (Int) -> Int = { $0 }) {
>>>>>>>     print(test(42))
>>>>>>> }
>>>>>>> 
>>>>>>> // Convenient access using shorthand arguments
>>>>>>> foo(Int(Double($0) * 3.14)))
>>>>>>> 
>>>>>>> 2. Make @autoclosure only wrap when necessary:
>>>>>>> func bar(_ test: @autoclosure () -> Int) {
>>>>>>>     print(test())
>>>>>>> }
>>>>>>> 
>>>>>>> let test = { 42 }
>>>>>>> 
>>>>>>> // function produces expected type 'Int'; did you mean to call it with 
>>>>>>> '()'?
>>>>>>> bar(test)
>>>>>>> 
>>>>>>> 3. Extend @autoclosure to closure types in general (this change is for 
>>>>>>> consistent alignment):
>>>>>>> // Note how we're using the shorthand argument list for this expression
>>>>>>> let uppercaseWrapper: @autoclosure (String) -> String = $0.uppercased()
>>>>>>> 
>>>>>>> 
>>>>>>> 
>>>>>>> -- 
>>>>>>> Adrian Zubarev
>>>>>>> Sent with Airmail
>>>>>>> 
>>>>>>> _______________________________________________
>>>>>>> swift-evolution mailing list
>>>>>>> [email protected] <mailto:[email protected]>
>>>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>>> 
>>>>> 
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> [email protected] <mailto:[email protected]>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution 
>>>>> <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> 
>> 
> 
> 

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to