Проблема дизайна: попытка избежать дублирования кода с помощью ограничений типа scala

Я новичок в Scala, и я пытаюсь использовать ограничения типов, чтобы избежать дублирования кода в следующем коде (очищенном от ненужного):

trait StandardStep1[-I1] {
  def next_step(i:I1):StandardStep2
}

trait StandardStep2

trait UniqueStep1[-I1] extends StandardStep1[I1] {
  def next_step(i:I1):UniqueStep2
}

trait UniqueStep2 extends StandardStep2

class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends StandardStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    new DoubleStandardStep2(new_left, new_right)
  }
}

class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2

class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends UniqueStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    new DoubleUniqueStep2(new_left, new_right)
  }
}

class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2

Объяснение:

StandardStep1 представляет собой шаг в некоторой конечной машине и имеет операцию next_step, которая дает StandardStep2 для данного входа.

UniqueStep1 — это особый тип StandardStep1, который дает UniqueStep2 при вызове next_step — и, очевидно, UniqueStep2 для этого должен наследоваться от StandardStep2.

Теперь мне пришлось написать обертки DoubleStep: DoubleStandardStep1 обертывает также StandardStep1s и возвращает DoubleStandardStep2 при вызове его next_step. DoubleUniqueStep1 делает то же самое, но возвращает DoubleUniqueStep2.

Реализация *DoubleStep*s' next_step имеет очевидное дублирование кода: они оба разбивают входные данные на i_left и i_right и одинаково вызывают свои обернутые шаги next_step.

Мне было интересно, как устранить это дублирование кода, создав общий абстрактный DoubleStep1, который будет выполнять эту часть кода:

object DoubleStepHelper {
  def next_step_args[IL,IR,SL <: StandardStep1[IL],SR <: StandardStep1[IR]](left:SL,right:SR)(i:(IL,IR)) = { 
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    (new_left, new_right)
  }
}

class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends StandardStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    ((l,r) => new DoubleStandardStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i))
  }
}

class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2


class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends UniqueStep1[(IL,IR)] {
  def next_step(i:(IL,IR)) = {
    ((l,r) => new DoubleUniqueStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i))
  }
}

class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2

Попытка скомпилировать этот код (scala 2.9.2) завершается ошибкой с сообщением:

type mismatch;
 found   : (this.StandardStep2, this.StandardStep2)
 required: (this.UniqueStep2, this.UniqueStep2)
    ((l,r) => new DoubleUniqueStep2(l,r)).tupled(DoubleStepHelper.next_step_args(left,right)(i))
                                                                                            ^

Я предполагаю, что это происходит потому, что предполагается, что DoubleStepHelper.next_step_args возвращает значение (StandardStep2, StandardStep2), что плохо, когда мы хотим обернуть его DoubleUniqueStep2.

Можете ли вы придумать способ решить эту проблему? Как сообщить компилятору, что next_step_args DoubleStepHelper может возвращать (UniqueStep2, UniqueStep2), если SL и SR наследуются от UniqueStep1?

Хорошей функцией может быть возможность определить тип результата next_step_args как что-то вроде (SL.next_step, SR.next_step), что означает, что его тип возвращаемого значения создается из типов возвращаемых значений определенных функций ( следующий_шаг) в SL и SR.

Могут ли границы «представления» решить эту проблему каким-либо образом?

Я мог бы принудительно выполнить кастинг, используя asInstanceOf, но это выглядит некрасиво.

Спасибо


person Oren    schedule 04.10.2012    source источник


Ответы (1)


Я нашел хорошее решение. Я создал AbstractStep1 и AbstractDoubleStep1, в которых тип следующего шага указан как параметр типа.

Затем я могу наследовать от этого класса случаи, когда следующим шагом является StandardStep2 или UniqueStep2.

Итак, вот код:

trait AbstractStep1[-I,+S2] {
  def next_step(i:I):S2
}

trait StandardStep1[-I] extends AbstractStep1[I,StandardStep2]

trait StandardStep2

trait UniqueStep1[-I] extends AbstractStep1[I,UniqueStep2] with StandardStep1[I]

trait UniqueStep2 extends StandardStep2

abstract class AbstractDoubleStep1[-IL,-IR,-S2L <: StandardStep2,-S2R <: StandardStep2,+S2](left:AbstractStep1[IL,S2L],right:AbstractStep1[IR,S2R]) extends AbstractStep1[(IL,IR),S2] {
  def wrapper(l:S2L,r:S2R):S2

  def next_step(i:(IL,IR)):S2 = {
    val (i_left, i_right) = i
    val new_left = left.next_step(i_left)
    val new_right = right.next_step(i_right)
    wrapper(new_left, new_right)
  }
}

class DoubleStandardStep1[-IL,-IR](left:StandardStep1[IL], right:StandardStep1[IR]) extends AbstractDoubleStep1[IL,IR,StandardStep2,StandardStep2,DoubleStandardStep2](left,right) {
  def wrapper(l:StandardStep2,r:StandardStep2) = {
    new DoubleStandardStep2(l,r)
  }
}

class DoubleStandardStep2(left:StandardStep2, right:StandardStep2) extends StandardStep2


class DoubleUniqueStep1[-IL,-IR](left:UniqueStep1[IL], right:UniqueStep1[IR]) extends AbstractDoubleStep1[IL,IR,UniqueStep2,UniqueStep2,DoubleUniqueStep2](left,right) {
  def wrapper(l:UniqueStep2,r:UniqueStep2) = {
    new DoubleUniqueStep2(l,r)
  }
}

class DoubleUniqueStep2(left:UniqueStep2, right:UniqueStep2) extends UniqueStep2

Я думаю, что также возможно преобразовать AbstractDoubleStep1 в triat вместо абстрактного класса, что сделает его более полезным в наследовании миксинов, но это не важно для меня в момент.

person Oren    schedule 05.10.2012