Почему я получаю сообщение об ошибке: аргументы типа не соответствуют ошибке компилятора признаков?

Я застрял на ошибке компилятора Scala (2.11.7) в IntelliJ (14.1.5) уже несколько часов. Я гуглил, ломал голову и экспериментировал с кучей вариаций, но, похоже, не могу обойти это.

Я взял контекст кода и обрезал его как можно меньше (включая отсечение любых зависимостей от внешних библиотек), чтобы захватить домен, который я использовал, чтобы попытаться сузить проблему. Я думал, что использую довольно простой идиоматический Scala; case-объекты, трейты с параметрами типа и т.д.

Четвертая с последней строки, начинающаяся с abstract case class CoordinateRadian, генерирует эту ошибку компилятора Scala:

Ошибка: (128, 129) аргументы типа [Test.this.CoordinateRadian.unitBase.type, Test.this.LongitudeRadian, Test.this.LatitudeRadian] не соответствуют ограничениям параметра типа типажа Coordinate [U ‹: Test.this.Angle .UnitBase.Member,+O ‹: Test.this.Longitude[U],+A ‹: Test.this.Latitude[U]] класс абстрактного случая CoordinateRadian private[CoordinateRadian] (долгота: LongitudeRadian, широта: LatitudeRadian) расширяет координату [CoordinateRadian.unitBase.type, LongitudeRadian, LatitudeRadian] {

Вот код с ошибкой компилятора:

package org.public_domain

class Test {
  object Angle {
    object UnitBase {
      trait Member
      case object RADIAN extends Member
      case object DEGREE extends Member
    }
    trait UnitBase[U <: Angle.UnitBase.Member] {
      def unitBase: U
    }

    object ZeroBase {
      trait Member
      case object LEFT extends Member
      case object MIDDLE extends Member
    }
    trait ZeroBase[U <: Angle.UnitBase.Member, Z <: Angle.ZeroBase.Member] extends Angle.UnitBase[U] {
      def zeroBase: Z
    }
  }
  trait Angle[U <: Angle.UnitBase.Member, Z <: Angle.ZeroBase.Member] extends Angle.ZeroBase[U, Z] {
    def theta: Double
  }

  trait Angle__ObjectBase {
    def basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member)
  }

  object AngleRadianMiddle extends ((Double) => AngleRadianMiddle) with Angle__ObjectBase {
    val basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member) =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): AngleRadianMiddle =
      new AngleRadianMiddle(theta) {
        def unitBase =
          basesAsTuple._1

        def zeroBase =
          basesAsTuple._2

        private def readResolve(): Object =
          AngleRadianMiddle(theta)

        def copy(thetaNew: Double = theta): AngleRadianMiddle =
          AngleRadianMiddle(thetaNew)
      }
  }
  abstract case class AngleRadianMiddle private[AngleRadianMiddle] (theta: Double) extends Angle[AngleRadianMiddle.basesAsTuple._1.type, AngleRadianMiddle.basesAsTuple._2.type] {
    def copy(thetaNew: Double = theta): AngleRadianMiddle
  }

  trait Itude[U <: Angle.UnitBase.Member] extends Angle[U, Angle.ZeroBase.MIDDLE.type] {
    val zeroBase =
      Angle.ZeroBase.MIDDLE
  }

  sealed trait Longitude[U <: Angle.UnitBase.Member] extends Itude[U]
  sealed trait Latitude[U <: Angle.UnitBase.Member] extends Itude[U]

  object LongitudeRadian extends ((Double) => LongitudeRadian) {
    val basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member) =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): LongitudeRadian =
      new LongitudeRadian(theta) {
        def unitBase =
          basesAsTuple._1

        private def readResolve(): Object =
          LongitudeRadian(theta)

        def copy(thetaNew: Double = theta): LongitudeRadian =
          LongitudeRadian(thetaNew)
      }
  }
  abstract case class LongitudeRadian(theta: Double) extends Longitude[LongitudeRadian.basesAsTuple._1.type] {
    def copy(thetaNew: Double = theta): LongitudeRadian
  }

  object LatitudeRadian extends ((Double) => LatitudeRadian) {
    val basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member) =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): LatitudeRadian =
      new LatitudeRadian(theta) {
        def unitBase =
          basesAsTuple._1

        private def readResolve(): Object =
          LatitudeRadian(theta)

        def copy(thetaNew: Double = theta): LatitudeRadian =
          LatitudeRadian(thetaNew)
      }
  }
  abstract case class LatitudeRadian(theta: Double) extends Latitude[LatitudeRadian.basesAsTuple._1.type] {
    def copy(thetaNew: Double = theta): LatitudeRadian
  }

  trait Coordinate[U <: Angle.UnitBase.Member, +O <: Longitude[U], +A <: Latitude[U]] {
    def longitude: O
    def latitude: A

    val x: O =
      longitude
    val y: A =
      latitude
  }

  object CoordinateRadian extends ((LongitudeRadian, LatitudeRadian) => CoordinateRadian) {
    val unitBase: Angle.UnitBase.Member =
      Angle.UnitBase.RADIAN

    def apply(longitude: LongitudeRadian, latitude: LatitudeRadian): CoordinateRadian =
      new CoordinateRadian(longitude, latitude) {
        private def readResolve(): Object =
          CoordinateRadian(longitude, latitude)

        def copy(longitudeNew: LongitudeRadian = longitude, latitudeNew: LatitudeRadian = latitude): CoordinateRadian =
          CoordinateRadian(longitudeNew, latitudeNew)
      }
  }
  //abstract case class CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, Longitude[CoordinateRadian.unitBase.type], Latitude[CoordinateRadian.unitBase.type]] {
  abstract case class CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, LongitudeRadian, LatitudeRadian] {
    def copy(longitudeNew: LongitudeRadian = longitude, latitudeNew: LatitudeRadian = latitude): CoordinateRadian
  }
}

