val a = 42 // независими
val b = 4 // изчисления
val c = a + b // операция
val d = (a + b) * 10 // композиция на операции
val e = f(g(a)) // композиция на функции
Пренесохме възможността за тези операции върху ефекта Future
(и стойността в него)
map
– трансформация на единична стойност (напр. val c = -a
)map2
, или още zipMap
или zipWith
– трансформация на две независими стойности (val c = a + b
). Резултатът c
зависи от тяхmap3
, zipMap3
…; mapN
дефинира зависимостиflatMap
– когато функциите в изразите са ефектни, напр. ако f
и g
връщат Future
flatMap
– ефектна трансформация на единична стойностval futureA = calc(42)
val futureB = calc(10)
val sum = for {
a <- futureA
b <- futureB
} yield a + b
println {
Await.result(sum, 5.seconds)
}
> 52
> Exception in thread "main" java.util.concurrent.TimeoutException: Futures timed out after [5 seconds]
val sum = for {
(a, b) <- calc(42).zip(calc(10))
} yield a + b
println {
Await.result(sum, 5.seconds)
}
> 52
Тук вече няма значение дали Future-а е eager или lazy
Примери: групи, полета, полиноми, векторни пространства и много други
Алгебрични структури – множества със съответни операции и аксиоми (свойства)
алгебрични структури ~ тип данни
Нека 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
работещ с различни типове
в математиката: „Нека фиксираме поле F, такова че…“
в математиката: „Нека фиксираме ортогонална координатна система“
Текуща:
Експлицитно предаване на контекст
Имплицитно предаване на контекст
В Scala чрез implicts
Type class-овете дефинират операции и аксиоми/свойства, които даден тип трябва да притежава.
За да бъде един тип от даден клас, то трябва да предоставим валидна имплементация на операциите на type class-а
((a · b) · c) · d – едно по едно, от ляво надясно
(a · b) · (c · d) – балансирано и паралелизуемо
Могат да бъдат проверявани чрез тестове
fold
vs foldLeft
fold
изисква асоциативна операция
Класовете в ООП моделират обекти
Type class-овете моделират типове
Използването на един и същи интерфейс с различни типове
Избор на конкретна имплементация според конкретния тип
Пример: реализацията на Monoid
се избира конкретно според типа
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
Липсва информация за конкретния тип, но се изпълнява конкретна имплементация
Late binding-а е фундаментален за ООП
“I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages… to communicate with messages… OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.” ― Alan Kay
разширяване на тип без промяна на кода му
добавяне на интерфейс към тип
без промяна на кода му
Type class-овете поддържат ретроактивен полиморфизъм
Типовата система е логическа и търсенето на implicit стойности, отговарящи на определен тип, съвпада с механиката на изводите, познати ни от логическото програмиране
ClassTag
– информация за класа на подадения типTypeTag
– пълна типова информация, включително за generic параметритеВ Haskell всеки type class може да има само една инстанция за определен тип.
В Scala липсва такова ограничение, което е едновременно и плюс и минус.
Eq
)Type class-ове на повече типове
Дефинират type class релация между няколко типа
Type class-овете: