WARNING: Turns out there are some nasty side-effects, see the update below.
: .warning
If you have every attempted to create a generic enum, you have most certainly encountered the following error message:
error: unimplemented IR generation feature non-fixed multi-payload enum layout
: .warning
Yikes! Well, it turns out that Swift (whether by-design or a current limitation) needs to know the full layout size of the enum at compile time. So code like this:
enum FailableOf<T> {
case Success(T)
case Failure(Error)
init(_ value: T) {
self = .Success(value)
}
init(_ error: Error) {
self = .Failure(error)
}
var failed: Bool { /* ... */ }
var error: Error? { /* ... */ }
var value: T? {
switch self {
case .Success(let value):
return value
default:
return nil
}
}
}
Is just not possible to write. I had worked around this by using a wrapper class FailableWrapper<T>
(see Error Handling in Swift).
However, Rob Napier tweeted a much more elegant solution.
And autoclosure saves(?) the day for generic recursive enums https://t.co/YDeGM69lER
— Rob Napier (@cocoaphony) August 6, 2014
//platform.twitter.com/widgets.js
When I saw that, I thought, "well duh!". So much better than my workaround.
Here's the source for my full FailableOf<T>
class; no more need for the wrapper class.
public enum FailableOf<T> {
case Success(@autoclosure() -> T)
case Failure(Error)
public init(_ value: T) {
self = .Success(value)
}
public init(_ error: Error) {
self = .Failure(error)
}
public var failed: Bool {
switch self {
case .Failure(let error):
return true
default:
return false
}
}
public var error: Error? {
switch self {
case .Failure(let error):
return error
default:
return nil
}
}
public var value: T? {
switch self {
case .Success(let value):
return value()
default:
return nil
}
}
}
UPDATE August 6th: Thanks for John Vasileff for pointing out, what should have been, an obvious design flaw with this approach.
: .info
@owensd @cocoaphony consider idempotence
var v1: Int = 0
var result1 = FailableOf.Success(v1++)
result1.value // 0
result1.value // 1
— John Vasileff (@jvasileff) August 6, 2014
//platform.twitter.com/widgets.js
Yep… he's right. =)
Back to my wrapper class for now.