在Haskell中,您可以构建参数化类型,如以下示例所示:
data Tree a = Leaf | Node a (Tree a) (Tree a)
..然后使它们成为类型类的实例,如果type参数也是同一类型类的实例(这里使用Eq作为示例,它可以是任何东西):
instance (Eq m) => Eq (Tree m) where
Leaf == Leaf = True
Node a b c == Node x y z = (a == x) && (b == y) && (c == z)
_ == _ = False
我想知道Scala中可能会出现类似的事情。要开始,让我们创建参数化类型:
abstract class Tree[T]
case class Leaf[T]() extends Tree [T]
case class Node[T](value: T, left: Tree[T], right: Tree[T]) extends Tree [T]
..并且在实现类型类时选择一个更简单的特性:
trait Serializable {
def toDifferentString(): String
}
如果Serializable
类型为Tree
,我现在希望能够为T
提供Serializable
特征的实现。
我可以看到几种如何部分地完成它的方法,但没有一种能让我得到类似Haskell的结果:
:<
抽象类中添加类型约束Tree
,但随后它变得不那么通用了,我假设我可以修改Tree
类Tree[Serializable]
到Serializable
的隐式转换,但我甚至不确定它是否会起作用以及它会有多强大。解决问题的好方法是什么?
答案 0 :(得分:12)
Scala中的类型类模式如下:
trait Serializable[-T] {
def toDifferentString(v: T): String
}
class SerializableTree[T : Serializable] extends Serializable[Tree[T]] {
def toDifferentString(t: Tree[T]) = t match {
case Leaf() => ""
case Node(v, left, right) =>
val vStr = implicitly[Serializable[T]].toDifferentString(v)
val lStr = toDifferentString(left)
val rStr = toDifferentString(right)
s"Node($vStr, $lStr, $rStr)"
}
}
object SerializableTree {
implicit def st[T : Serializable]: Serializable[Tree[T]] = new SerializableTree[T]
}
Scala中的类型类是作为模式实现的,其中类型类提供了一个或多个方法,这些方法将其提供这些方法的类作为其参数之一。
表示法T : Serializable
是上下文绑定,等同于Serializable[T]
类型的隐式参数。 implicitly[Serializable[T]]
正在检索此隐式参数(方法implicitly[A]
返回类型为A
的隐式参数)。
对象SerializableTree
包含为树生成序列化程序所必需的定义,因此必须导入才能使用它。通常,类型类的常见定义放在类型类本身的对象伴随上,这使得它在没有导入的情况下可用。
由于我提出Serializable
逆变,因此Serializable[Tree[T]]
可用于需要Serializable[Node[T]]
或Serializable[Leaf[T]]
的地方。