scala #5
TRANSCRIPT
![Page 1: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/1.jpg)
Scala #5Cake pattern, path-dependent types, HList
![Page 2: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/2.jpg)
Self type
Можно объявлять зависимости трейта на другие трейты. Это позволяет реализовать DI на уровне языка.trait A
trait B {
this: A =>
}
![Page 3: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/3.jpg)
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
![Page 4: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/4.jpg)
Difference with extends
То есть B не ведёт себя как отдельная компонента, что противоречит идеологии Dependency Injection.
![Page 5: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/5.jpg)
Cake patterntrait AComp {
val a: A
class A {
def a() = "It's A"
}
}
trait BComp {
val b: B
class B {
def b() = "It's B"
}
}
trait CComp {
this: AComp with BComp =>
def c() =
"It's C with " + b.b() +
" and " + c.c()
}
new CComp {
val a = new A
val b = new B
}
![Page 6: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/6.jpg)
Example
Давайте напишем чайник. У него есть интерфейс с кнопкой. Сервис нагревания. Сервис вычисления количества воды. И сервис проверки, что воды не меньше min и не больше max. А также две реализации чайника с разным min и max. Во втором также есть сервис темературы воды, чайник не включается, если температура выше 98 градусов.
![Page 7: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/7.jpg)
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)
}
}
![Page 8: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/8.jpg)
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)
}
}
![Page 9: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/9.jpg)
Другой пример: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
}
Abstract types
![Page 10: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/10.jpg)
Graph example
Давайте напишем граф, который может быть расширен несколькими вариантами:● Обычный граф● Цветной граф● Ориентированный граф
![Page 11: Scala #5](https://reader036.vdocuments.pub/reader036/viewer/2022080903/55a344521a28ab94038b45d6/html5/thumbnails/11.jpg)
Heterogeneous list
А теперь напишем HList, который состоит из HNil, HCons, а также есть метод concat, который умеет объединять два списка.