В scala, как сделать внутренний тип абстрактного типа инвариантным для разных экземпляров? - PullRequest
0 голосов
/ 04 марта 2019

В некоторых случаях я хотел бы использовать внутренний тип в качестве альтернативы универсального типа, например, в следующем случае вместо определения Dependent[T <: BaseType] я определяю его следующим образом:

abstract class BaseType {

  type N
  def create(v: Int): N

  class Dependent(val n: N) {

    def +(other: Dependent) = new Dependent(create(n.hashCode() + n.hashCode()))
  }
}

object BaseType {

  class Aggregator[T <: BaseType](val seq: Seq[T#Dependent]) {

    def result() = seq.reduce(_ + _)
  }
}

Очевидно, этоне будет компилироваться, потому что Dependent теперь является классом Inner, а разные Dependent, созданные в разных экземплярах BaseType, не могут взаимодействовать друг с другом.(Кстати, будет сгенерирована следующая ошибка компиляции)

Error:(15, 35) type mismatch;
 found   : T#Inner
 required: _33.Inner where val _33: T
    def result() = seq.reduce(_ + _)

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

static class Dependent { ...

Нет смысла иметь более длинный код в Scala, чем Java.Непосредственной аналогией в scala будет добавление принудительного правила в BaseType, которое требует, чтобы все его реализации были объектами / синглтонами, а не классами.Однако я не видел такой возможности.

Итак, вопрос в том, КАКОЙ КРАТКИЙ способ сделать то же самое, что легко сделать в Java?

ОБНОВЛЕНИЕ очевидно, я не совсем понимал свое намерение. Я хочу не использовать класс Dependent как есть, а расширять BaseType таким образом, чтобы BaseType служил параметром типа для Dependent, например, если я определяю 2 объекта:

  object Sub1 extends BaseType {
    override type N = Long
    override def create(v: Int): N = v.toLong
  }
  object Sub2 extends BaseType {
    override type N = Double
    override def create(v: Int): N = v.toDouble
  }

Тогда оба знака + и Aggregator могут использоваться на 2 зависимых, только если они принадлежат одному и тому же объекту, что означает, что это удастся:

  assert(
    new Aggregator(
      Seq(
        new Sub1.Dependent(1),
        new Sub1.Dependent(2)
      )).result() == new Sub1.Dependent(1) +
      new Sub1.Dependent(2)
  )

но это не удастся:

  assert(
    new Aggregator(
      Seq(
        new Sub1.Dependent(1),
        new Sub2.Dependent(2)
      )).result() == new Sub1.Dependent(1) +
      new Sub2.Dependent(2)
  )

, потому что 2 зависимых экземпляра имеют разные типы, однако с последним компилятором scala даже первый выйдет из строя, потому что к моменту определения Агрегатора он не знает T <: BaseType является синглтоном.

Ответы [ 2 ]

0 голосов
/ 04 марта 2019

, потому что в Java такую ​​задачу легко выполнить:

static class Dependent { ...

Нет, задача не выполняется, потому что Dependent не может использовать N (даже если это параметр типа BaseType).Вам понадобится static class Dependent<N>, и эквивалент Scala будет

object BaseType {
  class Dependent[N]
}

Это правда, что это немного дольше, чем Java, если у вас еще нет объекта-компаньона, но это просто не является обязательным требованием в дизайне Scalaчто он может делать все, что может сделать Java с более коротким кодом (или вообще, на самом деле).

Если вы хотите

, то и знак +, и агрегатор могут использоваться только на 2 зависимыхесли они принадлежат одному и тому же объекту, это означает, что это удастся

, это можно сделать:

class Aggregator[T <: BaseType with Singleton](val seq: Seq[T#Dependent]) {
  def result() = seq.reduce(_ + _)
}
0 голосов
/ 04 марта 2019

Я не уверен, что это то, что вы хотите, возможно, вы чрезмерно упростили вашу реальную проблему.
Но, в противном случае, это не решило бы вашу проблему?

sealed trait BaseType[N] {
  def create(v: Int): N
}

object BaseType {
  final implicit val LongBase: BaseType[Long] = new BaseType[Long] {
    override def create(v: Int): Long = v.toLong
  }

  final implicit val DoubleBase: BaseType[Double] = new BaseType[Double] {
    override def create(v: Int): Double = v.toDouble
  }

  final class Aggregator[N : BaseType](val seq: Seq[Depedent[N]]) {
    def result: Depedent[N] = seq.reduce(_ + _)
  }
}

final class Depedent[N](val n: N)(implicit base: BaseType[N]) {
  def + (that: Depedent[N]): Depedent[N] =
    new Depedent(base.create(this.n.hashCode + that.n.hashCode))
}

Если этоне удовлетворяет вашей проблеме, пожалуйста, оставьте комментарий, разъясняющий, почему это не так.

...