SlideShare a Scribd company logo
Введение в
Scalaz
http://lambdansk.org
https://twitter.com/ZhekaKozlov
Scalaz – библиотека, которая
приносит концепции Haskell в
Scala
Проекты, использующие Scalaz:
• https://github.com/argonaut-io/argonaut/
• https://github.com/tpolecat/doobie
• https://github.com/oncue/remotely
• https://github.com/scalaz/scalaz-stream
• https://github.com/xuwei-k/httpz
• https://github.com/http4s/http4s
Функциональные структуры данных,
отсутствующие в Scala stdlib:
scalaz.NonEmptyList[A] Непустой список
scalaz.EphemeralStream[A] Правильный Stream
(аналог списков в Haskell)
scalaz.FingerTree[V, A] http://staff.city.ac.uk/~ross
/papers/FingerTree.html
scalaz.DList[A] Чистофункциональный
аналог ListBuffer
FingerTree[Int, A] Vector[A] TreeMap[Int, A] List[A]
head O(1) O(log32 n) O(log n) O(1)
tail O(1) O(log32 n) O(log n) O(1)
last O(1) O(log32 n) O(log n) O(n)
init O(1) O(log32 n) O(log n) O(n)
get(index) O(log n) O(log32 n) O(log n) O(n)
Функциональные структуры данных,
отсутствующие в Scala stdlib:
scalaz./[A, B] Правильный Either
scalaz.&/[A, B] Как Either, но может быть
одновременно и A, и B
scalaz.Either3[A, B, C] Как Either, но может быть
A, B или C
В ООП доминирует подтиповой
полиморфизм:
class Int {
def <(other: Int): Boolean = …
}
class Double {
def <(other: Double): Boolean = …
}
class String {
def <(other: String): Boolean = …
}
В ООП доминирует подтиповой
полиморфизм:
class Int {
def <(other: Int): Boolean = …
}
class Double {
def <(other: Double): Boolean = …
}
class String {
def <(other: String): Boolean = …
}
Общее поведение выделяется в
класс:
trait Ordered[A] {
def <(other: A): Boolean
…
}
class Int extends Ordered[Int] { … }
class Double extends Ordered[Double] { … }
class String extends Ordered[String] { … }
Проблема 1: не можем заранее
предугадать, какое поведение нам
понадобится:
class Int extends Ordered[Int]
with Addable[Int]
with Random[Int]
with ToJSON[Int]
with ToXML[Int]
with ToBinary[Int]
with …
Проблема 2: не всё общее
поведение можно выразить через
наследование
trait Zero[A] {
def zero: A
}
class Int extends Zero[Int] {
def zero = 0
}
Чтобы иметь возможность вызвать zero,
нужен экземпляр класса Int
???
Решение: классы типов
trait Addable[A] {
def add(a1: A, a2: A): A
}
trait Zero[A] {
def zero: A
}
Решение: классы типов
object intInstance
extends Addable[Int]
with Zero[Int] {
def add(a1: Int, a2: Int) = a1+a2
def zero = 0
}
object stringInstance
extends Addable[String]
with Zero[String] {
def add(s1: String, s2: String) = s1+s2
def zero = ""
}
scala> intInstance.add(3, 5)
res0: Int = 8
scala> stringInstance.zero
res1: String = ""
scalaz.Equal[A]
trait Equal[A] {
def equal(a1: A, a2: A): Boolean
}
scala> intInstance.equal(3, 3)
res0: Boolean = true
scala> stringInstance.equal("s", "s2")
res1: Boolean = false
Добавим синтаксического сахару:
implicit class EqualOps[A](a: A)
(implicit val e: Equal[A]) {
def ===(other: A) = e.equal(a, other)
def /==(other: A) = !e.equal(a, other)
}
scala> 3 === 3
res0: Boolean = true
scala> "s" /== "s2"
res1: Boolean = true
scala> 3 === "s"
<console>:14: error: type mismatch;
found : String("s")
required: Int
3 === "s"
^
scalaz.Order[A]
trait Order[A] extends Equal[A] {
def order(a1: A, a2: A): Ordering
def equal(a1: A, a2: A) =
order(a1, a2) == EQ
}
sealed trait Ordering
case object LT extends Ordering
case object EQ extends Ordering
case object GT extends Ordering
Принцип использования Scalaz:
Когда объявляем класс
class Rational(nom: Int, denom: Int) { … }
… в объекте-компаньоне создаём инстанс,
реализующий все необходимые классы типов:
object Rational {
implicit object instance
extends Order[Rational]
with Numeric[Rational]
with … {
def order(r1: Rational, r2: Rational) = …
…
}
}
scalaz.Enum[A]
trait Enum[A] extends Order[A] {
def succ(a: A): A
def pred(a: A): A
def fromToL(f: A, t: A): List[A] = …
}
implicit class EnumOps[A](a: A)
(implicit val e: Enum[A]) {
def |-> (to: A) = e.fromToL(a, to)
}
scala> 1 |-> 5
res0: List[Int] = List(1, 2, 3, 4, 5)
scala> 'a' |-> 'd'
res1: List[Char] = List(a, b, c, d)
scalaz.Semigroup[A]
trait Semigroup [A] {
def append(a1: A, a2: => A): A
}
implicit class SemigroupOps[A](a: A)
(implicit val s: Semigroup[A]) {
def |+| (o: => A) = s.append(a, o)
}
scala> 3 |+| 2
res0: Int = 5
scala> List(1,2,3) |+| List(4,5)
res1: List[Int] = List(1, 2, 3, 4, 5)
scala> "abc" |+| "de"
res2: String = abcde
scala> List(1,2,3) ++ "ab"
res0: List[AnyVal] = List(1, 2, 3, a, b)
scala> List(1,2,3) |+| "ab"
<console>:26: error: type mismatch;
found : String("ab")
required: List[Int]
List(1,2,3) |+| "ab"
^
scalaz.Monoid[A]
trait Monoid [A] extends Semigroup [A] {
def zero: A
}
def sum[A](list: List[A])
(implicit m: Monoid[A]) =
list.foldLeft(m.zero)(_ |+| _)
scala> sum(List(1,2,3))
res0: Int = 6
scala> sum(List("ab","cd","ef"))
res1: String = abcdef
scala> sum(List(List(1,2),List(3,4))
res2: List[Int] = List(1,2,3,4)
class List[A] {
def map[B](f: A => B): List[B]
}
class Vector[A] {
def map[B](f: A => B): Vector[B]
}
class Option[A] {
def map[B](f: A => B): Option[B]
}
class List[A] {
def map[B](f: A => B): List[B]
}
class Vector[A] {
def map[B](f: A => B): Vector[B]
}
class Option[A] {
def map[B](f: A => B): Option[B]
}
scalaz.Functor[F[_]]
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
Конструктор типа
trait Functor[F[_]] {
def map[A,B](fa: F[A])(f: A => B): F[B]
}
implicit object listInstance =
new Functor[List] {
def map[A,B](fa: List[A])(f: A => B) =
fa.map(f)
}
val opt: Option[Int] = for {
i ← Some(2)
j ← Some(3)
} yield (i + j)
val list: List[(Int, Int)] = for {
i ← List(1,2,3)
j ← List(5,6)
} yield (i, j)
val opt: Option[Int] = for {
i ← Some(2)
j ← Some(3)
} yield (i + j)
val list: List[(Int, Int)] = for {
i ← List(1,2,3)
j ← List(5,6)
} yield (i, j)
trait Apply[F[_]] extends Functor[F] {
def apply2[A,B,C](fa: => F[A], fb: => F[B])
(f: (A,B) => C): F[C]
}
scalaz.Apply[F[_]]
scala> Apply[Option].apply2(x, y)(_ + _)
res0: Option[Int] = Some(5)
scala> Apply[List].apply2(List(1,2,3),
List(5,6))((_, _))
res1: List[(Int,Int)] = List((1,5),(1,6),
(2,5),(2,6),(3,5),(3,6))
val opt: Option[Int] = Some(5)
val list: List[String] = List("a")
val fut: Future[Double] = Future(3.5)
val opt: Option[Int] = Some(5)
val list: List[String] = List("a")
val fut: Future[Double] = Future(3.5)
trait Pure[F[_]] {
def point[A](a: => A): F[A]
}
scalaz.Pure[F[_]]
Был удалён в Scalaz 7
trait Applicative[F[_]] extends Apply[F] {
def point[A](a: => A): F[A]
}
scalaz.Applicative[F[_]]
class List[A] {
def flatMap[B](f: A => List[B]): List[B]
}
class Vector[A] {
def flatMap[B](f: A => Vector[B]): Vector[B]
}
class Option[A] {
def flatMap[B](f: A => Option[B]): Option[B]
}
scalaz.Bind[F[_]]
trait Bind[F[_]] extends Apply[F] {
def bind[A,B](fa: F[A])
(f: A => F[B]): F[B]
}
scala> some(some(5)).join
res0: Option[Int] = Some(5)
scala> List(List(1,2), List(3,4,5)).join
res1: List[Int] = List(1,2,3,4,5)
scalaz.Monad[F[_]]
trait Monad[F[_]]
extends Applicative[F]
with Bind[F] {
…
}
Введение в Scalaz

More Related Content

Введение в Scalaz