Last active
August 29, 2015 14:27
-
-
Save stew/fc73bf83a68e4ebb27b2 to your computer and use it in GitHub Desktop.
this flatmap is probably not stacksafe
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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