# [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
}
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
}
}

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 }