Upload
john-kozlov
View
198
Download
6
Embed Size (px)
Citation preview
Введение в 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+a2def zero = 0
}
object stringInstanceextends Addable[String]with Zero[String] {
def add(s1: String, s2: String) = s1+s2def zero = ""
}
scala> intInstance.add(3, 5)res0: Int = 8
scala> stringInstance.zerores1: 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 === 3res0: 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): Orderingdef equal(a1: A, a2: A) =order(a1, a2) == EQ
}
sealed trait Orderingcase object LT extends Orderingcase object EQ extends Orderingcase 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): Adef pred(a: A): Adef 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 |-> 5res0: 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 |+| 2res0: 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)).joinres0: Option[Int] = Some(5)
scala> List(List(1,2), List(3,4,5)).joinres1: List[Int] = List(1,2,3,4,5)
scalaz.Monad[F[_]]
trait Monad[F[_]]extends Applicative[F]with Bind[F] {
…}