Type Classes

Докъде сме?

Абстрактност

Абстрактността в математиката

Примери: групи, полета, полиноми, векторни пространства и много други

Алгебрични структури – множества със съответни операции и аксиоми (свойства)

алгебрични структури ~ тип данни

Група

Нека G е множество с бинарна операция „·“

G наричаме група, ако:

  • асоциативност – ∀ a, b, c ∈ G:

    (a · b) · c = a · (b · c)
  • неутрален елемент – ∃ e ∈ G, такъв че ∀ a ∈ G

    e · a = a · e = a
  • обратен елемент – ∀ a ∈ G, ∃ a’ ∈ G, такъв че

    a · a' = a' · a = e

Моноид

Нека M е множество с бинарна операция „·“

M наричаме моноид, ако:

  • асоциативност – ∀ a, b, c ∈ M:

    (a · b) · c = a · (b · c)
  • неутрален елемент – ∃ e ∈ M, такъв че ∀ a ∈ M

    e · a = a · e = a

Реализация?

Задача: напишете метод sum работещ със списъци от различни типове

sum(List(1, 3, 4))
sum(List("a", "b", "c"))
sum(List(Rational(1, 2), Rational(3, 4)))

Контекст в програмния код

в математиката: „Нека фиксираме поле F, такова че…“

в математиката: „Нека фиксираме ортогонална координатна система“

context
  1. The parts of a written or spoken statement that precede or follow a specific word or passage, usually influencing its meaning or effect;
  1. The set of circumstances or facts that surround a particular event, statement, idea, etc.
  1. “What comes with the text, but is not in the text.”

Примери

Текуща:

  • конфигурация
  • транзакция
  • сесия
  • ExecutionContext – pool от нишки

Контекст в програмния код

  • import
  • подтипов полиморфизъм
  • dependency injection
  • външен scope
  • параметри

Експлицитно предаване на контекст

Имплицитно предаване на контекст

В математиката: „Дадено е поле F, такова че…“

В Scala 3:

given f: Field[Double] = ???

Context bound

def sum[A : Monoid](xs: List[A])

Логическо програмиране
в типовата система

  • Типовата система е напълно логическа
  • Търсенето на given стойности от определен тип съвпада с механиката на логическите изводи, позната ни от логическото програмиране
  • При изводите given без параметри съответства на логически факти, а given с параметри на логически правила

Type class-овете дефинират операции и аксиоми/свойства, които даден тип трябва да притежава.

За да бъде един тип от даден клас, то трябва да предоставим валидна имплементация на операциите на type class-а

Аксиомите са важни

((a · b) · c) · d – едно по едно, от ляво надясно

(a · b) · (c · d) – балансирано и паралелизуемо

Могат да бъдат проверявани чрез тестове

fold vs foldLeft

(1 to 100000000).par.fold(0)(_ + _)

fold изисква асоциативна операция

Контекст в Scala 2

ООП класове срещу type class-ове

Класовете в ООП моделират обекти

Type class-овете моделират типове

Полиморфизъм

Използването на един и същи интерфейс с различни типове

Полиморфизъм в Scala

Параметричен полиморфизъм (generics)

def mapTwice[A](xs: List[A])(f: A => A): List[A] =
  xs.map(f compose f)

mapTwice(List(1, 2, 3))(_ * 2) // List(4, 8, 12)
mapTwice(List("ab", "c", "def"))(str => str + str) // List(abababab, cccc, defdefdefdef)

Ad hoc полиморфизъм

Избор на конкретна имплементация според конкретния тип

Ad hoc полиморфизъм – overloading

def stringify(n: Int) = n.toString
def stringify(n: Rational) = s"$n.numer/$n.denom"

stringify(1) // "1"
stringify(Rational(1)) // "1/1"

Ad hoc полиморфизъм – type classes

Пример: реализацията на Monoid се избира конкретно според типа

sum(List(Rational(2), Rational(4))) // rationalMonoid
sum(List(2, 4)) // intMonoid

Подтипов полиморфизъм

trait Figure:
  def area: Double
  def circumference: Double

case class Circle(radius: Double) extends Figure:
  def area: Double = Pi * radius * radius
  def circumference: Double = 2 * Pi * radius

case class Square(side: Double) extends Figure:
  def area: Double = side * side
  def circumference: Double = 4 * side

val figure = getRandomFigure(10)
figure.area // 100

Липсва информация за конкретния тип, но се изпълнява конкретна имплементация

Duck typing и структурно подтипизиране

type Closable = { def close(): Unit }

def handle[A <: Closable, B](resource: A)(f: A => B): B =
  try f(resource) finally resource.close()

handle(new FileReader("file.txt"))(f => readLines(f))

Binding

  • Static (compile time) – параметричен и ad-hoc полиморфизъм
  • Late (runtime) – подтипов полиморфизъм и duck typing/структурно типизиране

Late binding-а е фундаментален за ООП

Ретроактивност

разширяване на тип без промяна на кода му

Ретроактивен полиморфизъм

добавяне на интерфейс към тип
без промяна на кода му

Type class-овете поддържат ретроактивен полиморфизъм

Numeric

Ordering

Сериализация до JSON

По-късно в курса ще разгледаме библиотеката Circe

Езици, поддържащи type class-ове

  • Haskell
  • Scala
  • Rust
  • Idris

В Haskell всеки type class може да има
само една инстанция за определен тип.

В Scala липсва такова ограничение, което е едновременно и плюс и минус.

Библиотеки за type class-ове?

Библиотеки

  • Общи
  • В конкретен домейн
    • Spire – математически абстракции, използва Cats
    • Cats Effects – абстракции за асинхронност

Категории

Cats

Различни видове котк… категории 😸

Vivian – Scala Magician

Моноид в Cats

Multiversal equality в Cats (Eq)

Scala with Cats

Scala with Cats

В заключение

Type class-овете:

  • моделират типове
  • предоставят общ интерфейс и аксиоми за цяло множество от типове
  • или още – общ език, чрез който да мислим и боравим с тези типове
  • позволяват ad hoc полиморфизъм
  • наблягат на композитността и декларативността
  • добавят се ретроактивно към типовете и в Scala могат да бъдат контекстно-зависими

Въпроси :)?

ч