После двухдневного изучения я наконец-то нашел решение, которое компилируется без предупреждения и проходит мой тест спецификации.Ниже приведен скомпилированный фрагмент моего кода, чтобы показать, что требуется.Тем не менее, обратите внимание, что код не работает, потому что я упустил части для фактического выполнения перестановок:
/**
* 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, x), 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
}
Как видите, решение состоит в том, чтобы определить типы и объекты экстрактора, которые являются локальнымик методу simpifyTop.
Я также включил небольшой пример того, как применить такой смешанный класс к классу Foo.Как вы можете видеть, Foo - чуть больше, чем фабрика для составных перестановок своего собственного типа.Это большое преимущество, если у вас есть много подобных классов, которые иначе не связаны.
Однако я не могу удержаться от того, чтобы сказать, что система типов Scala безумно сложна!Я опытный разработчик библиотеки Java и очень хорошо разбираюсь в Java Generics.Однако мне потребовалось два дня, чтобы выяснить шесть строк кода с определениями трех типов и объектов!Если бы это было не в образовательных целях, я бы отказался от этого подхода.
Прямо сейчас, я испытываю соблазн оракула, что Scala НЕ будет следующей большой вещью с точки зрения языков программирования из-за этой сложности.Если вы Java-разработчик, который сейчас чувствует себя немного неловко из-за обобщений Java (не из-за меня), то вам не понравится система типов Scala, потому что она добавляет, по меньшей мере, инварианты, коварианты и противоречия к концепции обобщений Java.
В целом, система типов Scala, похоже, адресована большему количеству ученых, чем разработчиков.С научной точки зрения приятно рассуждать о безопасности типов программ.С точки зрения разработчиков, время, чтобы выяснить эти детали, потрачено впустую, потому что оно не позволяет им работать с функциональными аспектами программы.Сочетание сопоставления с образцом, смешивания и функций высокого порядка слишком мощное, чтобы его упустить.Однако я считаю, что Scala был бы гораздо более продуктивным языком без его слишком сложной системы типов.