How to unwrap arbitrarily deeply nested optionals

2019-04-29 02:19发布

问题:

As an exercise in Swift, I am trying to write an extension method that will unwrap arbitrarily deeply nested optionals. This has little practical use and is simply an exploration of Swift's type system.

Examples of arbitrarily deeply nested optionals are Optional<Optional<Optional<Int>>> and Optional<Optional<Optional<Optional<Int>>>>.

The only way I've discovered to do this is to use type erasure:

protocol TypeErasedOptional {
    func deeplyUnwrap() -> Any?
}

extension Optional: TypeErasedOptional {
    func deeplyUnwrap() -> Any? {
        switch self {
        case .none: return nil
        case .some(let wrapped as TypeErasedOptional): return wrapped.deeplyUnwrap()
        case .some(let wrapped): return wrapped
        }
    }

    func unwrap<T>(_ type: T.Type = T.self) -> T? {
       switch deeplyUnwrap() {
       case .none: return nil
       case .some(let wrapped as T): return wrapped
       default: return nil
       }
    }
}

This works well. We can unwrap a deeply nested optional, but unfortunately we have to restate the Wrapped type:

let x = Optional<Optional<Optional<Int>>>(3)
let y = x.unwrap(Int.self)

I can't think of any way to do this without type erasure. And once you use type erasure, you must restate the type to get it back. I don't want this. Can someone more versed in Swift let me know either that this cannot be done or whether there is another way?

回答1:

Here's a solution that provides flattening up to a six-levels Optional:

extension Optional {
    func flatten() -> Wrapped? {
        return self
    }

    func flatten<T>() -> T? where Wrapped == T? {
        return map { $0.flatten() } ?? nil
    }

    func flatten<T>() -> T? where Wrapped == T?? {
        return map { $0.flatten() } ?? nil
    }

    func flatten<T>() -> T? where Wrapped == T??? {
        return map { $0.flatten() } ?? nil
    }

    func flatten<T>() -> T? where Wrapped == T???? {
        return map { $0.flatten() } ?? nil
    }

    func flatten<T>() -> T? where Wrapped == T????? {
        return map { $0.flatten() } ?? nil
    }
}

The advantage of the above solution is the fact that is type safe, the disadvantages are the fact that it's statically typed (e.g. can't call flatten() on Any variables), and that you need to add more and more overloads if you need to support more and more nesting levels.



标签: swift swift4