[swift-evolution] [Pitch] Extending [at]autoclosure

Adrian Zubarev adrian.zubarev at devandartist.com
Sun Jun 25 01:52:59 CDT 2017


Imagine you’re calling a function that takes an auto closure inside another closure.
I’ll try to restructure this sentence and hopefully understand it correctly:

A closure with a nested function that takes an autoclosure, which has at least one parameter.

At first some other examples:

let test1: ([String]) -> Void = {
    $0.forEach { print($0) /* local argument wins */ }
}

let test2: ([String]) -> Void = {
    $0.forEach { string in
        // error: anonymous closure arguments cannot be used inside  
        // a closure that has explicit arguments print($0)
        print($0)
    }
}

let test3: ([String]) -> Void = { array in
    array.forEach { string in
        print(array) // fine again
    }
}
Now let’s showcase the issues you’ve mentioned:

func foo(_: @autoclosure (String) -> String) {}

let test4: ([String]) -> Void = {
    print($0)
     
    foo("\($0)") // Should be an error because it's ambiguous
}

// We have two options to resolve the issue:
let test4_1: ([String]) -> Void = { array in
    print(array)

    foo("\($0)") // Now it's fine
}

let test4_2: ([String]) -> Void = {
    print($0)
     
    // If closures would be allowed, so that `@autoclosure` will only
    // wrap when necessary, here the local shorthand argument will win
    // and behave like in `test1`
    foo({ "\($0)" })
     
    // Or in this example a trailing closure would do the same trick
    foo { "\($0)" }
}
I have an API where I would want to use @autoclosure that supports #1 and #2.

open func push(_ viewController: UIViewController,
               option: Option = .animated,
               with animator: (Transition) -> Animator = animator(for:)) { ... }
The caller could use a simple expression that will receive an important and necessary parameter to instantiate the animator.

containerViewController.push(someVC, with: CustomAnimator(for: $0 /* transition */))
But as already shown in the current implementation the closure should still be able to accept a default closure which takes some arguments.



-- 
Adrian Zubarev
Sent with Airmail

Am 25. Juni 2017 um 05:53:58, Gor Gyolchanyan (gor at gyolchanyan.com) schrieb:

I have thought of this before, but I always got stuck on the problem of nested closures.
Imagine you’re calling a function that takes an auto closure inside another closure.
What exactly will `$0` refer to?

On Jun 24, 2017, at 7:10 PM, Adrian Zubarev via swift-evolution <swift-evolution at swift.org> 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
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
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/20170625/5b52c54d/attachment.html>


More information about the swift-evolution mailing list