Игнорирование универсального аргумента в середине иерархии типов scala - PullRequest
0 голосов
/ 01 мая 2019

У меня есть параллельная иерархия типов, которые в общем ссылаются друг на друга: один через обычный обобщенный тип, другой через переменную типа.

Однако одна из параллельных иерархий короче другой, и это вызывает у меня проблемы.

Я предоставил фрагмент кода ниже, и ошибки компилятора добавляются в виде встроенных комментариев над строками, которые вызывают проблемы:

  • type arguments [Foo[_],FooKey] do not conform to trait PrimaryKey's type parameter bounds [A <: Entity[A],B <: PrimaryKey[A,B]]
  • overriding type K in trait Entity with bounds <: PrimaryKey[A,Foo.this.K]; type K has incompatible type

Проблема в том, что я хочу, чтобы FooKey не был универсальным, поскольку для FooKey не имеет значения, насколько точно параметризована реализация Foo.

package com.scalatest

object Database {
  trait PrimaryKey[A <: Entity[A], B <: PrimaryKey[A, B]] {
    this: B =>
  }

  trait Entity[A <: Entity[A]] {
    this: A =>

    type K <: PrimaryKey[A, K]
    val id: K
  }

  //Error:(17, 24) type arguments [com.scalatest.Database.Foo[_],com.scalatest.Database.FooKey] do not conform to trait PrimaryKey's type parameter bounds [A <: com.scalatest.Database.Entity[A],B <: com.scalatest.Database.PrimaryKey[A,B]]
  //  class FooKey extends PrimaryKey[Foo[_], FooKey]
  class FooKey extends PrimaryKey[Foo[_], FooKey]

  trait Foo[A <: Foo[A]] extends Entity[A] {
    this: A =>
    //Error:(24, 19) overriding type K in trait Entity with bounds <: com.scalatest.Database.PrimaryKey[A,Foo.this.K];
    // type K has incompatible type
    //    override type K = FooKey
    override type K = FooKey
  }

  class FooImpl(val id: FooKey) extends Foo[FooImpl]
}

Мне удалось использовать экзистенциальный тип вместо Foo[_] в моем объявлении FooKey в сочетании с выполнением всех A ковариантных, но тогда я все еще остался со 2-й ошибкой.

  class FooKey extends PrimaryKey[Foo[A] forSome { type A <: Foo[A] }, FooKey]

1 Ответ

1 голос
/ 01 мая 2019

Это неправда, что

для FooKey не имеет значения, как именно параметризована реализация Foo.

Вы хотите переопределить с помощьюFooKey тип K <: PrimaryKey[A, K], где A такой же, как в trait Entity[A <: Entity[A]] {... И вы делаете это переопределение в trait Foo[A <: Foo[A]] {..., поэтому FooKey должен удовлетворять условию K <: PrimaryKey[A, K] (это не просто верхняя граница, поскольку Kприсутствует в обеих сторонах) для каждого A <: Foo[A], поэтому FooKey следует определять количественно, а не экзистенциально.

Таким образом, вы должны добавить параметр типа к FooKey

  object Database {
    trait PrimaryKey[A <: Entity[A], B <: PrimaryKey[A, B]] {
      this: B =>
    }

    trait Entity[A <: Entity[A]] {
      this: A =>

      type K <: PrimaryKey[A, K]
      val id: K
    }

    class FooKey[A <: Foo[A]] extends PrimaryKey[A, FooKey[A]]

    trait Foo[A <: Foo[A]] extends Entity[A] {
      this: A =>
      override type K = FooKey[A]
    }

    class FooImpl(val id: FooKey[FooImpl]) extends Foo[FooImpl]
  }
...