[英]Recursive Swift Either Enum with Generic Associated Values
I want to develop a recursive Either
type as a Swift enum (at least a partially working version (;), and so have been playing around with generics (trying to work with the type system).我想开发一个递归的
Either
类型作为 Swift 枚举(至少是部分工作版本(;),所以一直在玩 generics (尝试使用类型系统)。
Here is what I have so far:这是我到目前为止所拥有的:
enum NumericEitherEnum<Left, Right> {
case left(Left)
case right(Right)
var left: Left? {
switch self {
case .left(let leftie) where leftie is NumericEitherEnum<Int, Float>:
return (leftie as? NumericEitherEnum<Int, Float>)?.left // This won't work (obviously)
case .left(let left):
return left
case .right(_):
return nil
}
}
var right: Right? {
switch self {
case .left(_):
return nil
case .right(let right) where right is NumericEitherEnum:
return (right as? NumericEitherEnum)?.right // This assumes the generic parameters of Left, Right in the current enum so won't work.
case .right(let right):
return right
}
}
}
I get cryptic diagnostic error messages in Xcode, which I haven't been able to get around:我在 Xcode 中收到神秘的诊断错误消息,我无法解决:
(leftie as? NumericEitherEnum<Int, Float>)?
(leftie as? NumericEitherEnum<Int, Float>)?
with NumericEitherEnum<Int, Float>
'NumericEitherEnum<Int, Float>
' What I am trying to achieve:我想要达到的目标:
print(NumericEitherEnum<NumericEitherEnum<Int, Float>, NumericEitherEnum<Int, Float>>.left(.left(3)).left ?? "failed :(") // Parses the innermost value 3
The fix-it doesn't fix the error or advise how to actually address the underlying cause.修复它不会修复错误或建议如何实际解决根本原因。 I think this is probably an edge case for the compiler to parse (maybe even bug);).
我认为这可能是编译器解析的边缘情况(甚至可能是错误);)。 I also realise that in fact it doesn't make sense logically to return an
Int
for a Left?
我也意识到实际上返回
Int
为Left?
type but is there any way I can express this within the type system (I tried associated types but I still don't know how to make this dynamic).类型,但有什么方法可以在类型系统中表达这个(我尝试了关联类型,但我仍然不知道如何使这个动态)。 There's also a problem to handle nested enums that have a different generic type signature (not sure how to express this).
处理具有不同泛型类型签名的嵌套枚举也存在问题(不知道如何表达)。
How can I resolve this issue in a better way?我怎样才能更好地解决这个问题? Ideally, I want to have a clean call site without too much indirection and extra data structures but would be open to trying out a different data structure if this isn't practically achievable.
理想情况下,我希望有一个干净的调用站点,没有太多间接和额外的数据结构,但如果这实际上无法实现,我愿意尝试不同的数据结构。
So I discovered that Swift 2 introduced support for recursive enums ages ago - source , While this is great.所以我发现 Swift 2 很久以前就引入了对递归枚举的支持 - 源代码,虽然这很棒。 I didn't manage to find any SO questions for this specific problem ie Generic Recursive Enums that had also been answered ( here ).
我没有找到针对这个特定问题的任何 SO 问题,即也已回答的通用递归枚举( 此处)。 I will summarise the final (partial solution) code now.
我现在将总结最终(部分解决方案)代码。
It turns out that I needed to describe what the problem is in terms of data structures.事实证明,我需要用数据结构来描述问题所在。 Essentially we want
.left
to contain either a value of type Left
or a RecursiveEither<Left, Right>
.本质上,我们希望
.left
包含Left
或RecursiveEither<Left, Right>
类型的值。 With this simple idea, we can create two enums - one for the wrapper enum, and one for the nested enum which can take a Value
or another wrapper.有了这个简单的想法,我们可以创建两个枚举 - 一个用于包装器枚举,一个用于嵌套枚举,它可以采用
Value
或另一个包装器。
enum RecursiveEither<Left, Right> {
case left(ValueOrLeftOrRight<Left, Left, Right>)
case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
guard case .left(let leftie) = self else { return nil }
return leftie.left
}
var right: Right? {
guard case .right(let rightie) = self else { return nil }
return rightie.right
}
}
enum ValueOrLeftOrRight<Value, Left, Right> {
case value(Value)
indirect case left(ValueOrLeftOrRight<Left, Left, Right>)
indirect case right(ValueOrLeftOrRight<Right, Left, Right>)
var left: Left? {
switch self {
case .value(let left): return left as? Left
case .left(let content): return content.left
default: return nil
}
}
var right: Right? {
switch self {
case .value(let right): return right as? Right
case .right(let content): return content.right
default: return nil
}
}
}
The call site is nice with this design:呼叫站点很适合这种设计:
let e = RecursiveEither<Int, Int>.left(.left(.left(.left(.value(3)))))
That being said, there are still limitations to this answer - assuming Left
and Right
won't change even in nested Either
.话虽如此,这个答案仍然存在限制 - 假设
Left
和Right
即使在嵌套的Either
中也不会改变。 Also, it's possible to design this differently with only one indirect
case to wrap the Either
type in another associated value - that might be more memory efficient since fewer frames get pushed onto the call stack at runtime.此外,可以仅使用一种
indirect
情况来设计这种不同的方式,以将Either
类型包装在另一个关联值中——这可能更有效,因为在运行时将更少的帧推入调用堆栈。
I have posted a gist with attempts on this problem.我已经发布了一个关于这个问题的尝试的要点。
If anyone has suggestions to improve this implementation, feel free to add to this.如果有人有改进此实现的建议,请随时添加。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.