展开/合并多级可选



我正试图编写一个函数来打开具有任意嵌套级别的选项。这是我正在使用的测试:

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??的有效值,因此不会返回默认值。

有什么办法可以安全地在这里做我想做的事吗?

此代码满足您的要求缺点是您需要在要放入选项的每种类型中实现Unwrappeable协议。也许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

诀窍在于,Unwrappeable协议将最终可以展开的类型(如在0、1或更多个展开之后(标记为特定类型。

这个问题的困难在于,在Optional的扩展中,您可以获得Wrapped类型,但如果Wrapped再次是可选的,则无法访问Wrapped。Wrapped(换句话说,Swift不支持Higher Kinded Types(。

另一种方法是尝试向Optional where Wrapped == Optional添加一个扩展。但同样,您需要更高类型的支持才能访问Wrapped。包装类型。如果我们试图扩展Optional where Wrapped == Optional<T>,我们也会失败,因为我们不能在T.上一般地扩展Optional

最新更新