我有一个关于有限排列集的代数群的用例 . 因为我想将这个组用于各种不相关的排列类,我想把它作为一个混合特性来做 . 这是我尝试的摘录
trait Permutation[P <: Permutation[P]] { this: P =>
def +(that: P): P
//final override def equals(that: Any) = ...
//final override lazy val hashCode = ...
// Lots of other stuff
}
object Permutation {
trait Sum[P <: Permutation[P]] extends Permutation[P] { this: P =>
val perm1, perm2: P
// Lots of other stuff
}
private object Sum {
def unapply[P <: Permutation[P]](s: Sum[P]): Some[(P, P)] = Some(s.perm1, s.perm2)
//def unapply(s: Sum[_ <: Permutation[_]]): Some[(Permutation[_], Permutation[_])] = Some(s.perm1, s.perm2)
}
private def simplify[P <: Permutation[P]](p: P): P = {
p match {
case Sum(a, Sum(b, c)) => simplify(simplify(a + b) + c)
// Lots of other rules
case _ => p
}
}
}
在某个时间点,我想调用简化方法,以便使用代数公理简化组操作的表达式 . 使用模式匹配似乎是有意义的,因为有很多公理需要评估,语法简洁 . 但是,如果我编译代码,我得到:
error: inferred type arguments [P] do not conform to method unapply's type parameter bounds [P <: Permutation[P]]
我不明白为什么编译器无法正确推断类型,我不知道如何帮助它 . 实际上,在这种情况下,当模式匹配时,P的参数类型是无关紧要的 . 如果p是任何排列和,则模式应该匹配 . 返回类型仍然是P,因为转换仅通过在P上调用运算符来完成 .
因此,在第二次尝试中,我交换了注释掉的unapply版本 . 但是,我从编译器(2.8.2)得到一个断言错误:
assertion failed: Sum((a @ _), (b @ _)) ==> Permutation.Sum.unapply(<unapply-selector>) <unapply> ((a @ _), (b @ _)), pt = Permutation[?>: Nothing <: Any]
有什么线索我怎么能让编译器接受这个?
提前致谢!
1 回答
经过两天的繁殖后,我终于找到了一个没有警告的编译解决方案并通过了我的规范测试 . 以下是我的代码的可编辑摘录,以显示所需内容 . 但是请注意,代码是无操作的,因为我遗漏了实际执行排列的部分:
如您所见,解决方案是定义simplifyTop方法本地的类型和提取器对象 .
我还提供了一个如何将这种混合应用于Foo类的小例子 . 正如您所看到的,Foo只不过是一个工厂,可以根据自己的类型进行组合排列 . 如果您有许多这样的课程,那将是一个很大的好处,否则这些课程是无关的 .
<咆哮>
但是,我无法抗拒说Scala的类型系统非常复杂!我是一名经验丰富的Java库开发人员,对Java Generics非常熟练 . 然而,花了两天的时间才弄清楚六行代码和三种类型和对象定义!如果这不是出于教育目的,我会抛弃这种方法 .
现在,我很想知道,由于这种复杂性,Scala不会成为编程语言方面的下一个重大事件 . 如果你是一个Java开发人员,现在对Java泛型感到有些不舒服(不是我),那么你会讨厌Scala的类型系统,因为它至少可以说是对Java泛型概念添加不变量,协变量和逆变量 .
总而言之,Scala的类型系统似乎解决了比开发人员更多的科学家 . 从科学的角度来看,很好地推断一个程序的类型安全性 . 从开发人员的角度来看,弄清楚这些细节的时间是浪费,因为它使他们远离程序的功能方面 .
没关系,我肯定会继续使用Scala . 模式匹配,混合和高阶函数的组合太强大,不容错过 . 但是,如果没有过于复杂的类型系统,我觉得Scala会更高效 .
</咆哮>