在Scala中指定或实现具有变体类型的类型类

时间:2013-12-14 01:07:57

标签: scala variant

我是Scala的新手,需要一些帮助来解决编译错误:

[error] .../traversals  /traversals.scala:120: type mismatch;
[error]  found   : Traversable[Tree]
[error]  required: Traversable[Node]
[error] Note: Tree >: Node, but trait Traversable is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error]             println ("Sorted " + sorted (tree2) (monadApp,inOrder));
[error]                                                           ^
[error] one error found

我很抱歉MWE那么久了,但是当我想写一些利用它们的例子时,我以一种天真的方式将一些类型的类从Haskell转换为Scala。 我不完全理解这个问题,但似乎我的Traversable特征不允许T被某个子类型或具体实例inOrder或函数{替换{1}}使用该实例。根据编译器的建议,我尝试在sorted特征的-前面添加一些TTraversable定义前面的TreeinOrder前面的T,但它没有帮助。

sorted

1 个答案:

答案 0 :(得分:5)

问题是您有两种不同的类型:Traversable[Node]Traversable[Tree]。这来自Haskell的ADT翻译。在Haskell中NodeEmpty都是Tree,而在Scala中,它们是Tree子类型,这会导致方差的概念发挥作用:给定A B的子类型,T[A]的子类型为T[B]

如果我们查看函数X => Y的类型,我们会看到它被声明为Function1[-X, +Y],即X中的逆变和{{1}中的协变}。这意味着需要Y并返回Tree的函数是一个函数的子类型,它接受Node并返回Node。例如:

Tree

函数def f[T](n: Node[T])(func: Node[T] => Tree[T]): Tree = func(n) def zeroRoot(tree: Tree[Int]): Node[Int] = Node(0, tree, Empty()) f(Node(1, Empty(), Empty())(zeroRoot) 有效,因为我们传递的是zeroRoot,它期望Node(没关系),我们返回Tree,其中转弯作为Node返回(也可以)。

顺便说一下,Tree可能应该是共变体。

现在,另一个反方差的例子是排序类。虽然Scala由于某些技术性而不变,但正确的排序应该是对立的。毕竟,如果您知道如何订购Tree,那么您可以订购Tree,因此Node将是Ordering[Tree]的子类型,因为第一个可以在第二个预期的地方通过。

我并没有真正密切关注代码,但有意义的是,用作排序的东西应该是反变量的。另一方面,似乎不可能使Ordering[Node]逆变,因为Traversable类型签名需要不变性。

通过将Functor类型明确传递给Tree来解决此问题,或明确将Sort声明为tree2。然后你会得到其他不同的问题! :)