2. Self type
Можно объявлять зависимости трейта на
другие трейты. Это позволяет реализовать
DI на уровне языка.
trait A
trait B {
this: A =>
}
3. Difference with extends
Почему не подходит extends?
В этом случае если в А все методы
реализованы, мы можем просто забыть
добавить нужный mixin.
trait A {
def foo = "It's foo 1"
}
trait B extends A
trait C extends A {
override def foo = "It's foo 2"
}
new B //no compiler error
4. Difference with extends
То есть B не ведёт себя как отдельная
компонента, что противоречит идеологии
Dependency Injection.
5. Cake pattern
trait AComp {
trait CComp {
val a: A
this: AComp with BComp =>
class A {
def c() =
def a() = "It's A"
"It's C with " + b.b() +
}
" and " + c.c()
}
}
trait BComp {
new CComp {
val b: B
val a = new A
class B {
val b = new B
def b() = "It's B"
}
}
}
6. Example
Давайте напишем чайник. У него есть
интерфейс с кнопкой. Сервис нагревания.
Сервис вычисления количества воды. И
сервис проверки, что воды не меньше min и
не больше max. А также две реализации
чайника с разным min и max.
Во втором также есть сервис темературы
воды, чайник не включается, если
температура выше 98 градусов.
7. Path-dependent types
Напишем математически понятную
имплементацию графа:
case class Node(id: Int)
case class Edge(left: Node, right: Node)
class Graph(nodes: ArrayBuffer[Node],
edges: ArrayBuffer[Edge]) {
def connect(left: Node, right: Node) {
edges += Edge(left, right)
}
}
8. Path-dependent types
Но лучше было бы запретить на уровне
компилятора невозможные действия:
class Graph {
case class Node(id: Int)
case class Edge(left: Node, right: Node)
val nodes: ArrayBuffer[Node] = ???
val edges: ArrayBuffer[Edge] = ???
def connect(left: Node, right: Node) {
edges += Edge(left, right)
}
}
9. Abstract types
Другой пример:
abstract class Key(id: String) {
type Value
}
class DataStorage {
val data = collection.mutable.Map[Key, Any]
def get(key: Key): Option[key.Value] = ???
def set(key: Key)(value: key.Value) = ???
}
object Keys {
trait StringKey {
this: Key =>
type Value = String
}
val nameKey = new Key("name") with StringKey
}
10. Graph example
Давайте напишем граф, который может
быть расширен несколькими вариантами:
● Обычный граф
● Цветной граф
● Ориентированный граф
11. Heterogeneous list
А теперь напишем HList, который состоит из
HNil, HCons, а также есть метод concat,
который умеет объединять два списка.