Введение в Scalaz

  • View
    198

  • Download
    6

  • Category

    Science

Preview:

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] {

…}