Scala
I
O

Scala
I
O
Cats Effect
ZIO

Cats Effect vs ZIO

slido.com with #077428
Haskell
Haskell
countZeros :: [Int] -> Int
countZeros numbers = 
      var count = 0
      do 
      	num <- numbers
        if num == 0 then
          count = count + 1
        return countHaskell
countZeros :: [Int] -> Int
countZeros numbers = 
    let go accum (0 : rest) = go (accum + 1) rest
    	go accum (_ : rest) = go accum rest
        go accum []         = accum
    in go 0 numbers
 countZeros :: [Int] -> Int   
 countZeros = length . filter ( == 0)Haskell
main :: ?
main = ?main :: [String] -> [String]
main (firstLine : nextLine : ...) = let 
	in firstOutLine : nextOutLine : ...def main(input: LazyList[String]): LazyList[String] = ???
enum LazyList[+A]:
   case Empty
   case Cons(head: () => A, tail: () => LazyList[A])type Dialogue = [Request] -> [Response]
main :: DialogueHaskell



Monads



await
async
Monads
(>>=) :: IO a -> (a -> IO b) -> IO bMonads



IO
type IO a = World -> IORes a
data IORes a = MkIORes a WorldcountZeros :: [Int] -> IO Int
countZeros nums = do
  count <- newIORef 0
  forM_ nums \num ->
    when (num == 0) $
      modifyIORef count (+ 1)
  readIORef countConcurrency

forkIO :: IO () -> IO ThreadId
throwTo :: Exception e => ThreadId -> e -> IO ()
atomicModifyIORef :: IORef a -> (a -> (a, b)) -> IO b
atomicWriteIORef :: IORef a -> a -> IO ()
takeMVar :: MVar a -> IO a
putMVar :: MVar a -> a -> IO ()
readMVar :: MVar a -> IO a
writeChan :: Chan a -> a -> IO ()
readChan :: Chan a -> IO aSTM

