Scala中的协变树

看看
scalaz.Tree [A],它在A中是不变的.我正在寻找一个多路树,我可以转储层次结构的值

例如.如果我有ADT

trait MyThing
case object Thing1 extends MyThing
case object Thing2 extends MyThing

我想要一棵MyThings树,我不能在没有对Scalaz进行MyThing演员的情况下使用它;

import scalaz.{Scalaz, Tree}
import Scalaz._
val tree = Thing1.asInstanceOf[MyThing].
               node(Thing2.asInstanceOf[MyThing].leaf)

这有点痛苦.

>是否有树的协变[A]版本?
>为什么树首先是不变的?

最佳答案 首先,我想要第二个Huw建议您使用类型归属而不是使用asInstanceOf进行向下转换.正如Huw所说,如果类型层次结构中的某些更改导致转换无效,则使用类型归属将在编译时而不是运行时失败.对于任何upcast来说,避免使用asInstanceOf也是一种很好的做法.您可以使用asInstanceOf进行向上转换或向下转换,但仅将其用于向下转换可以轻松识别代码中的不安全转换.

回答你的两个问题 – 不,Scalaz中没有协变树类型,原因在于Huw上面链接的pull request中详细讨论的原因.起初这可能看起来非常不方便,但是在Scalaz中避免非不变结构的决定与类似的设计决策有关 – 避免在ADT中使用子类型 – 这使得不变的树等不那么痛苦.

在其他支持ADT的语言中(例如Haskell和OCaml),ADT的叶子不是ADT类型的子类型,而Scala有点不寻常的基于子类型的实现会使类型推断变得混乱.以下是此问题的常见示例:

scala> List(1, 2, 3).foldLeft(None)((_, i) => Some(i))
<console>:14: error: type mismatch;
 found   : Some[Int]
 required: None.type
              List(1, 2, 3).foldLeft(None)((_, i) => Some(i))
                                                     ^

因为累加器的类型是从foldLeft的第一个参数推断出来的,所以它最终为None.type,这几乎没用.您必须提供类型归属(或foldLeft的显式类型参数),这可能非常不方便.

Scalaz试图通过推广使用不返回ADT叶子最特定子类型的ADT构造函数来解决这个问题.例如,它包含没有[A]和一些返回选项[A]的选项的[A](a:A)构造函数.

(有关这些问题的更多讨论,请参阅我的答案herethis related question).

在您的情况下,实现此方法可以像编写以下内容一样简单:

val thing1: MyThing = Thing1
val thing2: MyThing = Thing2

这允许你编写thing1.node(thing2.leaf).如果你想进一步走这条道路,我强烈建议将Argonaut’s Json ADT作为ADT设计的一个很好的例子,它淡化了子类型的作用.

点赞