Как развернуть произвольно глубоко вложенные опции в Swift? - PullRequest
0 голосов
/ 11 мая 2018

В качестве упражнения в Swift я пытаюсь написать метод расширения, который будет разворачивать произвольно глубоко вложенные опции. Это мало практического применения и просто исследование системы типов Swift.

Примерами произвольно глубоко вложенных опций являются Optional<Optional<Optional<Int>>> и Optional<Optional<Optional<Optional<Int>>>>.

Единственный способ сделать это - использовать стирание типа:

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

Это хорошо работает. Мы можем развернуть глубоко вложенный необязательный элемент, но, к сожалению, мы должны переформулировать тип Wrapped:

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

Я не могу придумать способ сделать это без стирания типа. И как только вы используете стирание типа, вы должны пересчитать тип, чтобы получить его обратно. Я не хочу этого Может ли кто-нибудь более разбирающийся в Swift дать мне знать, что это нельзя сделать или есть другой способ?

1 Ответ

0 голосов
/ 07 ноября 2018

Вот решение, которое обеспечивает выравнивание до шести уровней 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
    }
}

Преимущество вышеупомянутого решения заключается в том, что он является типобезопасным, а недостатки - в том, что он статическитипизированный (например, не может вызывать flatten() для Any переменных), и что вам нужно добавлять все больше и больше перегрузок, если вам нужно поддерживать все больше и больше уровней вложенности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...