Comfy Cats Effect room. Ask questions. Rant about config file formats. Be nice!
IO
is two free monads glued together with an interpreter that can sequence back and forth between them. Cont
is itself a free monad for a coalgebra, while Free
is obviously… uh, Free
, and we instantiate it with a trivial algebra
raiseError
is hard with the above)
Cont
is so bizarre
ContT
is even more bizarre
Task.liftIO
flatMap
on ContT
I would start looking at the runloop from these 3 points:
IO
it's a tree that gets translated, and each node in the tree represent a fundamental concept (so you have Async
, Delay
, Attempt
, Pure
, FlatMap
)case class State(current: IO[Any], stack: List[Any => IO[Any]]
. Each iteration pattern matches on current
, does some stuff, and keeps going by taking the next off the stack.Async
is case class Async(f: (Either[Throwable, A] => Unit) => Unit) extends IO[A]
. The key point is that Async
does not introduce any asynchrony, it just wraps something that can already do asynchrony by itself. It's kinda funny in that Async
does most of the work for the "complex" stuff, but actually the only thing it does is passing a function to another function.I can expand and unpack some of this if needed
runAsync
takes one
val latch = new OneShotLatch
var ref: Either[Throwable, A] = null
ioa unsafeRunAsync { a =>
// Reading from `ref` happens after the block on `latch` is
// over, there's a happens-before relationship, so no extra
// synchronization is needed for visibility
ref = a
latch.releaseShared(1)
()
}
acquire
that latch, which blocks the thread until it gets released
ref