Вы заметите, что пятая строка с конца файла закомментирована (и очень похожа на четвертую строку с конца файла). Если вы раскомментируете пятую часть с последней строки, а затем закомментируете четвертую с последней строки и скомпилируете код («Построить все» в IntelliJ), будут выданы две ошибки:

Ошибка: (125, 67) переопределение метода longitude в свойстве Coordinate типа => Test.this.Longitude[Test.this.CoordinateRadian.unitBase.type]; значение долготы имеет несовместимый тип абстрактный класс класса CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, Longitude[CoordinateRadian.unitBase.type], Latitude[CoordinateRadian.unitBase.type]] {

Ошибка: (125, 95) переопределение метода широты в свойстве Coordinate типа => Test.this.Latitude[Test.this.CoordinateRadian.unitBase.type]; значение широты имеет несовместимый тип абстрактный класс класса CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, Longitude[CoordinateRadian.unitBase.type], Latitude[CoordinateRadian.unitBase.type]] {

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


person chaotic3quilibrium    schedule 07.10.2015    source источник


Ответы (1)


Что ж, из-за ограничений на Coordinate вам нужно иметь LongitudeRadian <: Longitude[CoordinateRadian.unitBase.type] (и аналогично для широты), что неверно, потому что LatitudeRadian.basesAsTuple._1.type не является подтипом CoordinateRadian.unitBase.type (компилятор знает только, что LatitudeRadian.basesAsTuple._1 является Angle.UnitBase.Member). Если вы просто удалите аннотации типов из basesAsTuple и unitBase, компилятор без проблем выведет одноэлементные типы.

class Test {
  object Angle {
    object UnitBase {
      trait Member
      case object RADIAN extends Member
      case object DEGREE extends Member
    }
    trait UnitBase[U <: Angle.UnitBase.Member] {
      def unitBase: U
    }

    object ZeroBase {
      trait Member
      case object LEFT extends Member
      case object MIDDLE extends Member
    }
    trait ZeroBase[U <: Angle.UnitBase.Member, Z <: Angle.ZeroBase.Member] extends Angle.UnitBase[U] {
      def zeroBase: Z
    }
  }
  trait Angle[U <: Angle.UnitBase.Member, Z <: Angle.ZeroBase.Member] extends Angle.ZeroBase[U, Z] {
    def theta: Double
  }

  trait Angle__ObjectBase {
    def basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member)
  }

  object AngleRadianMiddle extends ((Double) => AngleRadianMiddle) with Angle__ObjectBase {
    val basesAsTuple =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): AngleRadianMiddle =
      new AngleRadianMiddle(theta) {
        def unitBase =
          basesAsTuple._1

        def zeroBase =
          basesAsTuple._2

        private def readResolve(): Object =
          AngleRadianMiddle(theta)

        def copy(thetaNew: Double = theta): AngleRadianMiddle =
          AngleRadianMiddle(thetaNew)
      }
  }
  abstract case class AngleRadianMiddle private[AngleRadianMiddle] (theta: Double) extends Angle[AngleRadianMiddle.basesAsTuple._1.type, AngleRadianMiddle.basesAsTuple._2.type] {
    def copy(thetaNew: Double = theta): AngleRadianMiddle
  }

  trait Itude[U <: Angle.UnitBase.Member] extends Angle[U, Angle.ZeroBase.MIDDLE.type] {
    val zeroBase =
      Angle.ZeroBase.MIDDLE
  }

  sealed trait Longitude[U <: Angle.UnitBase.Member] extends Itude[U]
  sealed trait Latitude[U <: Angle.UnitBase.Member] extends Itude[U]

  object LongitudeRadian extends ((Double) => LongitudeRadian) {
    val basesAsTuple =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): LongitudeRadian =
      new LongitudeRadian(theta) {
        def unitBase =
          basesAsTuple._1

        private def readResolve(): Object =
          LongitudeRadian(theta)

        def copy(thetaNew: Double = theta): LongitudeRadian =
          LongitudeRadian(thetaNew)
      }
  }
  abstract case class LongitudeRadian(theta: Double) extends Longitude[LongitudeRadian.basesAsTuple._1.type] {
    def copy(thetaNew: Double = theta): LongitudeRadian
  }

  object LatitudeRadian extends ((Double) => LatitudeRadian) {
    val basesAsTuple =
      (Angle.UnitBase.RADIAN, Angle.ZeroBase.MIDDLE)

    def apply(theta: Double): LatitudeRadian =
      new LatitudeRadian(theta) {
        def unitBase =
          basesAsTuple._1

        private def readResolve(): Object =
          LatitudeRadian(theta)

        def copy(thetaNew: Double = theta): LatitudeRadian =
          LatitudeRadian(thetaNew)
      }
  }
  abstract case class LatitudeRadian(theta: Double) extends Latitude[LatitudeRadian.basesAsTuple._1.type] {
    def copy(thetaNew: Double = theta): LatitudeRadian
  }

  trait Coordinate[U <: Angle.UnitBase.Member, +O <: Longitude[U], +A <: Latitude[U]] {
    def longitude: O
    def latitude: A

    val x: O =
      longitude
    val y: A =
      latitude
  }

  object CoordinateRadian extends ((LongitudeRadian, LatitudeRadian) => CoordinateRadian) {
    val unitBase =
      Angle.UnitBase.RADIAN

    def apply(longitude: LongitudeRadian, latitude: LatitudeRadian): CoordinateRadian =
      new CoordinateRadian(longitude, latitude) {
        private def readResolve(): Object =
          CoordinateRadian(longitude, latitude)

        def copy(longitudeNew: LongitudeRadian = longitude, latitudeNew: LatitudeRadian = latitude): CoordinateRadian =
          CoordinateRadian(longitudeNew, latitudeNew)
      }
  }
  //abstract case class CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, Longitude[CoordinateRadian.unitBase.type], Latitude[CoordinateRadian.unitBase.type]] {

  abstract case class CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[CoordinateRadian.unitBase.type, LongitudeRadian, LatitudeRadian] {
    def copy(longitudeNew: LongitudeRadian = longitude, latitudeNew: LatitudeRadian = latitude): CoordinateRadian
  }
}

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

