Развернуть / объединить многоуровневый дополнительный - PullRequest
0 голосов
/ 07 ноября 2018

Я пытаюсь написать функцию для разворачивания опциональных файлов с произвольным количеством уровней вложенности. Вот тест, который я использую:

let a: Int??? = 1
let b: Int??? = nil
print(a.unwrap(0), b.unwrap(0)) // should print 1, 0

Я могу получить правильный вывод с помощью базовой универсальной функции:

extension Optional {
    func unwrap<T> (_ defaultValue: T) -> T {
        return (self as? T) ?? defaultValue
    }
}

print(a.unwrap(0), b.unwrap(0)) // 1, 0

Но это не мешает вызову функции с типом, отличным от необязательного. Например, я мог бы вызвать a.unwrap("foo") и вывести «foo» вместо «1», поскольку, конечно, вы не можете разыграть Int??? до String.

Я пробовал использовать Wrapped вместо этого, что частично корректно ограничивает значение по умолчанию, но не дает правильного вывода:

extension Optional {
    func unwrap (_ defaultValue: Wrapped) -> Wrapped {
        return (self as? Wrapped) ?? defaultValue
    }
}

print(a.unwrap(0), b.unwrap(0)) // Optional(Optional(1)), nil

Развертывает только один дополнительный уровень вместо всех трех, и, поскольку nil является допустимым значением для Int??, он не возвращает значение по умолчанию.

Есть ли способ безопасно сделать то, что я хочу здесь?

1 Ответ

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

Этот код делает то, что вы просите. Недостатком является то, что вам нужно реализовать протокол Unwrappable для каждого типа, который вы хотите поместить в опциональные опции . Может быть Sourcery может помочь с этим.

protocol Unwrappable {
  associatedtype T

  func unwrap(_ default: T) -> T
}

extension Optional {}

extension Optional: Unwrappable where Wrapped: Unwrappable {
  typealias T = Wrapped.T

  func unwrap(_ defaultValue: T) -> T {
    if let value = self {
      return value.unwrap(defaultValue)
    }
    return defaultValue
  }
}

extension Int: Unwrappable {
  typealias T = Int

  func unwrap(_ default: Int) -> Int {
    return self
  }
}

let nestedOptionalValue: Int??? = 6
let nestedOptionalNil: Int??? = nil
let optionalValue: Int? = 6
let optionalNil: Int? = nil
print(nestedOptionalValue.unwrap(0)) // prints 6
print(nestedOptionalNil.unwrap(0))   // prints 0
print(optionalValue.unwrap(0))       // prints 6
print(optionalNil.unwrap(0))         // prints 0

Хитрость в том, что протокол Unwrappable помечает типы, которые в конечном итоге могут быть развернуты (например, после 0, 1 или более развертываний) до определенного типа.

Сложность в этой проблеме связана с тем, что в расширении для Optional вы можете получить тип Wrapped, но если Wrapped снова становится необязательным, вы не можете получить доступ к Wrapped.Wrapped (другими словами, Swift не поддержите Высшие Родственные Типы).

Другой подход - попытаться добавить расширение к Optional where Wrapped == Optional. Но опять же вам понадобится поддержка Higher Kinded Types для доступа к типу Wrapped.Wrapped. Если мы попытаемся расширить Optional where Wrapped == Optional<T>, мы также потерпим неудачу, потому что не можем расширить Optional в общем случае по T.

...