~zainab/blog

ref: 40e76d367e9268aa1bcfcb1e08e750bfa9fcf9cf blog/src/chapters/fs2/snippets.md -rw-r--r-- 7.2 KiB
40e76d36zainab-ali Fix typos 1 year, 1 month ago

#sbt

ThisBuild / scalaVersion := "3.0.2"
ThisBuild / libraryDependencies += "co.fs2" %% "fs2-core" % "3.1.3"

ThisBuild / initialCommands := s"""
    import fs2._, cats.effect._, cats.effect.unsafe.implicits.global
  """

#imports

import $ivy.`co.fs2::fs2-core:3.1.3`, fs2._
import $ivy.`org.typelevel::cats-effect:3.2.9`, cats.effect._
import cats.effect.unsafe.implicits.global

#definition

def sim(numberOfRolls: Int,
        jiaoziToServe: Int): (Bowl, Leftovers) = ???

#model

type Dough = Int
type Jiaozi = Int
type Bowl = List[Jiaozi]
type Leftovers = List[Jiaozi]

#structure

def roll(rollsToMake: Int): Stream[Pure, Dough] = ???

val cook: Pipe[Pure, Dough, Jiaozi] = ???

def serve(jiaoziToServe: Int): Pipe[Pure, Jiaozi, Jiaozi] = ???

#rolling

def roll(rollsToMake: Int): Stream[Pure, Dough] =
  Stream.iterate(0)(_ + 1).take(rollsToMake)

#rolling-test

roll(2).compile.toList

#cooking

val cook: Pipe[Pure, Dough, Jiaozi] = _.flatMap { dough =>
  Stream(
    dough * 3,
    dough * 3 + 1,
    dough * 3 + 2
  )
}

#cooking-test

roll(2).through(cook).compile.toList

#serving

def serve(jiaoziToServe: Int): Pipe[Pure, Jiaozi, Jiaozi] =
 _.take(jiaoziToServe)

#serving-test

roll(2).through(cook).through(serve(4)).compile.toList

#box-model

type Box = Ref[IO, Leftovers]

#empty-box

val emptyBox: IO[Box] = Ref.of(Nil)
val box: Box = emptyBox.unsafeRunSync()

#add-leftovers

box.update(leftovers => 3 :: leftovers).unsafeRunSync()

#get-leftovers

box.get.unsafeRunSync()

#store-definition

def store(box: Box): Pipe[IO, Jiaozi, Nothing] = ???

#store

def store(box: Box): Pipe[IO, Jiaozi, Nothing] =
  _.evalMap(jiaozi => box.update(jiaozi :: _))
    .drain

#store-test

{
  for {
    box <- emptyBox
    _ <- Stream(1, 2, 3).through(store(box)).compile.drain
    leftovers <- box.get
  } yield leftovers
}.unsafeRunSync()

#sim-effect-definition

def sim(numberOfRolls: Int,
        jiaoziToServe: Int): IO[(Bowl, Leftovers)] = ???

#sim-wrong-implementation

def sim(numberOfRolls: Int,
        jiaoziToServe: Int): IO[(Bowl, Leftovers)] = {
  for {
    box <- emptyBox
    bowl = roll(numberOfRolls)
        .through(cook)
        .through(serve(jiaoziToServe))
        .compile
        .toList
    leftovers <- box.get
  } yield (bowl, leftovers)
}

#sim-test

sim(2, 4).unsafeRunSync()

#serveThen-definition

def serveThen(n: Int,
              store: Pipe[IO, Jiaozi, Nothing]
              ): Pipe[IO, Jiaozi, Jiaozi] = ???

#sim-implementation

def sim(numberOfRolls: Int,
        jiaoziToServe: Int): IO[(Bowl, Leftovers)] = {
  for {
    box <- emptyBox
    bowl <- roll(numberOfRolls)
        .through(cook)
        .through(serveThen(jiaoziToServe, store(box)))
        .compile
        .toList
    leftovers <- box.get
  } yield (bowl, leftovers)
}

#hello-stream

