The Pierian Stream

Zainab Ali

Stream

Pierian

“A body of running water (such as a river or creek) flowing on earth.”

cat log.txt | uniq
java.io.FileOutputStream

Operators

repeat

infinity

evalMap

side effects

merge

concurrency

Composition

Stream("Mao", "Popcorn")
  .evalMap(meow)
  .repeat
  .evalMap(eat)

And many more

fold
map
filter
collect
evalMap
evalScan
evalFilter
evalTap
merge
zip
append
flatMap
debounce
interruptAfter
groupWithin
delayBy

fs2 is awesome

...or is it?

The life of cats

def eat(cat: String): IO[String] =
  IO.println(s"$cat eats.").as(cat)

def nap(cat: String): IO[Unit] =
  IO.println(s"$cat naps.")
Stream("Mao", "Popcorn")
  .evalMap(eat)
  .evalMap(nap)
  .repeat
Stream("Mao", "Popcorn")
  .evalMap(eat)
  .evalMap(nap)
  .repeat

1

Mao naps.
Mao eats.
Popcorn naps.
Popcorn eats.

2

Mao eats.
Mao naps.
Popcorn eats.
Popcorn naps.

3

Mao eats.
Popcorn eats.
Mao naps.
Popcorn naps.

Does simple to read

mean simple to reason?

Pierian

“Of or relating to learning”.

Analogies

“A little learning is a dang'rous thing; Drink deep, or taste not the Pierian spring”

⸺ An Essay on Criticism, Alexander Pope

Analogies

Pros

  • Physical
  • Easy to start

Cons

  • Fuzzy
  • Misconceptions

Mental models

Experimentation

Stream("Mao", "Popcorn")
  .evalMap(eat)
  .evalMap(nap)
  .repeat
Mao eats.
Mao naps.
Popcorn eats.
Popcorn naps.

Experimentation

Pros

  • Independent
  • Easy to do

Cons

  • Need many
  • Nondeterminism

Substitution

getOrElse(Some("Mao"), "Popcorn") === "Mao"
getOrElse(Some("Mao"), "Popcorn")

Some("Mao") match
  case Some(name) => name
  case None => "Popcorn"

case Some("Mao") => "Mao"
"Mao"
Stream("Mao", "Popcorn")
  .evalMap(eat)
  .evalMap(nap)
  .repeat
Pull.output(Seq("Mao", "Popcorn")).streamNoScope.underlying
  .evalMap(eat)
  .evalMap(nap)
  .repeat
Pull.output(Seq("Mao", "Popcorn"))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(eat(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
  .evalMap(nap)
  .repeat
Pull.output(Seq("Mao", "Popcorn"))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(eat(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(nap(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
  .repeat
Pull.output(Seq("Mao", "Popcorn"))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(eat(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(nap(o)).flatMap(Pull.output1))
    .streamNoScope.underlying ++
Pull.output(Seq("Mao", "Popcorn"))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(eat(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(nap(o)).flatMap(Pull.output1))
    .streamNoScope.underlying ++
Pull.output(Seq("Mao", "Popcorn"))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(eat(o)).flatMap(Pull.output1))
    .streamNoScope.underlying
    .flatMapOutput(o => Pull.eval(nap(o)).flatMap(Pull.output1))
    .streamNoScope.underlying ++ ...

Mental stack overflow

Evaluation / substitution

Pros

  • Set of steps
  • Consistent
  • Predictions

Cons

  • Small functions
  • Internal exposure

Equational reasoning

1

s.repeat === s ++ s ++ s ++ ...

2

(s1 ++ s2).evalMap(f) === s1.evalMap(f) ++ s2.evalMap(f)

Prove:

s.repeat.evalMap(eat) === s.evalMap(eat).repeat
mao.repeat.evalMap(eat) === mao.evalMap(eat).repeat
mao.repeat.evalMap(eat)
(mao ++ mao ++ ...).evalMap(eat)             // law 1
(mao.evalMap(eat) ++ mao.evalMap(eat) + ...) // law 2
mao.evalMap(eat).repeat                      // law 1

A monad is just a monoid in the category of endofunctors, what's the problem?

Equational reasoning

Pros

  • Consistent
  • Predictions
  • No internals

Cons

  • Mathematical
  • Hard to spot

Without a paddle

  • Analogies
  • Experiments
  • Substitution & evaluation
  • Equational reasoning

A better model

Physical analogy

Set of steps

Operators and composition

Predict behaviour

The pull model

We “pull” on a stream.

An operator:

  • “pulls” on its upstream.
  • Evaluates effects.
  • Outputs its result.
  • Or is “done”.

The pull model

Stream

evalMap

repeat

A better model

  • A precise sequence of steps
  • Describes composition
  • Can use new operators

Limited

Pieria

  • Streams
  • Reasoning
  • Analogies, experiments, mental models
  • The pull model

Revolutionary

or is it?

More broadly

  • Cats effect
  • Optics

Thank you!

Questions

The Pierian Stream

By Zainab Ali

The Pierian Stream

  • 760