При расширении черты в пределах черты, что означает "супер"?

Я хочу расширить черту в пределах признака, например:

  trait NodeTypes {
    trait Node {
      def allNodesHaveThis: Int
    }
  }

  trait ScrumptiousTypes extends NodeTypes {
    trait Node extends super.Node {
      def scrumptiousness: Int
    }
  }

  trait YummyTypes extends NodeTypes {
    trait Node extends super.Node {
      def yumminess: Int
    }
  }

  object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {
    case class Node() extends super.Node {
      override def allNodesHaveThis = 1
      override def scrumptiousness = 2  // error: ScrumptiousTypes.Node has been disinherited
      override def yumminess = 3
    }
  }

Если это сработает, было бы неплохо сказать: "Когда ваш Graph наследует от <Whatever>Types, его класс Node должен предоставить методы, требуемые <Whatever>."

Но компилятор Scala 2.11.2 говорит:

error: method scrumptiousness overrides nothing
      override def scrumptiousness = 2
                   ^

Похоже, что YummyTypes.Node тени ScrumptiousTypes.Node, следуя обычному способу, что Scala разрешает наследование "алмаз" для методов: по типу линеаризации. Однако, поскольку я понимаю вещи, это должно быть хорошо, потому что YummyTypes.Node явно расширяет super.Node, который с помощью линеаризации того же типа должен ссылаться на ScrumptiousTypes.

Что я неправильно понял? Или, к чему относится super.Node - и почему?


Если вам интересно, почему я это делаю, так что я могу смешивать изменения сразу по нескольким признакам, поэтому унаследованные черты взаимодействуют, как объясняется в этом вопросе. В конечном классе Node (и других классах, с которыми он работает) я не хочу явно выделяться из каждого признака Node: я хочу смешивать из одной "вещи" (что бы это ни было) и получить все взаимно согласованные изменения, сделанные в Node, и другие черты, все в комплекте. Или, если один признак определяет кучу расширений до Node, расширение от ScrumptiousTypes должно сделать все Node -extensions содержать член scrumptiousness, не указывая всех Node -extensions: trait Hypernode extends ScrumptiousTypes.Node, trait ZealousNode extends ScrumptiousTypes.Node и т.д.

Ответы

Ответ 1

тип использования также устраняет проблему

trait NodeTypes {

  trait Node {
    def allNodesHaveThis: Int
  }

}

trait ScrumptiousTypes extends NodeTypes {

  trait Node extends super.Node {
    def scrumptiousness: Int
  }

  type ScrumptiousTypesNode = this.Node
}

trait YummyTypes extends NodeTypes {

  trait Node extends super.Node {
    def yumminess: Int
  }

  type YummyTypesNode = this.Node
}

object Graph extends NodeTypes with ScrumptiousTypes with YummyTypes {

  case class Node() extends ScrumptiousTypesNode with YummyTypesNode {
    override def allNodesHaveThis = 1

    override def scrumptiousness = 2

    override def yumminess = 3
  }

}

------ v2 ------- используйте объект Node, но поскольку путь зависит, это не очень хорошая идея, и, возможно, это будут проблемы

trait NodeTypes {

  trait Node {
    def allNodesHaveThis: Int
  }

}

object NodeTypes extends NodeTypes

trait ScrumptiousTypes extends NodeTypes {

  trait Node {
    def scrumptiousness: Int
  }

  type ScrumptiousTypesNode = this.Node
}

object ScrumptiousTypes extends ScrumptiousTypes

trait YummyTypes extends NodeTypes {

  trait Node {
    def yumminess: Int
  }

  type YummyTypesNode = this.Node
}

object YummyTypes extends YummyTypes

trait Nodes {

  trait Nodes extends NodeTypes.Node with  YummyTypes.Node with ScrumptiousTypes.Node

}


object Graph extends  Nodes {

  case class Nodes() extends super.Nodes {
    override def yumminess: Int = 1
//
    override def scrumptiousness: Int = 2

    override def allNodesHaveThis: Int = 3
  }

}

Ответ 2

Это связано с линеаризацией класса. См. Spec.

Объяснение

Пусть C - класс с шаблоном C1 with ... with Cn. Тогда линеаризация представляет собой конкатенацию элементов из Cn в C1, заменяя все одинаковые элементы налево. Здесь элементы включают var, val, def, traits, object.

Если вы хотите увидеть порядок линеаризации, используйте

import scala.reflect.runtime.universe._
val tpe = typeOf[scala.collection.immutable.List[_]]
tpe.baseClasses foreach { s => println(s.fullName) }

