2012-09-23 3 views
1

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

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) } 
} 

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

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

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

+0

Это также актуально: https://tpolecat.github.io/2015/04/29/f-bounds.html – Daenyth

ответ

3

Я думаю, что вы хотите использовать собственный тип, который будет гарантировать, что экземпляр 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.

+0

Большое спасибо. Это не только зафиксировало мой пример с игрушкой, это была именно проблема в более крупном коде. – jmount

Смежные вопросы