Skip to content

Instantly share code, notes, and snippets.

@zainab-ali
Created July 10, 2023 18:43
Show Gist options
  • Save zainab-ali/e584e696ddbc4db9a4682ebf17228947 to your computer and use it in GitHub Desktop.
Save zainab-ali/e584e696ddbc4db9a4682ebf17228947 to your computer and use it in GitHub Desktop.
Basic pull free monad for pure infinite streams
enum Pull[A] {
case Pure[A](value: A) extends Pull[A]
case Output(name: String) extends Pull[Unit]
case Uncons[A](pull: Pull[A]) extends Pull[Option[(String, Pull[A])]]
case FlatMap[A, B](pull: Pull[A], f: A => Pull[B]) extends Pull[B]
}
import Pull._
val done: Pull[Unit] = Pure(())
def single(name: String): Pull[Unit] = Output(name)
def append(pull: Pull[Unit], name: String) =
FlatMap(pull, _ => single(name))
def repeat(pull: Pull[Unit]): Pull[Unit] = FlatMap(pull, _ => repeat(pull))
def take(n: Int, pull: Pull[Unit]): Pull[Unit] = {
def onOutput(element: Option[(String, Pull[Unit])]): Pull[Unit] =
element match {
case Some((name, rest)) =>
FlatMap(
Output(name),
_ => take(n - 1, rest)
)
case None => done
}
if n <= 0 then done else FlatMap(Uncons(pull), onOutput)
}
def step[A](pull: Pull[A]): Either[(String, Pull[A]), A] =
pull match {
case Pure(v) => Right(v)
case Output(name) => Left((name, done))
case Uncons(pull) =>
step(pull) match {
case Left((name, next)) => Right(Some((name, next)))
case Right(a) => Right(None)
}
case FlatMap(pull, f) =>
step(pull) match {
case Left((name, next)) =>
Left((name, FlatMap(next, f)))
case Right(a) => step(f(a))
}
}
def last(pull: Pull[Unit], prev: Option[String]): Option[String] = step(
pull
) match {
case Left((name, next)) => last(next, Some(name))
case Right(()) => prev
}
val kittens = append(single("Mao"), "Popcorn")
println(last(take(4, repeat(kittens)), None))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment