2. в теории
система типов - статическая аппроксимация
программы во время выполнения
Бенжамин Пирс, "TAPL"
что дают типы?
производительность
безопасность
документорованность
оптимизации компилятора
лучшие абстракции
одни достоинства
3. в мейнстриме
public Map<Desk<Poker>, List<Player<Poker>>
getPlayersPerDesks(){
Map<Desk<Poker>, List<Player<Poker>> players=
new HashMap<Desk<Poker>, List<Player<Poker>>();
...
return players;
}
boilerplate
5. 1, true, "abc" - термы / конкретные
значения / абстракции нулевого порядка
x => x + 1 - функция / абстракции
первого порядка (полиморфны, т.к.
конструируют множественные конкретные
значения)
* Int, Boolean, String
конкретные типы
* -> * List[T], Map[K, V]
функции на типах / полиморфные
типы / конструкторы типов
6. (* -> *) -> *
trait Functor[F[_]] {
def map[A, B](r: F[A], f: A => B): F[B]
}
map - функция высшего порядка; в качестве
переметра приниимает функции
Functor - тип высшего порядка; в качестве
параметров принимает конструкторы типов
10. вариантность типов и System F<:
S подтип T, конструктор типов C<U>
=> C<S> подтип/супертип/? C<T>
11. вариантность типов в Java
cписки ковариантны: S[] подтип T[]
oстальные типы - инварианты
workaround: имитация вариантности в месте
вызова с помощью органиченных
экзистенциальных типов
List<? extends List<? extends Number>> l =
new ArrayList<List<Number>>();
12. вариантность типов в Scala
C[+U] - ковариантность, S подтип T
=> C[S] подтип C[T]
C[-U] - контрвариантность, S подтип T
=> C[T] супертип C[S]
С[U] - инвариантность (как в Java), S подтип T
=> C[T] ? C[S]
13. trait Suit
trait Spades extends Suit
class Card[T <: Suit](rank: String)
class Hand[A <: Suit](cards: List[Card[A]] = Nil) {
def add[B >: A <: Suit](card: Card[B]): Hand[B] =
new Hand[B](card :: cards)
}
val hand1: Hand[Spades] = new Hand
.add(new Card[Spades]("A")).add(new Card[Spades]("Q"))
val hand2: Hand[Suit] = new Hand
.add(new Card[Spades]("A")).add(new Card[Hearts]("Q"))
14. trait Suit
trait Spades extends Suit
class Card[+T <: Suit](rank: String)
class Hand[A <: Suit](cards: List[Card[A]] = Nil) {
def add[B >: A <: Suit](card: Card[B]): Hand[B] =
new Hand[B](card :: cards)
}
val hand1: Hand[Spades] = new Hand
.add(new Card[Spades]("A")).add(new Card[Spades]("Q"))
val hand2: Hand[Suit] = new Hand
.add(new Card[Spades]("A")).add(new Card[Hearts]("Q"))
15. class HandEvaluator[T <: Suit]
(val evaluate: List[Card[T]] => Int)
val flushEval = new HandEvaluator(
(_: List[Card[Spades]]) => 1000)
val genEval = new HandEvaluator(
(_: List[Card[Suit]]) => 100)
val hand1: Hand[Spades] = ...
> println(hand1.evaluate(flushEval))
1000
> println(hand1.evaluate(genEval))
Suit >:Spades, but class HandEvaluator is invariant in
type T
16. class HandEvaluator[-T <: Suit]
(val evaluate: List[Card[T]] => Int)
val flushEval = new HandEvaluator(
(_: List[Card[Spades]]) => 1000)
val genEval = new HandEvaluator(
(_: List[Card[Suit]]) => 100)
val hand1: Hand[Spades] = ...
> println(hand1.evaluate(flushEval))
1000
> println(hand1.evaluate(genEval))
100
17. неявная конверсия типов
1. class A { def foo(): Unit }
2. class B
...магия...
3. val b = new B()
b.foo() // b был неявно конвертирован в A
[[A -> B]]
18. неявная конверсия типов
trait Card
class Hand(cards: List[Card]) { def weight = /*..*/ }
object Hand {
implicit def toHand(cards: List[Card]) = new Hand(cards)
}
import Hand._
List[Card]().weight
неявный
контекст
тип List[Card] "видимый как" Hand
19. ограничения на "вид"
class HandComparator {
def compare[T <% Hand](hand1: T, hand2: T) =
hand1.weight compareTo hand2.weight
}
new HandComparator().compare(new Hand(Nil), new Hand(Nil))
new HandComparator().compare(List[Card](), List[Card]())
20. ограничения на контекст
class Hand[A <: Suit](cards: List[Card[A]] = Nil) {
def add[B >: A <: Suit](card: Card[B]): Hand[B] =
new Hand[B](card :: cards)
def evaluate(evaluator: HandEvaluator[A]) =
evaluator.evaluate(cards)
}
val flushEval = new HandEvaluator(
(_: List[Card[Spades]]) => 1000)
val genEval = new HandEvaluator(
(_: List[Card[Suit]]) => 100)
21. ограничения на контекст
class Hand[A <: Suit](cards: List[Card[A]] = Nil) {
def add[B >: A <: Suit](card: Card[B]): Hand[B] =
new Hand[B](card :: cards)
def evaluate(evaluator: HandEvaluator[A]) =
evaluator.evaluate(cards)
}
помещаем в
неявный
контекст
implicit val flushEval = new HandEvaluator(
(_: List[Card[Spades]]) => 1000)
implicit val genEval = new HandEvaluator(
(_: List[Card[Suit]]) => 100)
22. ограничения на контекст
class Hand[A <: Suit : HandEvaluator](cards: List[Card[A]] =
Nil) {
def add[B >: A <: Suit](card: Card[B]): Hand[B] =
new Hand[B](card :: cards)
def evaluate = implicitly[HandEvaluator[A]].evaluate(cards)
}
implicit val flushEval = new HandEvaluator(
(_: List[Card[Spades]]) => 1000)
implicit val genEval = new HandEvaluator(
(_: List[Card[Suit]]) => 100)
23. ограничения на контекст
val h1 = Hand().add(Card[Hearts]("A")).add(Card[Hearts]("Q"))
val h2 = Hand().add(Card[Spades]("A")).add(Card[Spades]("Q"))
val h3 = Hand().add(Card[Spades]("A")).add(Card[Hearts]("Q"))
> println(h1.evaluate)
could not find implicit value for evidence parameter of
type by.scala.pokertypes.variance.HandEvaluator[B]
> println(h2.evaluate)
1000
> println(h3.evaluate)
100
24. реификация vs стирание типов
C#, F# (CLR) реализует реификацию типов, но не
поддерживает типы высших порядков
Java (JVM) стирает типы, и не поддерживает
типы высших порядков
Scala имитирует реификацию, и поддерживает
типы высших порядков
25. имитация реификации
def evaluate[T <: Suit : Manifest](hand: Hand[T]) = {
print("Type parameter class: " + manifest[T].erasure)
if (manifest[T] <:< manifest[Spades]) print("; Spades")
else print("; Random")
}
val h2 = Hand().add(Card[Spades]("A")).add(Card[Spades]("Q"))
> println(evaluate(hand1))
Type parameter class: interface Spades; Spades
> println(evaluate(hand2))
Type parameter class: interface Suit; Random
27. зависимые типы
case class Desk(id: Int) {
case class Action(name: String)
def log(action: Action) = /*..*/
}
desk1.log(desk1.Action("Small Blind"))
desk1.log(desk2.Action("Small Blind")) // не компилируется
28. черная магия
типы-фантомы
тип-объеднение и гетерогенные
списки
29. class Dealer[T <: Stage : Manifest](deck: List[Card]) {
def deal(cards: Int, recipient: Actor): Dealer = /*...*/
}
если бы мы могли гарантировать на
уровне типов, что дилер выдаст всем
нужное количество кард...
30. типы-фантомы
sealed trait Stage
trait Preflop extends Stage
trait Flop extends Stage
trait Turn extends Stage
trait River extends Stage
trait Showdown extends Stage
trait StageOrder[Last <: Stage, Current <: Stage]
implicit val gotoFlop = new StageOrder[Preflop, Flop] {}
implicit val gotoTurn = new StageOrder[Flop, Turn] {}
implicit val gotoRiver = new StageOrder[Turn, River] {}
32. trait HandEvaluator {
def compare(hand1: List[Card], hand2: List[Card]): Int
}
если бы мы могли проверять не только
тип списка, но и его содержимое...
33. гетерогенный список
class Card
object Card {
type NoCards = HNil
type HandCards = Card :: Card :: NoCards
type FlopCards = Card :: Card :: Card :: NoCards
type TurnCards = Card :: FlopCards
type RiverCards = Card :: TurnCards
}
34. гетерогенный список и тип-объединение
trait HandEvaluator {
def compare[T: (HandCards ∨ (RiverCards :: HandCards))#λ]
(hand1: T, hand2: T): Int
}
val handCards = new Card :: new Card :: HNil
val communityCards = new Card :: new Card :: new Card :: new
Card :: new Card :: HNil
handEvaluator.compare(handCards, handCards)
handEvaluator.compare(communityCards :: handCards,
communityCards :: handCards)
handEvaluator.compare(handCards, communityCards)