Как добиться параметризации типа, зависящего от аргумента и пути, в конструкторе?

(используя Скала 2.11.12)

Почему это компилируется?

  sealed trait Inner

  sealed trait Outer {
    sealed trait I extends Inner
  }

  case object OuterA extends Outer {
    case object Inner1 extends I
    case object Inner2 extends I
  }

  case object OuterB extends Outer {
    case object Inner1 extends I
  }

  class Data[O <: Outer](outer: O, inner: O#I)
  // I expected this not to compile but it actually does
  val data = new Data(OuterA, OuterB.Inner1)

Почему это не компилируется?

  sealed trait Inner

  sealed trait Outer {
    type I <: Inner
  }

  case object OuterA extends Outer {
    sealed trait OuterAInner extends Inner
    override type I = OuterAInner
    case object Inner1 extends OuterAInner
    case object Inner2 extends OuterAInner
  }

  case object OuterB extends Outer {
    sealed trait OuterBInner extends Inner
    override type I = OuterBInner
    case object Inner1 extends OuterBInner
  }

  class Data[O <: Outer](outer: O, inner: O#I)
  // I expected this to compile but it actually does not
  val data = new Data(OuterA, OuterA.Inner1)
  // type mismatch;
  //   found   : com.transparencyrights.ermine.model.V1.OuterA.Inner1.type
  //   required: ?#I
  //  Note that Inner1 extends Any, not AnyRef.
  //  Such types can participate in value classes, but instances
  //  cannot appear in singleton types or in reference comparisons.
  //    val data = new Data(OuterA, OuterA.Inner1)

Чего я хочу добиться, так это уникального конструктора Data, который принимает два аргумента, Outer и Inner, с типом Inner, ограниченным подтипом Inner, зависящим от данного экземпляра Outer.


person M. Karassev    schedule 02.08.2019    source источник


Ответы (1)


В обоих случаях проблема заключается в том, что O#I не делает то, что вы хотите.
На самом деле это относится не к I внутри конкретного O, которое у вас есть, а к общему внутри Outer.

Вы можете исправить оба фрагмента, используя типы, зависящие от пути, и ограничения обобщенного типа.

Дело 1

sealed trait Inner

sealed trait Outer {
  sealed trait I extends Inner
}

final case object OuterA extends Outer {
  final case object Inner1 extends I
  final case object Inner2 extends I
}

final case object OuterB extends Outer {
  final case object Inner1 extends I
}

final class Data[O <: Outer, I <: O#I] private (outer: O, inner: I)

object Data {
  final def apply[O <: Outer, I <: O#I](outer: O, inner: I)(implicit ev: I <:< outer.I): Data[O, I] =
    new Data(outer, inner)
}

val data = Data(OuterA, OuterB.Inner1) // Does not compile.
val data = Data(OuterA, OuterA.Inner1) // Does compile.

Случай 2

sealed trait Inner

sealed trait Outer {
  type I <: Inner
}

final case object OuterA extends Outer {
  override final type I = OuterAInner

  sealed trait OuterAInner extends Inner

  final case object Inner1 extends OuterAInner
  final case object Inner2 extends OuterAInner
}

final case object OuterB extends Outer {
  override final type I = OuterBInner

  sealed trait OuterBInner extends Inner

  final case object Inner1 extends OuterBInner
}

final class Data[O <: Outer, I <: O#I] private (outer: O, inner: I)

object Data {
  final def apply[O <: Outer, I <: O#I](outer: O, inner: I)(implicit ev: I <:< outer.I): Data[O, I] =
    new Data(outer, inner)
}

val data = new Data(OuterA, OuterA.Inner1) // Does compile.
val data = new Data(OuterA, OuterB.Inner1) // Does not compile.

Теперь, поскольку примеры не показывают, как вы хотите использовать класс Data и поля внешние и внутренние, этого может быть недостаточно. , но надеюсь, что это поможет вам.
Если у вас есть какие-либо сомнения, не стесняйтесь спрашивать в комментариях.

person Luis Miguel Mejía Suárez    schedule 02.08.2019
comment
На самом деле он работает без ограничений обобщенного типа (implicit ev: I <:< outer.I) в обоих случаях (проверено в Scastie со scala 2.11, 2.12 и 2.13). Достаточно второго параметра универсального типа I <: O#I. Похоже, что O#I в контексте связанного параметра универсального типа ведет себя иначе, чем при использовании в качестве типа параметра, есть ли у вас какие-либо намеки на это? - person M. Karassev; 02.08.2019
comment
@M.Karassev Мм, извините, но нет. Вы также можете использовать список из двух аргументов, например final def apply[O <: Outer](outer: O)(inner: outer.I). - person Luis Miguel Mejía Suárez; 02.08.2019
comment
Это компилируется, как указано в комментариях, или я что-то упустил? scastie.scala-lang.org/veLlWOvgTSOTd6wLaPty8w - person M. Karassev; 02.08.2019
comment
@M.Karassev В этом случае кажется, что вам даже не нужен сопутствующий объект Data: scastie.scala-lang.org/BalmungSan/Iw0K2xrdQoWjcKVNwy9i8Q/2 - Но, как я уже сказал, хитрость в том, как вы хотите использовать все эти вещи. - person Luis Miguel Mejía Suárez; 02.08.2019