Skip to content

Instantly share code, notes, and snippets.

@stew
Last active August 29, 2015 14:27
Show Gist options
  • Save stew/fc73bf83a68e4ebb27b2 to your computer and use it in GitHub Desktop.
Save stew/fc73bf83a68e4ebb27b2 to your computer and use it in GitHub Desktop.
this flatmap is probably not stacksafe
package cats
package effect
import java.util.concurrent.CountDownLatch
/**
* A computation of an A value
*/
sealed trait Comp[A] {
import Comp._
def map[B](f: A => B): Comp[B] = (this: @unchecked) match {
case Value(e) => Value(e map f)
case Async(onFinish) => BoundAsync[A,B](onFinish, (a => Value(Now(f(a)))))
case BoundAsync(onFinish, ff) => BoundAsync(onFinish, ((a:Any) => ff(a).map(f)))
}
def flatMap[B](f: A => Comp[B]): Comp[B] = (this: @unchecked) match {
case Value(e) => Value(e.flatMap(f(_).toEval))
case Async(onFinish) => BoundAsync[A,B](onFinish, f)
case BoundAsync(onFinish, ff) =>
BoundAsync(onFinish, ((a:Any) => ff(a).flatMap(f)))
}
def run: A = this match {
case Value(e) => e.value
case Async(onFinish) =>
val latch = new CountDownLatch(1)
@volatile var result: Option[A] = None
runAsync { a => result = Some(a); latch.countDown }
latch.await
result.get
case BoundAsync(onFinish, f) =>
val latch = new CountDownLatch(1)
@volatile var result: Option[A] = None
runAsync { a => result = Some(a); latch.countDown }
latch.await
result.get
}
def toEval: Eval[A] = this match {
case Value(e) => e
case Async(onFinish) =>
Eval.always {
val latch = new CountDownLatch(1)
@volatile var result: Option[A] = None
runAsync { a => result = Some(a); latch.countDown }
latch.await
result.get
}
case BoundAsync(onFinish, f) =>
Eval.always {
val latch = new CountDownLatch(1)
@volatile var result: Option[Eval[A]] = None
runAsync { a => result = Some(f(a).toEval); latch.countDown }
latch.await
result.get
}.flatMap(identity)
}
def runAsync(cb: A => Unit): Unit =
this match {
case Value(Now(a)) => cb(a)
case Async(onFinish) => onFinish(cb)
case BoundAsync(onFinish,f) => onFinish{x => f(x).runAsync(cb)}
case Value(e) => evalAsync(e, cb)
}
private def evalAsync(e: Eval[A], cb: A => Unit): Unit = {
val latch = new CountDownLatch(1)
@volatile var result: Option[A] = None
runAsync { a => result = Some(a); latch.countDown }
latch.await
cb(result.get)
}
}
object Comp {
def now[A](a: A): Comp[A] = Value(Now(a))
def always[A](a: => A): Comp[A] = Value(Always(a))
def later[A](a: => A): Comp[A] = Value(Later(a))
def async[A](onFinish: (A => Unit) => Unit) = Async(onFinish)
case class Value[A](e: Eval[A]) extends Comp[A] {
override def map[B](f: A => B): Comp[B] = Value(e map f)
}
case class Async[A](onFinish: (A => Unit) => Unit) extends Comp[A] {
override def map[B](f: A => B): Comp[B] = Async(cb => onFinish(a => cb(f(a))))
}
case class BoundAsync[A,B](onFinish: (A => Unit) => Unit,
f: A => Comp[B]) extends Comp[B] {
override def map[C](ff: B => C): Comp[C] =
BoundAsync[A,C](onFinish, (a => f(a).map(ff)))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment