8

在 Haskell 中,您可以构造参数化类型,如下例所示:

data Tree a = Leaf | Node a (Tree a) (Tree a)

..然后如果类型参数也是相同类型类的实例,则将它们设为类型类的实例(此处使用 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
}

现在我想做的是提供Serializabletrait 的实现Tree,如果T类型是Serializable.

我可以看到几种方法来部分地做到这一点,但没有一种方法能让我得到类似 Haskell 的结果:

  • :<在抽象类中添加一个类型约束Tree,但是它变得不那么通用了,我假设我可以修改这个Tree
  • 创建一个从Tree[Serializable]to的隐式转换Serializable,但我什至不确定它是否会起作用以及它的鲁棒性如何。

解决问题的好方法是什么?

4

1 回答 1

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上下文绑定的,等效于 type 的隐式参数Serializable[T]。这个隐式参数正在被检索implicitly[Serializable[T]](该方法implicitly[A]返回 type 的隐式参数A)。

该对象SerializableTree包含为树生成序列化程序所需的定义,因此必须导入它才能使用。通常,类型类的通用定义放在类型类本身的对象伴侣上,这使得它无需导入即可使用。

因为我做了Serializable逆变,所以Serializable[Tree[T]]可以在需要 aSerializable[Node[T]]Serializable[Leaf[T]]的地方使用 a 。

于 2013-09-09T09:56:03.413 回答