какой `product = & gt;` означает в Scala [duplicate]

356
задан Jacek Laskowski 24 December 2014 в 00:02
поделиться

11 ответов

Он преимущественно используется для Injection Dependency , например, в Cake Pattern . Существует великая статья , охватывающая множество различных форм инъекции зависимостей в Scala, включая Pattern Cake. Если вы Google «Cake Pattern и Scala», вы получите много ссылок, включая презентации и видео. На данный момент ссылка на на другой вопрос .

Теперь, что же такое различие между типом self и расширением черты, это просто. Если вы скажете B extends A, то B будет а A. Когда вы используете автотипы, B требует a A. Существуют два особых требования, которые создаются с помощью типов:

  1. Если расширен B, вы должны смешивать A .
  2. Когда конкретный класс, наконец, расширяет / смешивает - в этих признаках некоторый класс / признак должен реализовывать A.

Рассмотрим следующие примеры:

scala> trait User { def name: String }
defined trait User

scala> trait Tweeter {
     |   user: User =>
     |   def tweet(msg: String) = println(s"$name: $msg")
     | }
defined trait Tweeter

scala> trait Wrong extends Tweeter {
     |   def noCanDo = name
     | }
<console>:9: error: illegal inheritance;
 self-type Wrong does not conform to Tweeter's selftype Tweeter with User
       trait Wrong extends Tweeter {
                           ^
<console>:10: error: not found: value name
         def noCanDo = name
                       ^

Если Tweeter был подклассом User, ошибки не было. В приведенном выше коде нам требуется a User, когда используется Tweeter, однако User не был предоставлен Wrong, поэтому мы получили ошибку. Теперь, используя вышеприведенный код, рассмотрим:

scala> trait DummyUser extends User {
     |   override def name: String = "foo"
     | }
defined trait DummyUser

scala> trait Right extends Tweeter with User {
     |   val canDo = name
     | }
defined trait Right 

scala> trait RightAgain extends Tweeter with DummyUser {
     |   val canDo = name
     | }
defined trait RightAgain

С Right выполняется требование смешивания User. Однако второе требование, упомянутое выше, не выполняется: бремя реализации User по-прежнему остается для классов / признаков, которые расширяются Right.

С RightAgain оба требования выполнены. Предусмотрены User и реализация User.

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

248
ответ дан Victor Castillo Torres 27 August 2018 в 01:39
поделиться

Тип self позволяет указать, какие типы разрешены для микширования признака. Например, если у вас есть черта с типом self Closeable, то эта черта знает, что единственные вещи, которым разрешено смешивать ее, должны реализовать интерфейс Closeable.

4
ответ дан Blaisorblade 27 August 2018 в 01:39
поделиться

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

sealed trait Person
trait Student extends Person
trait Teacher extends Person
trait Adult { this : Person => } // orthogonal to its condition

val p : Person = new Student {}
p match {
  case s : Student => println("a student")
  case t : Teacher => println("a teacher")
} // that's it we're exhaustive
11
ответ дан Bruno Bieth 27 August 2018 в 01:39
поделиться

в первом случае, субтракт или подкласс класса B можно смешать с любым использованием A. Таким образом, B может быть абстрактным признаком.

0
ответ дан IttayD 27 August 2018 в 01:39
поделиться

Типы типов позволяют вам определять циклические зависимости. Например, вы можете добиться этого:

trait A { self: B => }
trait B { self: A => }

Наследование с использованием extends не позволяет этого. Попробуйте:

trait A extends B
trait B extends A
error:  illegal cyclic reference involving trait A

В книге Odersky посмотрите раздел 33.5 (Создание главы пользовательского интерфейса электронной таблицы), где он упоминает:

В примере электронной таблицы класс Model наследует от Оценщик и, таким образом, получает доступ к методу оценки. Чтобы перейти в другую сторону, класс Evaluator определяет свой тип self как Model:

package org.stairwaybook.scells
trait Evaluator { this: Model => ...

Надеюсь, что это поможет.

146
ответ дан Jacek Laskowski 27 August 2018 в 01:39
поделиться

TL; DR сводка других ответов:

  • Типы, которые вы продлеваете, подвергаются наследуемым типам, но сами типы типов не являются например: class Cow { this: FourStomachs } позволяет использовать методы, доступные только для жвачных животных, таких как digestGrass. Однако черты, которые расширяют Корову, не будут иметь таких привилегий. С другой стороны, class Cow extends FourStomachs будет подвергать digestGrass любому, кто extends Cow.
  • self-types разрешает циклические зависимости, распространяя другие типы не
8
ответ дан jazmit 27 August 2018 в 01:39
поделиться

Еще одно различие заключается в том, что типы self-types могут указывать типы неклассов. Например,

trait Foo{
   this: { def close:Unit} => 
   ...
}

Тип типа здесь является структурным типом. Эффект состоит в том, чтобы сказать, что все, что смешивается в Foo, должно реализовать возвращаемый модуль метода no-arg «close». Это позволяет использовать безопасные микшины для утиного ввода.

53
ответ дан kostja 27 August 2018 в 01:39
поделиться

Раздел 2.3 «Аннотации самодельного изображения» оригинальной статьи Скалы Мартина Одерского Масштабируемые абстракции компонентов на самом деле очень хорошо объясняют цель selftype за пределами состава смеси: обеспечить альтернативный способ связывания класса с абстрактным типом.

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

abstract class Graph {
  type Node <: BaseNode;
  class BaseNode {
    self: Node =>
    def connectWith(n: Node): Edge =
      new Edge(self, n);
  }
  class Edge(from: Node, to: Node) {
    def source() = from;
    def target() = to;
  }
}

class LabeledGraph extends Graph {
  class Node(label: String) extends BaseNode {
    def getLabel: String = label;
    def self: Node = this;
  }
}
11
ответ дан lcn 27 August 2018 в 01:39
поделиться
trait A { def x = 1 }
trait B extends A { override def x = super.x * 5 }
trait C1 extends B { override def x = 2 }
trait C2 extends A { this: B => override def x = 2}

// 1.
println((new C1 with B).x) // 2
println((new C2 with B).x) // 10

// 2.
trait X {
  type SomeA <: A
  trait Inner1 { this: SomeA => } // compiles ok
  trait Inner2 extends SomeA {} // doesn't compile
}
6
ответ дан Oleg Galako 27 August 2018 в 01:39
поделиться

Начнем с циклической зависимости.

trait A {
  selfA: B =>
  def fa: Int }

trait B {
  selfB: A =>
  def fb: String }

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

trait A1 extends A {
  selfA1: B =>
  override def fb = "B's String" }
trait B1 extends B {
  selfB1: A =>
  override def fa = "A's String" }
val myObj = new A1 with B1

Хотя, если вы переопределите член типа self, вы потеряете доступ к исходному элементу, к которому все еще можно получить доступ через супер, используя наследование. Итак, что действительно наработано с использованием наследования:

trait AB {
  def fa: String
  def fb: String }
trait A1 extends AB
{ override def fa = "A's String" }        
trait B1 extends AB
{ override def fb = "B's String" }    
val myObj = new A1 with B1

Теперь я не могу утверждать, что понимаю все тонкости шаблона пирога, но мне кажется, что основным методом обеспечения модульности является составление а не наследования или типов.

Версия наследования короче, но основная причина, по которой я предпочитаю наследование по типам self, заключается в том, что мне гораздо сложнее получить правильный порядок инициализации с собственными типами. Однако есть некоторые вещи, которые вы можете делать с самими типами, которые вы не можете сделать с наследованием. Типы типов могут использовать тип, тогда как для наследования требуется черта или класс:

trait Outer
{ type T1 }     
trait S1
{ selfS1: Outer#T1 => } //Not possible with inheritance.

Вы даже можете сделать:

trait TypeBuster
{ this: Int with String => }

Хотя вы никогда не сможете создать экземпляр. Я не вижу абсолютной причины для того, чтобы не унаследовать от типа, но я уверен, что было бы полезно иметь классы и черты конструктора путей, поскольку у нас есть типы / классы конструктора типов. Как, к сожалению,

trait InnerA extends Outer#Inner //Doesn't compile

Мы имеем это:

trait Outer
{ trait Inner }
trait OuterA extends Outer
{ trait InnerA extends Inner }
trait OuterB extends Outer
{ trait InnerB extends Inner }
trait OuterFinal extends OuterA with OuterB
{ val myV = new InnerA with InnerB }

Или это:

  trait Outer
  { trait Inner }     
  trait InnerA
  {this: Outer#Inner =>}
  trait InnerB
  {this: Outer#Inner =>}
  trait OuterFinal extends Outer
  { val myVal = new InnerA with InnerB with Inner }

. Одна точка, которая должна быть больше сочувствующей, заключается в том, что черты могут расширяет классы. Спасибо Дэвиду Маклверу за это. Вот пример из моего собственного кода:

class ScnBase extends Frame
abstract class ScnVista[GT <: GeomBase[_ <: TypesD]](geomRI: GT) extends ScnBase with DescripHolder[GT] )
{ val geomR = geomRI }    
trait EditScn[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]
trait ScnVistaCyl[GT <: GeomBase[_ <: ScenTypes]] extends ScnVista[GT]

ScnBase наследуется от класса кадра Swing , поэтому его можно использовать как тип self, а затем смешать в end (при создании экземпляра). Однако val geomR необходимо инициализировать, прежде чем использовать его, наследуя черты. Поэтому нам нужен класс для принудительной инициализации geomR. Класс ScnVista может затем наследоваться из-за множества ортогональных признаков, которые сами могут быть унаследованы. Использование нескольких параметров типа (generics) предлагает альтернативную форму модульности.

9
ответ дан Peter Mortensen 27 August 2018 в 01:39
поделиться

Обновление. Основное отличие состоит в том, что самонастройки могут зависеть от multiple классов (я допускаю, что это бит-код). Например, вы можете иметь

class Person {
  //...
  def name: String = "...";
}

class Expense {
  def cost: Int = 123;
}

trait Employee {
  this: Person with Expense =>
  // ...

  def roomNo: Int;

  def officeLabel: String = name + "/" + roomNo;
}

. Это позволяет добавить микс Employee только к любому подклассу Person и Expense. Конечно, это имеет смысл только в том случае, если Expense распространяется Person или наоборот. Дело в том, что использование self-types Employee может быть независимым от иерархии классов, от которых он зависит. Он не заботится о том, что расширяет то, что - если вы переключите иерархию Expense vs Person, вам не нужно изменять Employee.

1
ответ дан Petr Pudlák 27 August 2018 в 01:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: