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] def mapF[A, B](f: A => B): F[A] => F[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) { Nil } else { val result = ListBuffer.empty[B] for(i <- 0 to list.size - 1) { result.append(f(list(i))) } result.toList } } }
// 1. Identity low List(1, 2, 3).map(identity) // List(1, 2, 3) // 2. Associativity low 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 divide2(a: Int, b1: Int, b2: Int): Try[Try[Int]] = divide(a, b1).map(divide(_, b2)) def sequentialDivision(a: Int, b1: Int, bs: Int *) = bs match { case head :: tail => divide(a, b1).map(sequentialDivision(_, head, tail)) case _ => divide(a, b1) }
trait 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 low val optionX = Option(12) // Some(12) optionX.flatMap(Option(_)) // Some(12) None.flatMap(Option(_)) // None // 2. Left unit low 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 low 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))
withFilter
, map
, и flatMap
(с соответствующими типами).
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!"
Монада и функторы - чистые значения, внутри которых может лежать что угодно (эффекты, мутабельные данные)
При ленивых и ассинхронных вичислениях можно указывать строгий порядок без callback'ов