[英]Recursive Swift Either Enum with Generic Associated Values
我想开发一个递归的Either
类型作为 Swift 枚举(至少是部分工作版本(;),所以一直在玩 generics (尝试使用类型系统)。
这是我到目前为止所拥有的:
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
}
}
}
我在 Xcode 中收到神秘的诊断错误消息,我无法解决:
(leftie as? NumericEitherEnum<Int, Float>)?
与NumericEitherEnum<Int, Float>
'我想要达到的目标:
print(NumericEitherEnum<NumericEitherEnum<Int, Float>, NumericEitherEnum<Int, Float>>.left(.left(3)).left ?? "failed :(") // Parses the innermost value 3
修复它不会修复错误或建议如何实际解决根本原因。 我认为这可能是编译器解析的边缘情况(甚至可能是错误);)。 我也意识到实际上返回Int
为Left?
类型,但有什么方法可以在类型系统中表达这个(我尝试了关联类型,但我仍然不知道如何使这个动态)。 处理具有不同泛型类型签名的嵌套枚举也存在问题(不知道如何表达)。
我怎样才能更好地解决这个问题? 理想情况下,我希望有一个干净的调用站点,没有太多间接和额外的数据结构,但如果这实际上无法实现,我愿意尝试不同的数据结构。
所以我发现 Swift 2 很久以前就引入了对递归枚举的支持 - 源代码,虽然这很棒。 我没有找到针对这个特定问题的任何 SO 问题,即也已回答的通用递归枚举( 此处)。 我现在将总结最终(部分解决方案)代码。
事实证明,我需要用数据结构来描述问题所在。 本质上,我们希望.left
包含Left
或RecursiveEither<Left, Right>
类型的值。 有了这个简单的想法,我们可以创建两个枚举 - 一个用于包装器枚举,一个用于嵌套枚举,它可以采用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
}
}
}
呼叫站点很适合这种设计:
let e = RecursiveEither<Int, Int>.left(.left(.left(.left(.value(3)))))
话虽如此,这个答案仍然存在限制 - 假设Left
和Right
即使在嵌套的Either
中也不会改变。 此外,可以仅使用一种indirect
情况来设计这种不同的方式,以将Either
类型包装在另一个关联值中——这可能更有效,因为在运行时将更少的帧推入调用堆栈。
我已经发布了一个关于这个问题的尝试的要点。
如果有人有改进此实现的建议,请随时添加。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.