def mapList[A, B](f: A => B): List[A] => List[B] = { (listA: List[A]) => def mapImpl(input: List[A], buffer: List[B]): List[B] = input match { case head :: tail => mapImpl(tail, buffer :+ f(head)) case _ => buffer } mapImpl(listA, Nil) }
def mapOption[A, B](f: A => B): Option[A] => Option[B] = { case Some(a) => Some(f(a)) case _ => None }
def mapFuture[A, B](f: A => B): Future[A] => Future[B] def mapTry[A, B](f: A => B): Try[A] => Try[B] def mapReads[A, B](f: A => B): Reads[A] => Reads[B]
trait Functor[F[_]] { def map[A, B](f: A => B): F[A] => F[B] } map(_.doubleValue)(List(1, 2, 3)) // List(2.0, 4.0, 6.0) map(_.toString)(map(_.doubleValue)(List(1, 2, 3))) // List("2.0", "4.0", "6.0")
trait FunctorInterface[A] { def map[B](f: A => B): FunctorInterface[B] } Functor[List].map(_.doubleValue)(List(1, 2, 3)) ~ List(1, 2, 3).map(_.doubleValue)
def identity[A](x: A):A = x List(1, 2, 3).map(identity) == identity(List(1, 2, 3)) == List(1, 2, 3)
val intToDouble: Int => Double val doubleToString: Double => String List(1, 2, 3).map(intToDouble).map(doubleToString) == List(1, 2, 3).map(intToDouble andThen doubleToString) == List("1.0", "2.0", "3.0")
val listFunctor: Functor[List] = new Functor[List] { def map[A, B](f: A => B): List[A] => List[B] = { (list: List[A]) => if(list.isEmpty) { List.empty[B] } else { val buffer = ListBuffer.empty[B] for(i <- 0 until list.size) { buffer.append(f(list(i))) } buffer.toList } } }
// 1. Identity law List(1, 2, 3).map(identity) // List(1, 2, 3) // 2. Associativity law List(1, 2, 3) .map(intToDouble) // List(1.0, 2.0, 3.0) .map(doubleToString) // List("1.0", "2.0", "3.0") List(1, 2, 3) .map(itToDouble andThen doubleToString) // List("1.0", "2.0", "3.0")
sealed trait Try[+T] final case class Success[+T](value: T) extends Try[T] final case class Failure[+T](exception: Exception) extends Try[T]def divide(a: Int, b: Int): Try[Int] = try Success(a / b) catch { case NonFatal(e) => Failure(e) // Не выбрасываем исключение => нет побочного эффекта }
def printResult(result: Try[Int]) = result match { case Success(n) => println(n) case Failure(e) => printnl(s"An error ${e.message}") } def divide2(a: Int, b1: Int, b2: Int): Try[Try[Int]] = divide(a, b1).map(divide(_, b2)) def printResult(result: Try[Try[Int]]) = result match { case Success(Success(n)) => println(n) case Failure(e) => printnl(s"An error ${e.message}") } def sequentialDivision(a: Int, b1: Int, bs: Int *) = bs match { case head :: tail => divide(a, b1).map(sequentialDivision(_, head, tail)) case _ => divide(a, b1) }
def Bind[F[_]] { def >>=[A, B](f: A => F[B]): F[A] => F[B] def flatMap[A, B](f: A => F[B]): F[A] => F[B] }def sequentialDivision(a: Int, b1: Int, bs: Int *): Try[Int] = bs match { case head :: tail => divide(a, b1).flatMap(sequentialDivision(_, head, tail)) case _ => divide(a, b1) }
trait Monad[M[_]] extends Functor[M] { def unit[A](x: A): M[A] def flatMap[A, B](f: A => M[B]): M[A] => M[B] override def map[A, B](f: A => B): M[A] => M[B] = flatMap(f andThen unit) }
optionX.flatMap(Option(_)) == optionX
Option(x).flatMap(foo) == foo(x)
optionX.flatMap(foo).flatMap(bar) == optionX.flatMap(x => foo(x).flatMap(bar))
val optionMonad: Monad[Option] = new Monad[Option] { def unit[A](x: A) = if(x == null) None else Some(x) def flatMap[A, B](f: A => Option[B]): Option[A] => Option[B] = { case Some(a) => f(a) case _ => None } }
// 1. Right unit law val optionX = Option(12) // Some(12) optionX.flatMap(Option(_)) // Some(12) None.flatMap(Option(_)) // None // 2. Left unit law val intHalf: Int => Option[Int] = x => if(x % 2 == 0) Some(x / 2) else None Option(5).flatMap(intHalf) // None intHalf(5) // None Option(4).flatMap(intHalf) // Some(4) intHalf(4) // Some(4) // 3. Associativity law val intThird: Int => Option[Int] = x => if(x % 3 == 0) Some(x / 3) else None optionX.flatMap(intHalf).flatMap(intThird) // Some(2) optionX.flatMap(x => intHalf(x).flatMap(intThird)) // Some(2)
val foo = (for(i <- 0 to x) yield i * i) // Range(0*0, 1*1, ..., i*i) val bar = for(i <- 0 to x; j <- 0 to y) yield i * j // Range(0*0, 0*1, ..., 0*j, 1*0, 1*1,..., i*j) foo ~ (0 to x).map(i => i * i) bar ~ (0 to x).flatMap(i => (0 to y).map(j => i * j))
fooM .flatMap { foo1 => f1(foo1) .flatMap { foo2 => f2(foo2) .flatMap(foo3 => f3(foo3)) } } |
~ |
for { foo1 <- fooM foo2 <- f1(foo1) foo3 <- f2(foo2) x <- f3(foo3) } yield x |
case class BucketBook(userId: UUID, bookId: UUID) case class Book(id: UUID, authorId: UUID) def getUserBucket(userId: UUID): Future[Seq[BucketBook]] def getBooks(bookIds: Seq[UUID]): Future[Seq[Book]] def getAuthors(authorIds: Seq[UUID]): Future[Seq[Author]]
def bucketAuthors(userId: UUID): Future[Seq[Author]] = getUserBucket(userID) .flatMap { bucketBooks => val bookIds = bucketBooks.map(_.bookId) getBooks(bookIds) .flatMap { books => val authorIds = books.map(_.authorId) getAuthors(authorIds) } }
def bucketAuthors(userId: UUID): Future[Seq[Author]] = for { bucketBooks <- getUserBucket(userID) bookIds = bucketBooks.map(_.bookId) books <- getBooks(bookIds) authorIds = books.map(_.authorId) authors <- getAuthors(authorIds) } yield authors
scala> val ioa: IO[Unit] = IO { println("Hello, world!") } ioa: IO[Unit] scala> val program: IO[String] = | for { | _ <- ioa | _ <- ioa | } yield "Done!" program: IO[String] scala> program.unsafeRunSync() Hello, world! Hello, world! res0: String = "Done!"
Cats is a library which provides abstractions for functional programming in the Scala programming language. The name is a playful shortening of the word category. [Cats - библиотека, предоставляющая абстракции функционального программирования в Scala. Cats - игривое сокращение от слова "КАТегория"] |
|
It provides purely functional data structures to complement those from the Scala standard library [Предоставляет "чисто"-функциональные структуры данных для стандартной библиотеки Scala] |