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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With