abstract case class LongitudeRadian(theta: Double) extends Longitude[RADIAN.type]

abstract case class CoordinateRadian private[CoordinateRadian] (longitude: LongitudeRadian, latitude: LatitudeRadian) extends Coordinate[RADIAN.type, LongitudeRadian, LatitudeRadian]

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

person Alexey Romanov    schedule 07.10.2015
comment
Потрясающий! тысм! Я очень ценю ваш ответ. Конечно, возникают следующие вопросы... почему мое повторение ТОЧНО ОДНОЙ информации о типе в этих объявлениях (если идентичность - это идентичность...) вызывает путаницу у компилятора? Я столкнулся со многими проблемами компилятора в другом направлении (неправильный вывод типов), я предпочитал указывать типы по умолчанию. Очень неприятно видеть, что теперь даже чрезмерное указание (на основе идентичности) может вызвать проблемы с компилятором. - person chaotic3quilibrium; 07.10.2015
comment
И как, во аиде, ты придумал свой ответ? Что заставило вас отказаться от чрезмерной спецификации типов для basesAsTuple и unitBase? Я просто пытаюсь понять, как я мог бы сделать то же самое для себя в будущем. Есть ли у вас какой-то дополнительный инструмент, который вы используете, который показывает больше того, что делает компилятор, и о чем он запутался, что привело вас к этим конкретным объявлениям? Это не тот маршрут, который я бы когда-либо рассматривал (учитывая мое предположение о личности). - person chaotic3quilibrium; 07.10.2015
comment
Как я уже сказал, просто взглянув на ограничения для Coordinate и параметры типа CoordinateRadian и LatitudeRadian, стало ясно. Вы не повторяете одну и ту же информацию: компилятор делает вывод, например. val basesAsTuple: (Angle.UnitBase.RADIAN.type, Angle.ZeroBase.MIDDLE.type) в LatitudeRadian, не val basesAsTuple: (Angle.UnitBase.Member, Angle.ZeroBase.Member). Таким образом, он знает, что LatitudeRadian.basesAsTuple._1 равно Angle.UnitBase.RADIAN, а LatitudeRadian.basesAsTuple._1.type совпадает с Angle.UnitBase.RADIAN.type, чего требует ограничение. - person Alexey Romanov; 08.10.2015
comment
Итак, есть ли явный способ добавить спецификатор типа в basesAsTuple и сохранить предполагаемое значение? Или это требует, чтобы я опустил спецификатор типа, поскольку это единственный способ, которым компилятор может сделать скачок вывода? Я очень надеюсь, что это первое, так как последнее - довольно неприятная и раздражающая ловушка. - person chaotic3quilibrium; 08.10.2015
comment
Да, и это указано в комментарии выше: val basesAsTuple: (Angle.UnitBase.RADIAN.type, Angle.ZeroBase.MIDDLE.type). - person Alexey Romanov; 08.10.2015
comment
Хм. Это означает, что везде, где я хочу использовать объект case, мне нужно знать, использую ли я его просто как значение, а где просто как тип. Или, к вашему сведению, если я полностью пропущу спецификацию (за исключением исходной спецификации в родительском трейте), компилятор сможет сделать вывод о соотношении значения к типу за меня. Меня все еще что-то не устраивает в этом. Я бы очень хотел, чтобы был более наглядный способ представить это, чтобы я мог вникнуть во все это и увидеть общий принцип, который я, вероятно, упускаю. - person chaotic3quilibrium; 08.10.2015