Scala编译器无法推断混合类型以进行模式匹配
我有一个关于有限排列集的代数群的用例.因为我想将这个组用于各种不相关的排列类,我想把它作为一个混合特性来做.这是我尝试的摘录
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] 有什么线索我怎么能让编译器接受这个? 提前致谢! 解决方法
经过两天的繁殖后,我终于找到了一个没有警告的编译解决方案并通过了我的规范测试.以下是我的代码的可编辑摘录,以显示所需内容.但是请注意,代码是无操作的,因为我遗漏了实际执行排列的部分:
/** * A generic mix-in for permutations. * <p> * The <code>+</code> operator (and the apply function) is defined as the * concatenation of this permutation and another permutation. * This operator is called the group operator because it forms an algebraic * group on the set of all moves. * Note that this group is not abelian,that is the group operator is not * commutative. * <p> * The <code>*</code> operator is the concatenation of a move with itself for * <code>n</code> times,where <code>n</code> is an integer. * This operator is called the scalar operator because the following subset(!) * of the axioms for an algebraic module apply to it: * <ul> * <li>the operation is associative,* that is (a*x)*y = a*(x*y) * for any move a and any integers x and y. * <li>the operation is a group homomorphism from integers to moves,* that is a*(x+y) = a*x + a*y * for any move a and any integers x and y. * <li>the operation has one as its neutral element,* that is a*1 = m for any move a. * </ul> * * @param <P> The target type which represents the permutation resulting from * mixing in this trait. * @see Move3Spec for details of the specification. */ trait Permutation[P <: Permutation[P]] { this: P => def identity: P def *(that: Int): P def +(that: P): P def unary_- : P final def -(that: P) = this + -that final def unary_+ = this def simplify = this /** Succeeds iff `that` is another permutation with an equivalent sequence. */ /*final*/ override def equals(that: Any): Boolean // = code omitted /** Is consistent with equals. */ /*final*/ override def hashCode: Int // = code omitted // Lots of other stuff: The term string,the permutation sequence,the order etc. } object Permutation { trait Identity[P <: Permutation[P]] extends Permutation[P] { this: P => final override def identity = this // Lots of other stuff. } trait Product[P <: Permutation[P]] extends Permutation[P] { this: P => val perm: P val scalar: Int final override lazy val simplify = simplifyTop(perm.simplify * scalar) // Lots of other stuff. } trait Sum[P <: Permutation[P]] extends Permutation[P] { this: P => val perm1,perm2: P final override lazy val simplify = simplifyTop(perm1.simplify + perm2.simplify) // Lots of other stuff. } trait Inverse[P <: Permutation[P]] extends Permutation[P] { this: P => val perm: P final override lazy val simplify = simplifyTop(-perm.simplify) // Lots of other stuff. } private def simplifyTop[P <: Permutation[P]](p: P): P = { // This is the prelude required to make the extraction work. type Pr = Product[_ <: P] type Su = Sum[_ <: P] type In = Inverse[_ <: P] object Pr { def unapply(p: Pr) = Some(p.perm,p.scalar) } object Su { def unapply(s: Su) = Some(s.perm1,s.perm2) } object In { def unapply(i: In) = Some(i.perm) } import Permutation.{simplifyTop => s} // Finally,here comes the pattern matching and the transformation of the // composed permutation term. // See how expressive and concise the code is - this is where Scala really // shines! p match { case Pr(Pr(a,x),y) => s(a*(x*y)) case Su(Pr(a,Pr(b,y)) if a == b => s(a*(x + y)) case Su(a,Su(b,c)) => s(s(a + b) + c) case In(Pr(a,x)) => s(s(-a)*x) case In(a) if a == a.identity => a.identity // Lots of other rules case _ => p } } ensuring (_ == p) // Lots of other stuff } /** Here's a simple application of the mix-in. */ class Foo extends Permutation[Foo] { import Foo._ def identity: Foo = Identity def *(that: Int): Foo = new Product(this,that) def +(that: Foo): Foo = new Sum(this,that) lazy val unary_- : Foo = new Inverse(this) // Lots of other stuff } object Foo { private object Identity extends Foo with Permutation.Identity[Foo] private class Product(val perm: Foo,val scalar: Int) extends Foo with Permutation.Product[Foo] private class Sum(val perm1: Foo,val perm2: Foo) extends Foo with Permutation.Sum[Foo] private class Inverse(val perm: Foo) extends Foo with Permutation.Inverse[Foo] // Lots of other stuff } 如您所见,解决方案是定义simplifyTop方法本地的类型和提取器对象. 我还提供了一个如何将这种混合应用于Foo类的小例子.正如您所看到的,Foo只不过是一个工厂,可以根据自己的类型进行组合排列.如果您有许多这样的课程,那将是一个很大的好处,否则这些课程是无关的. <咆哮> 但是,我无法抗拒说Scala的类型系统非常复杂!我是一名经验丰富的Java库开发人员,对Java Generics非常熟练.然而,花了两天的时间才弄清楚六行代码和三种类型和对象定义!如果这不是出于教育目的,我会抛弃这种方法. 现在,我很想知道,由于这种复杂性,Scala不会成为编程语言方面的下一个重大事件.如果你是一个Java开发人员,现在对Java泛型感到有些不舒服(不是我),那么你会讨厌Scala的类型系统,因为它至少可以说是对Java泛型概念添加不变量,协变量和逆变量. 总而言之,Scala的类型系统似乎解决了比开发人员更多的科学家.从科学的角度来看,很好地推断一个程序的类型安全性.从开发人员的角度来看,弄清楚这些细节的时间是浪费,因为它使他们远离程序的功能方面. 没关系,我会继续使用Scala.模式匹配,混合和高阶函数的组合太强大,不容错过.但是,如果没有过于复杂的类型系统,我觉得Scala会更高效. < /咆哮> (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |