繁体   English   中英

递归 Swift 具有通用关联值的枚举

[英]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 中收到神秘的诊断错误消息,我无法解决:

  1. '替换(leftie as? NumericEitherEnum<Int, Float>)? NumericEitherEnum<Int, Float> '
  2. '枚举案例'left'不能用作实例成员'

我想要达到的目标:

print(NumericEitherEnum<NumericEitherEnum<Int, Float>, NumericEitherEnum<Int, Float>>.left(.left(3)).left ?? "failed :(") // Parses the innermost value 3

修复它不会修复错误或建议如何实际解决根本原因。 我认为这可能是编译器解析的边缘情况(甚至可能是错误);)。 我也意识到实际上返回IntLeft? 类型,但有什么方法可以在类型系统中表达这个(我尝试了关联类型,但我仍然不知道如何使这个动态)。 处理具有不同泛型类型签名的嵌套枚举也存在问题(不知道如何表达)。

我怎样才能更好地解决这个问题? 理想情况下,我希望有一个干净的调用站点,没有太多间接和额外的数据结构,但如果这实际上无法实现,我愿意尝试不同的数据结构。

所以我发现 Swift 2 很久以前就引入了对递归枚举的支持 - 源代码,虽然这很棒。 我没有找到针对这个特定问题的任何 SO 问题,即也已回答通用递归枚举( 此处)。 我现在将总结最终(部分解决方案)代码。

事实证明,我需要用数据结构来描述问题所在。 本质上,我们希望.left包含LeftRecursiveEither<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)))))

话虽如此,这个答案仍然存在限制 - 假设LeftRight即使在嵌套的Either中也不会改变。 此外,可以仅使用一种indirect情况来设计这种不同的方式,以将Either类型包装在另一个关联值中——这可能更有效,因为在运行时将更少的帧推入调用堆栈。

我已经发布了一个关于这个问题的尝试的要点

如果有人有改进此实现的建议,请随时添加。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM