Jean Lebrument Jean Lebrument - 12 days ago 5
Swift Question

Avoid consecutive "if let" declarations in Swift

In Swift I used

if let
declarations to check if my object is not
nil


if let obj = optionalObj
{
}


But sometimes, I have to face with consecutive
if let
declarations

if let obj = optionalObj
{
if let a = obj.a
{
if let b = a.b
{
// do stuff
}
}
}


I'm looking for a way to avoid consecutive
if let
declarations.

I would try something like :

if let obj = optionalObj && if let a = obj.a && if let b = a.b
{
// do stuff
}


But the swift compiler do not allow this.

Any suggestion ?

Answer

I wrote a little essay on the alternatives some time ago: https://gist.github.com/pyrtsa/77978129090f6114e9fb

One approach not yet mentioned in the other answers, which I kinda like, is to add a bunch of overloaded every functions:

func every<A, B>(a: A?, b: B?) -> (A, B)? {
    switch (a, b) {
        case let (.Some(a), .Some(b)): return .Some((a, b))
        default:                       return .None
    }
}

func every<A, B, C>(a: A?, b: B?, c: C?) -> (A, B, C)? {
    switch (a, b, c) {
        case let (.Some(a), .Some(b), .Some(c)): return .Some((a, b, c))
        default:                                 return .None
    }
}

// and so on...

These can be used in if let statements, case expressions, as well as optional.map(...) chains:

// 1.
var foo: Foo?
if let (name, phone) = every(parsedName, parsedPhone) {
    foo = ...
}

// 2.
switch every(parsedName, parsedPhone) {
    case let (name, phone): foo = ...
    default: foo = nil
}

// 3.
foo = every(parsedName, parsedPhone).map{name, phone in ...}

Having to add the overloads for every is boilerplate'y but only has to be done in a library once. Similarly, with the Applicative Functor approach (i.e. using the <^> and <*> operators), you'd need to create the curried functions somehow, which causes a bit of boilerplate somewhere too.