Scala:不可变性和路径依赖类型兼容性

时间:2013-01-27 03:58:29

标签: scala path-dependent-type

我已就这个主题提出了一些问题,但这次我想进行更一般性的讨论,因为在我看来,Scala缺少一些非常重要的块。

考虑以下代码(从我的实际项目中简化),

trait World {
  type State <: StateIntf
  def evolve(s: State): State
  def initialState: State
}

class Algorithm(world: World) {
  def process(s: world.State) {
    val s1 = world.evolve(s)
    // ... do something with s and s1
  }
}

一切看起来都很美丽和数学,但是

object SomeWorld extends World {...}
new Algorithm(SomeWorld).process(SomeWorld.initialState)  // incompatible type

当然,您可以通过以下方式进行操作

trait World {
  type State <: StateIntf
  var s: State
  def evolve: Unit      // s = next state
  def initialize: Unit  // s = initial state
  def getState: StateIntf = s
}

但我们只是回到可变世界。

我被告知这是因为Scala没有流量分析。如果这是问题,Scala不应该得到那块吗?我只需要compilor可以知道从val传递给val的值是相同的,因此它们的内部类型必须一致。这对我来说似乎很自然,如:

  1. val是涉及scala不变性的最基本概念
  2. 需要路径依赖类型兼容性来模拟具有完全不变性的World之类的东西(从数学角度非常需要)
  3. 传递val的流量分析解决问题
  4. 我要求的太多了吗?或者已经有一种很好的方法来解决它?

2 个答案:

答案 0 :(得分:6)

我认为泛型为这个问题提供了一个更简单的解决方案:

trait World[S <: StateInf] {
  def evolve(s: S): S
  def initialState: S
}

class Algorithm[S <: StateInf](world: World[S]) {
  def process(s: S) {
    val s1 = world.evolve(s)
    // ... do something with s and s1
  }
}

答案 1 :(得分:5)

编译器有时需要一点帮助来证明在使用路径依赖类型时你所做的是合法的。即,正如您所说,编译器缺少流分析,因此我们必须明确告诉它我们不只是使用任何World,我们正在使用SomeWorld,以便我们可以使用{{1 }}。

在您的情况下,如果您更改SomeWorld.initialState,请执行以下操作:

Algorithm

然后以下编译:

class Algorithm[W <: World](world: W) {
  def process(s: world.State) {
    val s1 = world.evolve(s)
    // ... do something with s and s1
  }
}