Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unwrap arbitrarily deeply nested optionals in Swift?

Tags:

swift

swift4

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?

like image 960
Gregory Higley Avatar asked May 11 '18 00:05

Gregory Higley


People also ask

How do I unwrap optional binding in Swift?

An if statement is the most common way to unwrap optionals through optional binding. We can do this by using the let keyword immediately after the if keyword, and following that with the name of the constant to which we want to assign the wrapped value extracted from the optional.

What is the point of optionals in Swift?

Optionals are in the core of Swift and exist since the first version of Swift. An optional value allows us to write clean code with at the same time taking care of possible nil values. If you're new to Swift you might need to get used to the syntax of adding a question mark to properties.

What is optional binding in Swift?

Swift has the concept of optionals that many programming languages don't have. An optional type means that it can either have a value or there isn't a value. Swift forces you to check if an optional has a value before using it. Optional Binding is a common way to find out whether an optional has a value or not.


1 Answers

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.

like image 150
Cristik Avatar answered Nov 15 '22 07:11

Cristik