val helloStream = Stream("hello")

#hello-pull

val helloPull = Pull.output1("hello")

#hello-pull-to-stream

helloPull.stream

#hello-run

helloStream.compile.toList
helloPull.stream.compile.toList

#done

Pull.done.stream

#hello-echo

helloStream.pull.echo

#hello-echo-back-forth

helloStream.pull.echo.stream.compile.toList
helloStream.pull.echo.stream.pull.echo.stream.compile.toList
helloStream.compile.toList

#hello-world

val helloWorldPull = helloPull >> Pull.output1("world")

#hello-world-run

helloWorldPull.stream.toList
(helloStream ++ Stream("world")).toList

#world-pipe

def world: Pipe[Pure, String, String] =
  in => (in.pull.echo >> Pull.output1("world")).stream

#hello-pull-type

helloPull

#pull-pure

val helloResult = Pull.pure("hello")

#hello-result-stream

helloResult.stream

#pull-flatmap-definition

class Pull[F, O, R] {
  def flatMap[R1](f: R => Pull[F, O, R1]): Pull[F, O, R1] = ???
}

#pull-flatmap

val outputHello = helloResult.flatMap { (text: String) =>
  Pull.output1(text)
}

#pull-output-hello-stream

outputHello.stream.compile.toList

#hello-stream-uncons

helloStream.pull.uncons1

#take1

def take1: Pipe[Pure, String, String] = { in =>
  in.pull.uncons1.flatMap { 
     ???
  }
}

#take1-complete

def take1: Pipe[Pure, String, String] = { in =>
  in.pull.uncons1.flatMap {
    case Some((h, _)) => Pull.output1(h) // Ⓐ
    case None => Pull.done // Ⓑ
  }.stream
}

#take1-run

helloStream.through(take1).compile.toList
Stream.empty.through(take1).compile.toList
Stream("hello", "world").through(take1).compile.toList

#pull-take

helloStream.pull.take(1)

#serve-then-pull

def serveThen(n: Int,
              store: Pipe[IO, Jiaozi, Nothing]
              ): Pipe[IO, Jiaozi, Jiaozi] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => ???
    case None => Pull.done
  }.stream
}

#serve-then-pull-v2

def serveThen(n: Int, 
              store: Pipe[IO, Jiaozi, Nothing]
              ): Pipe[IO, Jiaozi, Jiaozi] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => rest.through(store)
                       ???
    case None => Pull.done
  }.stream
}

#serve-then-pull-complete

def serveThen(n: Int, 
              store: Pipe[IO, Jiaozi, Nothing]
              ): Pipe[IO, Jiaozi, Jiaozi] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => rest.through(store).pull.echo
    case None => Pull.done
  }.stream
}

#final-sim

def sim(numberOfRolls: Int,
        jiaoziToServe: Int): IO[(Bowl, Leftovers)] = {
  for {
    box <- emptyBox
    bowl <- roll(numberOfRolls)
        .through(cook)
        .through(serveThen(jiaoziToServe, store(box)))
        .compile
        .toList
    leftovers <- box.get
  } yield (bowl, leftovers)
}

#run-sim

val (bowl, leftovers) = sim(2, 4).unsafeRunSync()

#serve-then-pull-parameterized

def serveThen[I](n: Int, 
              store: Pipe[IO, I, Nothing]
              ): Pipe[IO, I, I] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => rest.through(store).pull.echo
    case None => Pull.done
  }.stream
}

#serve-then-pull-parameterized-more

def serveThen[F[_], I](n: Int, 
              store: Pipe[F, I, Nothing]
              ): Pipe[F, I, I] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => rest.through(store).pull.echo
    case None => Pull.done
  }.stream
}

#serve-then-pull-parameterized-more-renamed

def takeThen[F[_], I](n: Int, 
              pipe: Pipe[F, I, Nothing]
              ): Pipe[F, I, I] = { in =>
  in.pull.take(n).flatMap {
    case Some(rest) => rest.through(pipe).pull.echo
    case None => Pull.done
  }.stream
}