newTVar :: a -> STM (TVar a)
readTVar :: TVar a -> STM a
writeTVar :: TVar a -> a -> STM ()
atomically :: STM a -> IO a
retry :: STM a
Async
async :: IO a -> IO (Async a)
wait :: Async a -> IO aResource
bracket
        :: IO a         -- ^ computation to run first (\"acquire resource\")
        -> (a -> IO b)  -- ^ computation to run last (\"release resource\")
        -> (a -> IO c)  -- ^ computation to run in-between
        -> IO c         -- returns the value from the in-between computationallocate :: IO a -> IO () -> ResIO (ReleaseKey, a)
runResourceT :: ResIO a -> IO aStreams
- Conduit
- Streaming
- Streamly
- ...
Scala
Akka
package akka.actor
trait Actor {
    abstract def receive: PartialFunction[Any, Unit]2010
Future
package scala.concurrent
trait Future[+T]{
    def onComplete[U](f: Try[T] => U)(implicit ec: ExecutionContext): Unit
    
    def map[S](f: T => S)(implicit ec: ExecutionContext): Future[S] = ... 
    
    def flatMap[S](f: T => Future[S])(implicit ec: ExecutionContext): Future[S] = def businessFeature(arg: Arg, input: Input): Future[Result] = for {
       info   <- getInfo(arg)
       _      <- doSomething(input)
       result <- getResult(info, input)
    } yield result2011
Monifu
2013
trait Scheduler extends ExecutionContext with Executor {
  def execute(command: Runnable): Unit
  def scheduleOnce(initialDelay: Long, unit: TimeUnit, r: Runnable): Cancelable
  def scheduleWithFixedDelay(
  	initialDelay: Long, delay: Long, unit: TimeUnit, r: Runnable): Cancelable
}
trait Observer[-A] extends Any with Serializable {
  def onNext(elem: A): Future[Ack]
  def onError(ex: Throwable): Unit
  def onComplete(): Unit
}
abstract class Observable[+A]{
  def subscribe(observer: Observer[A])(implicit s: Scheduler): Cancelable
}Monix
2016
abstract class Callback[-A] {
  def onSuccess(value: A): Unit
  def onError(ex: Throwable): Unit
}
sealed abstract class Task[+A]{
  def runAsync(cb: Callback[A])(implicit s: Scheduler): Cancelable
}
abstract class MVar[A] {
  def put(a: A): Task[Unit]
  def take: Task[A]
  def read: Task[A]
}
scalaz
package scalaz
package effect
sealed abstract class IO[A] {
  private[effect] 
  	def apply(rw: World[RealWorld]): Trampoline[(World[RealWorld], A)]2010
scalaz
package scalaz
package effect
sealed abstract class IO[A] {
  private[effect] 
  	def apply(rw: Tower[IvoryTower]): Trampoline[(Tower[IvoryTower], A)]2010
scalaz-stream
2013
trait Process[+F[_], +O] 
object Process {
  case class Await[F[_], A, +O] private[stream](
    req: F[A], recv: A => Process[F,O],
    fallback: Process[F,O],
    cleanup: Process[F,O]) extends Process[F, O]
  case class Emit[F[_], O] private[stream](
    head: Seq[O], 
    tail: Process[F,O]) extends Process[F,O]
  case object Halt extends Process[Nothing, Nothing]scalaz-stream
2015
Functional Streams For Scala
scalaz
cats
scalaz-stream
2015
F S F S
scalaz
cats
scalaz-stream
2015
(F S) ²
scalaz
cats
scalaz-stream
2015
FS2
scalaz
cats
FS2
final class Task[+A](private[fs2] val get: Future[Attempt[A]])
trait Semaphore[F[_]] {    
  def decrementBy(n: Long): F[Unit]
  def incrementBy(n: Long): F[Unit]
}
trait Queue[F[_], A] {
  def enqueue1(a: A): F[Unit]
  def dequeue1: F[A]
}
trait Topic[F[_], A] {
  def publish1(a: A): F[Unit]
  def subscribe(maxQueued: Int): Stream[F, A]
}
trait Signal[F[_], A] { 
  def get: F[A] 
  def set(a: A): F[Unit]
}cats-effect
2017
sealed abstract class IO[+A]
case class Pure[+A](a: A)                                   extends IO[A]
case class Delay[+A](thunk: () => A)                        extends IO[A]
case class RaiseError(e: Throwable)                         extends IO[Nothing]
case class Suspend[+A](thunk: () => IO[A])                  extends IO[A]
case class Bind[E, +A](source: IO[E], f: E => IO[A])        extends IO[A]
case class Async[+A](
   k: (IOConnection, Either[Throwable, A] => Unit) => Unit) extends IO[A]cats-effect
2017
trait Semaphore[F[_]] {    
  def decrementBy(n: Long): F[Unit]
  def incrementBy(n: Long): F[Unit]
}
abstract class MVar[A] {
  def put(a: A): Task[Unit]
  def take: Task[A]
  def read: Task[A]
}
trait Deferred[F[_], A] // Topic {
  def get: F[A]
  def complete(a: A): F[Unit]
}
abstract class Ref[F[_], A] // Signal {
  def get: F[A]
  def set(a: A): F[Unit]
}scalaz8-effect
2017
scalaz-ioeffect
2018
scalaz-zio
2018
ZIO
2019
IO

IO
trait IO[+A]:
  def run(world: World): (A, World)
  def flatMap[B](f: A => IO[B]): IO[B] = world1 =>
    val (a, world2) = run(world1)
    f(a).run(world2)
    
  def unsafeRun() = run(new World)._1
def countZeros(xs: LazyList[Int]): IO[Int] =
  def go(xs: LazyList[Int], ref: IORef[Int]): IO[Unit] = xs match
    case 0 #:: rest => ref.update(_ + 1).flatMap(_ => go(rest, ref))
    case _ #:: rest => go(rest, ref)
    case _          => IO(())
  for
    ref <- IO.newIORef(0)
    _   <- go(xs, ref)
    x   <- ref.get
  yield x
Scala IO
enum IO[+A]:
  def flatMap[B](f: A => IO[B]): IO[B] = FlatMap(this, f)
  case Pure(a: A)
  case FlatMap[A, +B](a: IO[A], f: A => IO[B]) extends IO[B]
  @tailrec final def unsafeRun(): A = this match
    case Pure(a)        => a
    case FlatMap(fx, f) =>
      fx match
        case Pure(x)        => f(x).unsafeRun()
        case FlatMap(fy, g) =>
          fy.flatMap(y => g(y).flatMap(f)).unsafeRun()Scala IO
enum IO[+A]:
  def flatMap[B](f: A => IO[B]): IO[B]                    = 
    Continue(this, _.fold(Throw(_), f))
  def handleWith[A1 >: A](h: Throwable => IO[A1]): IO[A1] = 
    Continue(this, _.fold(h, Pure(_)))
  case Pure(a: A)
  case Throw(err: Throwable)
  case Continue[A, +B](a: IO[A], f: Try[A] => IO[B]) extends IO[B]
  @tailrec private def unsafeRun1(): Try[A] = this match
    case Pure(a)         => Success(a)
    case Throw(err)      => Failure(err)
    case Continue(fa, f) =>
      fa match
        case Pure(a)         => f(Success(a)).unsafeRun1()
        case Throw(err)      => f(Failure(err)).unsafeRun1()
        case Continue(fx, g) => Continue(fx, ta => Continue(g(ta), f)).unsafeRun1()
        
  final def unsafeRun(): Try[A]             =
    try unsafeRun1()
    catch { case NonFatal(err) => Failure(err) }Scala IO
enum IO[+A]:
  case Pure(a: A)
  case Throw(err: Throwable)
  case Continue[A, +B](a: IO[A], f: Try[A] => IO[B]) extends IO[B]
  case Async(cont: (Try[A] => Unit) => Unit, ec: ExecutionContext)
  @tailrec private def unsafeRun1(callback: Try[A] => Unit): Unit = this match
    case Pure(a)         => callback(Success(a))
    case Throw(err)      => callback(Failure(err))
    case Async(cont, _)  => cont(callback)
    case Continue(fa, f) =>
      fa match
        case Pure(a)         => f(Success(a)).unsafeRun1(callback)
        case Throw(err)      => f(Failure(err)).unsafeRun1(callback)
        case Continue(fx, g) => Continue(fx, u => Continue(g(u), f)).unsafeRun1(callback)
        case Async(cont, ec) => cont(e => ec.execute(() => f(e).unsafeRun(callback)))
  final def unsafeRun(callback: Try[A] => Unit): Unit             =
    try unsafeRun1(callback)
    catch { case NonFatal(err) => callback(Failure(err)) }
Common

Common::Ref
abstract class Ref[F[_], A] {
  def get: F[A]
  def set(a: A): F[Unit]
  def modify[B](f: A => (A, B)): F[B]
  def tryModify[B](f: A => (A, B)): F[Option[B]]
 }
 
 sealed abstract class Ref[A]{
  def get: UIO[A]
  def set(a: A): UIO[Unit]
  def modify[B](f: A => (B, A)): UIO[B]
  def setAsync(a: A): UIO[Unit]
 }Common::Promise
abstract class Deferred[F[_], A] {
  def get: F[A]
  def complete(a: A): F[Unit]
 }
 
final class Promise[E, A]{
  def await: IO[E, A]
  def completeWith(io: IO[E, A]): UIO[Boolean]
}Common::Semaphore
abstract class Semaphore[F[_]] {
  def acquire: F[Unit]
  def release: F[Unit]
  def permit: Resource[F, Unit]
}
 
final class Semaphore{
  def withPermits[R, E, A](n: Long)(task: ZIO[R, E, A]): ZIO[R, E, A]
  def withPermitManaged[R, E]: ZManaged[R, E, Unit]
}Common::Queue
abstract class Queue[F[_], A] {
  def offer(a: A): F[Unit]
  def take: F[A]
}
 
abstract class Queue[A]{
  def offer(a: A): UIO[Boolean]
  def take: UIO[A]
}Common::Resource
sealed abstract class Resource[F[_], +A]  {
  def use[B](f: A => F[B])(implicit F: BracketThrow[F]): F[B]
  def flatMap[B](f: A => Resource[F, B]): Resource[F, B]
}
object Resource{
  def make[F[_]: Functor, A](
    acquire: F[A])(
    release: A => F[Unit]): Resource[F, A]
}
 
abstract class ZManaged[-R, +E, +A]{
  def use[B](f: A => ZIO[R, E, B]): ZIO[R, E, B]
  def flatMap[R, E, B](f: A => ZManaged[R, E, B]): ZManaged[R, E, B]
}
object ZManaged{
  def make[R, E, A](
    acquire: ZIO[R, E, A])(
    release: A => ZIO[R, Nothing, Any]): ZManaged[R, E, A]
}Common::Resource
val application: Resource[IO, Unit] = for {
   config <- readConfig
   state  <- initializeState(config)
   db     <- initializeDB(config.db)
   kafka  <- connectToKafka(config.kafka)
   _      <- startKafkaListeners(config.topics, kafka, db)
   _      <- runBackroundWorkers(config.bg)
   _      <- startHttpServer(config.http, db)
   _      <- IO.never
} yield ()Common::Fiber
sealed abstract class IO[+A]{
  def start: IO[Fiber[IO, Throwable, A]]
}
trait GenSpawn[F[_], E]{
  def start[A](fa: F[A]): F[Fiber[F, E, A]]
}
trait Fiber[F[_], E, A]{
  def join: F[Outcome[F, E, A]]
  def cancel: F[Unit]
}
 
sealed trait ZIO[-R, +E, +A]{
  final def fork: URIO[R, Fiber[E, A]]
}
sealed abstract class Fiber[+E, +A]{
  def await: UIO[Exit[E, A]]
  def interrupt: UIO[Exit[E, A]]
}Common::FiberLocal
sealed abstract class IOLocal[+A]{
  def get: IO[A]
  def set(value: A): IO[Unit]
  def reset: IO[Unit]
}
object IOLocal {
  def apply[A](default: A): IO[IOLocal[A]] 
}
final class FiberRef[A]{
  val get: UIO[A]  
  def set(value: A): UIO[Unit]
}
object FiberRef {
  def make[A](initial: A, 
    fork: A => A = (a: A) => a, 
    join: (A, A) => A = ((_: A, a: A) => a)): UIO[FiberRef[A]] =
}
Common
- 
	IO монада
- 
	Структуры коммуникации- Ref
- Queue
- Promise
- Semaphore
- FiberLocals
 
- 
	Fiber concurrency
- 
	Структурный контроль за ресурсами
Compete

Compete::Performance


Compete::Streams
FS2
- Pull-based
- Doobie
- Http4s
- ....
zio-streams
- iterator-like
- ...:)??
Compete::Streams
FS2
- Pull-based
- Doobie
- Http4s
- ....
zio-streams
- iterator-like
- ...:)??
Compete::Context
case class ApplicationContext(
   traceId: TraceId,
   spanId: SpanId,
   currentUser: User,
   pgPool: PostgresPool[IO],
   config: Config
)type F[A] = ReaderT[ApplicationContext, IO, A]
final case class Kleisli[F[_], -A, B](run: A => F[B]){
  def local[C](f: C => A): Kleisli[F, C, B]
  def ask[F[_], A](implicit F: Applicative[F]): Kleisli[F, A, A]
}sealed trait ZIO[-R, +E, +A]{
  def provide(r: R): IO[E, A] = ZIO.provide(r)(self)
  def provideSome[C](f: C => R): ZIO[C, E, A]
}
object ZIO{
  def environment[R]: URIO[R, R] = access(r => r)
}Cats-Effect
ZIO
Compete::Context
case class ApplicationContext(
   traceId: TraceId,
   spanId: SpanId,
   currentUser: User,
   pgPool: PostgresPool[IO],
   config: Config
)type F[A] = ReaderT[ApplicationContext, IO, A]
final case class Kleisli[F[_], -A, B](run: A => F[B]){
  def local[C](f: C => A): Kleisli[F, C, B]
  def ask[F[_], A](implicit F: Applicative[F]): Kleisli[F, A, A]
}sealed trait ZIO[-R, +E, +A]{
  def provide(r: R): IO[E, A] = ZIO.provide(r)(self)
  def provideSome[C](f: C => R): ZIO[C, E, A]
}
object ZIO{
  def environment[R]: URIO[R, R] = access(r => r)
}Cats-Effect
ZIO
Compete::Unsafe Execution
trait Effect[F[_]]
     def runAsync[A](fa: F[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit]
}trait Dispatcher[F[_]]{
     def unsafeToFuture[A](fa: F[A]): Future[A]
}
object Dispatcher{
     def apply[F[_]](implicit F: Async[F]): Resource[F, Dispatcher[F]]
}object ZIO{
   def runtime[R]: URIO[R, Runtime[R]]
}
trait Runtime[R]{
  def unsafeRunAsync[E, A](zio: => ZIO[R, E, A])(k: Exit[E, A] => Any): Unit
}Cats-Effect 2
Cats-Effect 3
ZIO
Compete::Unsafe Execution
trait Effect[F[_]]
     def runAsync[A](fa: F[A])(cb: Either[Throwable, A] => IO[Unit]): SyncIO[Unit]
}trait Dispatcher[F[_]]{
     def unsafeToFuture[A](fa: F[A]): Future[A]
}
object Dispatcher{
     def apply[F[_]](implicit F: Async[F]): Resource[F, Dispatcher[F]]
}object ZIO{
   def runtime[R]: URIO[R, Runtime[R]]
}
trait Runtime[R]{
  def unsafeRunAsync[E, A](zio: => ZIO[R, E, A])(k: Exit[E, A] => Any): Unit
}Cats-Effect 2
Cats-Effect 3
ZIO
Compete::DomainErrors
trait UserRepository{
  def findUser(id: UserId): IO[Either[UserNotFound, User]]
  def createUser(user: User): IO[Either[AlreadyExists, UserId]]
}trait UserRepository{
  def findUser(id: UserId): EitherT[IO, UserNotFound, User]]
  def createUser(user: User): EitherT[IO, AlreadyExists, UserId]]
}trait UserRepository{
  def findUser(id: UserId): IO[UserNotFound, User]
  def createUser(user: User): IO[AlreadyExists, UserId]
}Vanilla
Cats-Effect
ZIO
Compete::DomainErrors
trait UserRepository{
  def findUser(id: UserId): IO[Either[UserNotFound, User]]
  def createUser(user: User): IO[Either[AlreadyExists, UserId]]
}trait UserRepository{
  def findUser(id: UserId): EitherT[IO, UserNotFound, User]]
  def createUser(user: User): EitherT[IO, AlreadyExists, UserId]]
}trait UserRepository{
  def findUser(id: UserId): IO[UserNotFound, User]
  def createUser(user: User): IO[AlreadyExists, UserId]
}Vanilla
Cats-Effect
ZIO
Cats Effect Specials
- 
	F[_] + Type-class hierarchy
- 
	Fiber-aware thread pool (zio 2?)
ZIO Specials
- 
	STM (cats-stm?)
- 
	Module-pattern- 
		Module-pattern 2.0 + ZLayer
- 
		Distage
 
- 
		
- 
	ZSchedule (fs-timeseries?)
Итоги
- Используйте cats-effect
- Используйте ZIO
- Используйте Tofu
Благодарю за внимание

Эти слайды:
Cats vs ZIO
By Oleg Nizhnik
Cats vs ZIO
- 1,961
 
   
  