В вашем случае, если вы измените порядок от ScrumptiousTypes with YummyTypes до YummyTypes with ScrumptiousTypes, тогда ошибка будет на методе yumminess.

Альтернативой @余杰 水 является расширение внутреннего класса, например,

case class Node() extends super[ScrumptiousTypes].Node with super[YummyTypes].Node 

Ответ 3

Это не должно быть ответом. Это просто цитаты из и интерпретации спецификации, которые слишком долго подходят для чтения в комментариях (вызвано johny answer). Я излагаю свои интерпретации, чтобы вы могли определить, где я ошибся. Может быть, это приведет к объяснению или к способу цепочки расширений черт в чертах (или в отчете об ошибке в маловероятном случае, когда моя интерпретация окажется правильной).

Соответствующие проходы из Scala spec

§6.5 Это и супер: ссылка супер.m ссылается статически на метод или тип m в наименее правильный супертип самого внутреннего шаблона, содержащего ссылку. Он оценивает член m 'в фактическом супертипе этого шаблона, который равен m или который переопределяет m.

Большой вопрос: что говорит спецификация super.Node внутри YummyTypes? Чтобы узнать, нам нужно знать определения спецификационных терминов, используемых выше:

§5.1 Шаблоны: шаблон определяет сигнатуру типа, поведение и начальное состояние признака или класса объектов или одного объекта.

Итак, шаблон - это то, что мы обычно называем определением объекта, класса или определения.

§5.4. Черты: признак - это класс, который должен быть добавлен в какой-то другой класс в качестве микса.... Наименее правильным супертипом шаблона является тип класса или составной тип, состоящий из всех его типов родительского класса.

§5.1.2 Линеаризация классов: Пусть C - класс с шаблоном C 1with ... with C <суб > псуб > . Линеаризация C, L (C) определяется следующим образом:

L (C) = C, L (C n) + ⃗... + ⃗ L (C 1)

Здесь + ⃗ обозначает конкатенацию, где элементы правого операнда заменяют одинаковые элементы левого операнда.

Я считаю, что линеаризация - это последовательность классов, которую вы получаете, начиная с определяемого класса и затем читая типы с справа налево. Когда два класса в линеаризации определяют член или тип с тем же именем ( "элемент" ), класс, который на первом месте "выигрывает".

Итак, линеаризация Graph должна быть Graph, YummyTypes, ScrumptiousTypes, NodeTypes, за которой следуют стандартные вещи, такие как Any. Действительно, это подтверждается, когда я изменяю Graph следующим образом:

  object Graph extends ScrumptiousTypes with YummyTypes {
    case class Node() extends super.Node { /* … */ }

    typeOf[Graph.type].baseClasses foreach { s => println(s.fullName) }
  }

который производит:

Graph
YummyTypes
ScrumptiousTypes
NodeTypes
java.lang.Object
scala.Any

§5.4 Черты: предположим, что признак D определяет некоторый аспект экземпляра x типа C (т.е. D является базовым классом C). Тогда фактический супертип D в x является составным типом, состоящим из всех базовых классов в L (C), которые достигают D. Фактический супертип дает контекст для разрешения суперссылки в признаке. Обратите внимание, что фактический супертип зависит от типа, к которому добавляется признак в композиции смеси; он не статически известен во время определения признака.

Я считаю, что "фактический" наименее правильный супертип смешанного признака определяется типом фактического объекта, в котором этот признак смешивается (Graph в моем примере), не обязательно супертип что определение признака явно расширяет (NodeTypes в моем примере).

Заключение

Итак, кажется, что фактический супертип YummyTypes в Graph должен быть ScrumptiousTypes. Итак, фактический супертип YummyTypes.Node в Graph должен быть ScrumptiousTypes.Node.

Однако добавив эту строку в Graph:

typeOf[Node].baseClasses foreach { s => println(s.fullName) }

дает:

Graph.Node
scala.Serializable
java.io.Serializable
scala.Product
scala.Equals
YummyTypes.Node
NodeTypes.Node
java.lang.Object
scala.Any

ScrumptiousTypes.Node отсутствует. По-видимому, внутри YummyTypes, super.Node не относится к Node in YummyTypes "фактическому наименее правильному супертипу.

Однако, если я добавлю:

abstract override def text = super.text + " ScrumptiousTypes" // in ScrumptiousTypes 
abstract override def text = super.text + " YummyTypes"       // in YummyTypes

печать text в Graph вызывает:

 ScrumptiousTypes YummyTypes

демонстрируя, что внутри YummyTypes в Graph, super.text ссылается на ScrumptiousTypes.text!