Как объявить параметризованную арифметику типа в Scala?

Как объявить в Scala сигнатуры типов, необходимые для реализации нескольких арифметических систем, которые имеют один и тот же признак, объявляющий операции? Я думал, что решил эту проблему, пока не попытался добавить несколько вспомогательных реализаций к базовому признаку/классу. В следующем фрагменте кода (который компилируется) обратите внимание, что sq() определяется как this*self, а не как this*this. И self() тоже не может быть реализован в трейте (нам нужно подождать, пока мы не доберемся до конкретного расширяющего класса, чтобы реализовать его).

trait NumberBase [NUMBERTYPE <: NumberBase[NUMBERTYPE]] {
   // type NUMBERTYPE >: this.type
   def *(that: NUMBERTYPE):NUMBERTYPE
   def self:NUMBERTYPE
   def sq:NUMBERTYPE = { this*self }
}

class D(val v:Double) extends NumberBase[D] {
   def self:D = { this }
   def *(that: D):D = { new D(this.v*that.v) }
}

Вопрос/цель: удалить использование self() или (по крайней мере, переместить реализацию self() в NumberBase) без изменения сигнатуры типа * в D. Многие исправления в приведенной выше ссылке делают производный класс нереализуемый (например, new D() не является типом, возвращаемым * или * имеет неугадываемую сигнатуру типа в D). Я не возражаю против того, что некоторые сигнатуры становятся более уродливыми, но я хотел бы, чтобы код выражал то, что выражает этот код: что производные классы работают только со своими собственными типами и возвращают экземпляры своего собственного типа (они не перемещаются вверх и вниз в иерархии).

Я нашел здесь обсуждение, в котором перечислены некоторые проблемы (но не вижу решения): http://www.scala-lang.org/node/839 . С некоторыми проблемами вы не столкнетесь, пока не увидите и базовый класс, и реализацию.

Полный код с приведенным выше обходным путем (принуждение реализующих классов к реализации self()) приведен здесь: https://github.com/WinVector/AutoDiff, где мы используем тот факт, что разные арифметические системы используют один и тот же базовый класс или трейт для написания функций, которые являются общими для разных арифметических реализаций. Это позволяет нам использовать стандартную машинную арифметику (например, D) или что-то еще (например, систему счисления, которая вычисляет градиенты как побочный эффект).


person jmount    schedule 23.09.2012    source источник
comment
Это также актуально: tpolecat.github.io/2015/04/29 /f-bounds.html   -  person Daenyth    schedule 02.07.2015


Ответы (1)


Я думаю, вы хотите использовать собственный тип, который гарантирует, что экземпляр NumberBase[N] будет также экземпляром типа N:

trait NumberBase[N <: NumberBase[N]] { this: N =>
  def *(that: N): N
  def sq: N = this * this
}

class D(val v: Double) extends NumberBase[D] {
  def *(that: D): D = new D(this.v * that.v)
}

val a = new D(0.5)
val b = new D(0.25)

a * b  // D(0.125)
a.sq   // D(0.25)

НО, если вы действительно хотите определить новый тип числа, вы должны сделать то, что делают сами библиотеки Scala, и использовать класс типов Numeric.

person dhg    schedule 23.09.2012
comment
Спасибо большое. Это не только исправило мой игрушечный пример, но и было проблемой в более крупном коде. - person jmount; 24.09.2012