Scala: абстрактные типы и миксины - PullRequest
2 голосов
/ 12 ноября 2011

Скажем, у меня есть два класса с одинаковым идентификатором для параметризованного типа

  trait A {
    type O
    def f(a: Int, os: Seq[O]): Int
  }

  trait B {
    type O
    def g(a: Int, o: O): Int = { h1(o) + h2(a) }
    def h1(o: O): Int
    def h2(a: Int): Int = {a/2}
  }

Я хотел бы создать дочерний класс, который будет "жениться" на двух

  trait C extends A with B {
    def f(a: Int, os: Seq[O]): Int = {
      os.map{ o => g(a, o) }.sum
    }
  } 

Наконец, я создаю реализацию для C

  class D extends C {
    type O = Int
    def h1(o: O): Int = {5 * o}
  }

При написании C я пока не знаю, что это за тип O - однако я бы хотел ограничить A.O == B.O таким образом, что "имеет смысл" использовать B.g в реализации A.f. Я попытался реализовать это, и на удивление показалось, что Скала предположил, что был только один type O

  val d = new D
  println(d.f(1, Seq(1,2,3)))

Мне это кажется неправильным - почему A.O и B.O согласны?

EDIT Я также хотел бы отметить, что если бы вы вместо этого наложили ограничения на O следующим образом,

  case class Z()
  case class Z1() extends Z
  case class Z2() extends Z1

  trait A {
    type O <: Z
  }

  trait B {
    type O >: Z2
  }


class D extends C {
    type O = Z1

Компиляция не удастся. Однако, если вы установите это вместо,

  trait A {
    type O <: Z
  }

  trait B {
    type O <: Z1
  }
  class D extends C {
    type O = Z2

Компиляция прошла успешно, и все работает нормально.

Ответы [ 2 ]

4 голосов
/ 12 ноября 2011

Я думаю, что Scala всегда «женится» на членах - как типовых, так и на элементах значения - черт, когда оба смешаны в:

trait A { type T = Int }
trait B { type T = String }

object C extends A with B

дает

переопределение типа T в признаке A, что равно Int; введите T в признаке B, что соответствует String нуждается в модификаторе override

(именно так Scala решает проблему множественного наследования - независимо от того, сколько раз идентификатор смешан, он существует только один раз).

2 голосов
/ 12 ноября 2011

Ваш второй пример не удался, потому что Scala нужна небольшая помощь в установлении границ типов в C.Я полагаю, может быть, он должен быть достаточно умен, чтобы понять это самостоятельно, но кто-то, более сведущий в теории типов, должен будет объяснить, почему или почему нет.Это определение для C должно работать:

trait C extends A with B {
  type O >: Z2 <: Z // restrict O's type so it's valid for both A and B
}

Обработка абстрактных типов в Scala аналогична обработке абстрактных методов.Методы с тем же именем (и сигнатурой), которые извлекаются из нескольких признаков, переопределяются вместе.Возьмите классический контрпример, полученный из примера, приведенного в Ye Olde C ++ Language Programming .

trait Cowboy {
  /** Shoot something */
  def draw
}

trait Window {
  /** Draw something */
  def draw
}

class CowboyWindow {
  /** Probably best to draw something on the screen, then shoot it */
  def draw {
    // ...
  }
}

Так же, как предполагается, что в вашем файле есть только один type Oкод, предполагается, что в этом коде есть только один метод draw.В этом случае это предположение проблематично, поскольку вы можете в конечном итоге выстрелить себе в ногу.

Как вы обнаружили в своем примере, обычно это предположение работает лучше всего (подумайте о нескольких интерфейсах, объявляющих close() метод).Но если это предположение проблематично для некоторого класса, как и для CowboyWindow, вы можете заменить наследование композицией, чтобы обойти это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...