Luiz
(Luiz Fernando)
1
I'm currently writing some code that expects me to be able to turn Optional<(T1, T2, T3)> into (Optional<T1>, Optional<T2>, Optional<T3>), for any variable-length tuple in order to do destructuring using if-let and if-case-let bindings. I could theoretically come up with variations by hand, but since this is part of a Swift code generator I must be able to handle any crazy construction the user throws at it.
I tried to tackle this with parameter packs, but the best I can come up with still needs an indirection to populate the (nil, nil, ...) value:
func shuffle<each T>(_ tuple: (repeat each T)?) -> (repeat Optional<each T>) {
if let tuple = tuple {
return (repeat each tuple)
}
func _dummy<U>(_ any: U.Type) -> U? { nil }
return (repeat _dummy((each T).self))
}
I'm wondering if there's a cleaner way of doing it?
2 Likes
xAlien95
(Stefano De Carolis)
2
You can use T?.none directly:
func shuffle<each T>(_ tuple: (repeat each T)?) -> (repeat (each T)?) {
if let tuple { return (repeat each tuple) }
return (repeat (each T)?.none)
}
5 Likes
johnny1
(johnny1)
3
Great solution by @xAlien95. You can also use (repeat nil as (each T)?).
3 Likes
I think the canonical name for this operation is unzip?
I was hoping this would work, but it crashes the compiler.
func unzip<each T>(_ tuple: (repeat each T)?) -> (repeat (each T)?) {
tuple ?? (repeat nil as (each T)?)
}
You can work around it with this, though:
func unzip<each T>(_ tuple: (repeat each T)?) -> (repeat (each T)?) {
tuple.map { (repeat each $0) } ?? (repeat nil as (each T)?)
}
Unfortunately, the same trick doesn't work on a method, which this probably should be*.
Crashes:
extension Optional {
func unzip<each WrappedElement >() -> (repeat (each WrappedElement)?)
where Wrapped == (repeat each WrappedElement) {
map { (repeat each $0) } ?? (repeat nil as (each WrappedElement)?)
}
}
Workaround:
extension Optional {
func unzip<each WrappedElement>() -> (repeat (each WrappedElement)?)
where Wrapped == (repeat each WrappedElement) {
switch self {
case let wrapped?: (repeat each wrapped)
case nil: (repeat nil as (each WrappedElement)?)
}
}
}
* Ha! Maybe stick with that global function until post-beta season, when there might be some time for people to work on this…
↔
let two: Optional = (1, 2)
unzip(two)
two.unzip()
let one: Optional = 1
unzip(one)
one.unzip() // runtime crash
2 Likes