[swift-evolution] [Idea] Use optionals for non-optional parameters

Pyry Jahkola pyry.jahkola at iki.fi
Tue Aug 16 13:40:05 CDT 2016

> On 16 Aug 2016, at 20:24, Justin Jia via swift-evolution <swift-evolution at swift.org> wrote:
> Thank you for the sharing how you solve this problem! It seems like the best workaround so far. 
> I wish $0 can be replaced by the actual name. (Maybe tuples?)
> let a = (x: x, y: y)
> let result = a.my_map { foo(x: $0.x, y: $0.y }
> In my_map unwrap all variables inside tuple?
> I agree that short-circuiting is an issue. But personally I still think the imperfect solution is good enough. But I'll try to find other possible solutions. 

One technique around this is to introduce a set of generic library functions that try to unwrap all of their arguments and construct a tuple thereof if every argument evaluated to `.some(_)`. The evaluation of expressions can also be delayed with @autoclosure:

    func unwrap<A, B>(_ a: A?, _ b: @autoclosure () throws -> B?) rethrows -> (A, B)? {
        guard let a = a, let b = try b() else { return nil }
        return (a, b)

    func unwrap<A, B, C>(_ a: A?,
                         _ b: @autoclosure () throws -> B?,
                         _ c: @autoclosure () throws -> C?) rethrows -> (A, B, C)?
        guard let a = a, let b = try b(), let c = try c() else { return nil }
        return (a, b, c)

    // ...etc, for higher arities than 3.

Repetitive library code? Yes. But can be easily generated with a code generator such as swift.gyb, and you hardly need it for arities higher than a few.

You can use `unwrap()` together with `if let` or `guard let`:

    if let (a, b, c) = unwrap(Int("1"), Double("12"), Bool("true")) {
        print(a, b, c) //=> 1 12.0 true

Or even `.map()` if you like. This use is similar to Dave Sweeris' suggestion of `optionally()`:

    struct User { var username, language: String }
    let data = ["u": "chris", "l": "swift"]
    let user = unwrap(data["u"], data["l"]).map {u, l in
        User(username: u, language: l)
    print(user) //=> Optional(User(username: "chris", language: "swift"))

I guess that reads better than this equivalent with `.flatMap()` and `.map()`:

    let user = data["u"].flatMap { u in
        data["l"].map { l in
            User(username: u, language: l)

OTOH, it isn't much better than either delayed `let` initialisation:

    let user: User?
    if let u = data["u"], let l = data["l"] {
        user = User(username: u, language: l)
    } else {
        user = nil

Or the use of a one-off closure like so:

    let user: User? = {
        guard let u = data["u"], let l = data["l"] else { return nil }
        return User(username: u, language: l)

— Pyry

(Sorry for the sparing use of newlines in the examples above. Tried to keep it tight.)

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160816/a72e1a19/attachment.html>

More information about the swift-evolution mailing list