> On 16 Aug 2016, at 20:24, Justin Jia via swift-evolution 
> <swift-evolution@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.)

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to