如何在Scala中实现不变列表?

时间:2015-12-06 14:52:28

标签: scala

我想在Scala中实现一个链接列表,其中type参数是不变的,与标准库List不同。

这是一次尝试:

sealed trait InvariantList[T]
case object Nil extends InvariantList[_]
case class Cons[T](head : T, tail : InvariantList[T]) extends InvariantList[T]

object InvariantList {
  def map[A, B](xs : InvariantList[A])(f : A => B) : InvariantList[B] = {
    xs match {
      case Nil => Nil[B]
      case Cons(head, tail) => Cons(f(head), map(tail)(f))
    }
  }
}

object Example {
  val xs = Cons(7, Cons(5, Nil[Int]))
  InvariantList.map(xs)(_ + 1)
}

这非常接近我想要的,但是在Nil的实现和示例使用中必须指定map的类型参数是很烦人的。

我也尝试过使用case class Nil[T]() extends InvariantList[T],但这也很难看,因为我必须将这些问题全部放在一起。

在不变链表实现中定义Nil的推荐方法是什么?我认为这个问题更普遍适用于任何具有空构造函数的不变数据结构。我希望它与标准库List一样方便使用(方便的是我不必指定类型参数或添加parens)。

1 个答案:

答案 0 :(得分:3)

标准库的不变集合没有Nil元素。例如,SetListBuffer是不变的,但它们没有Nil子类型。他们要求您说Set.empty[A]ListBuffer.empty[A]

scala> val l : ListBuffer[Int] = Nil
<console>:11: error: type mismatch;
 found   : scala.collection.immutable.Nil.type
 required: scala.collection.mutable.ListBuffer[Int]
       val l : ListBuffer[Int] = Nil
                                 ^

scala> val set: Set[Int] = Nil
<console>:11: error: type mismatch;
 found   : scala.collection.immutable.Nil.type
 required: Set[Int]
       val set: Set[Int] = Nil
                           ^

单个case object Nil extends InvariantList[_]由于不变性而无法工作。每种列表类型都需要它自己的空表示。因为空List[Dog]与空List[Animal]不一样,如果Dog <: Animal等,

您基本上要求的是单个符号Nil,可以像许多不同类型一样对待。我认为你在案例类的正确轨道上,因为每个列表类型允许你一个Nil[A]。遗憾的是,永远不会允许您在Nil上进行模式匹配,只允许Nil(),因为您需要一个带参数的提取器,而不是一个单独的提取器(您的Nil不是)。如果你想方便不写类型参数和括号,你可以在同一个包中编写这样的方法:

def nil[A] = Nil[A]()

它并不理想,但它会部分解决您的问题。把它们放在一起:

sealed trait InvariantList[A]

case class Nil[A]() extends InvariantList[A]

case class Cons[A](head : A, tail : InvariantList[A]) extends InvariantList[A]

object InvariantList {

  def map[A, B](xs : InvariantList[A])(f : A => B) : InvariantList[B] = {
    xs match {
      case Nil() => nil
      case Cons(head, tail) => Cons(f(head), map(tail)(f))
    }
  }

}

scala> val xs = Cons(7, Cons(5, nil))
xs: Cons[Int] = Cons(7,Cons(5,Nil()))

scala> InvariantList.map(xs)(_ + 1)
res0: InvariantList[Int] = Cons(8,Cons(6,Nil()))