diff --git a/tests/pos/suspend-strawman/choices.scala b/tests/pos/suspend-strawman/choices.scala new file mode 100644 index 000000000000..968c223d9c0b --- /dev/null +++ b/tests/pos/suspend-strawman/choices.scala @@ -0,0 +1,28 @@ +import scala.util.boundary, boundary.Label +import runtime.suspend + +trait Choice: + def choose[A](choices: A*): A + +// the handler +def choices[T](body: Choice ?=> T): Seq[T] = + boundary[Seq[T]]: + given Choice with + def choose[A](choices: A*): A = + suspend[A, Seq[T]](s => choices.flatMap(s.resume)) + Seq(body) + +def choose[A](choices: A*)(using c: Choice): A = c.choose(choices*) + +def TestChoices: Seq[Int] = + choices: + def x = choose(1, -2, -3) + def y = choose("ab", "cde") + val xx = x; + xx + ( + if xx > 0 then + val z = choose(xx / 2, xx * 2) + y.length * z + else y.length + ) + diff --git a/tests/pos/suspend-strawman/generators.scala b/tests/pos/suspend-strawman/generators.scala new file mode 100644 index 000000000000..a890196e6215 --- /dev/null +++ b/tests/pos/suspend-strawman/generators.scala @@ -0,0 +1,67 @@ +import scala.util.boundary +import scala.compiletime.uninitialized +import runtime.suspend + +trait CanProduce[-T]: + def produce(x: T): Unit + +object generate: + + def produce[T](x: T)(using cp: CanProduce[T]): Unit = cp.produce(x) + + def apply[T](body: CanProduce[T] ?=> Unit): Iterator[T] = new: + var nextKnown: Boolean = false + var nextElem: Option[T] = uninitialized + + var step: () => Unit = () => + boundary[Unit]: + given CanProduce[T] with + def produce(x: T): Unit = + nextElem = Some(x) + suspend[Unit, Unit]: k => + step = () => k.resume(()) + body + nextElem = None + + def hasNext: Boolean = + if !nextKnown then { step(); nextKnown = true } + nextElem.isDefined + + def next: T = + require(hasNext) + nextKnown = false + nextElem.get +end generate + +enum Tree[T]: + case Leaf(x: T) + case Inner(xs: List[Tree[T]]) + +def leafs[T](t: Tree[T]): Iterator[T] = + generate: + def recur(t: Tree[T]): Unit = t match + case Tree.Leaf(x) => generate.produce(x) + case Tree.Inner(xs) => xs.foreach(recur) + recur(t) + +object Variant2: + trait Generator[T]: + def nextOption: Option[T] + + def generate[T](body: CanProduce[T] ?=> Unit): Generator[T] = new: + + def nextOption: Option[T] = + step() + + var step: () => Option[T] = () => + boundary: + given CanProduce[T] with + def produce(x: T): Unit = + suspend[Unit, Option[T]]: k => + step = () => k.resume(()) + Some(x) + body + None + end generate + + diff --git a/tests/pos/suspend-strawman/monadic-reflect.scala b/tests/pos/suspend-strawman/monadic-reflect.scala new file mode 100644 index 000000000000..84c5255c2a96 --- /dev/null +++ b/tests/pos/suspend-strawman/monadic-reflect.scala @@ -0,0 +1,56 @@ +import scala.util.boundary +import runtime.suspend + +trait Monad[F[_]]: + + /** The unit value for a monad */ + def pure[A](x: A): F[A] + + extension [A](x: F[A]) + /** The fundamental composition operation */ + def flatMap[B](f: A => F[B]): F[B] + + /** The `map` operation can now be defined in terms of `flatMap` */ + def map[B](f: A => B) = x.flatMap(f.andThen(pure)) + +end Monad + +trait CanReflect[M[_]]: + def reflect[R](mr: M[R]): R + +trait Monadic[M[_]: Monad]: + + /** + * Embedding of pure values into the monad M + */ + def pure[A](a: A): M[A] + + /** + * Sequencing of monadic values + * + * Implementations are required to implement sequencing in a stack-safe + * way, that is they either need to implement trampolining on their own + * or implement `sequence` as a tail recursive function. + * + * Actually the type X can be different for every call to f... + * It is a type aligned sequence, but for simplicity we do not enforce this + * here. + */ + def sequence[X, R](init: M[X])(f: X => Either[M[X], M[R]]): M[R] + + /** + * Helper to summon and use an instance of CanReflect[M] + */ + def reflect[R](mr: M[R])(using r: CanReflect[M]): R = r.reflect(mr) + + /** + * Reify a computation into a monadic value + */ + def reify[R](prog: CanReflect[M] ?=> R): M[R] = + boundary [M[R]]: + given CanReflect[M] with + def reflect[R2](mr: M[R2]): R2 = + suspend [R2, M[R]] (k => mr.flatMap(k.resume)) + pure(prog) + +end Monadic \ No newline at end of file diff --git a/tests/pos/suspend-strawman/runtime.scala b/tests/pos/suspend-strawman/runtime.scala new file mode 100644 index 000000000000..406da4f7dd5e --- /dev/null +++ b/tests/pos/suspend-strawman/runtime.scala @@ -0,0 +1,11 @@ +package runtime +import scala.util.boundary, boundary.Label + +/** A hypothetical API for suspensions. Not yet implemented. + * Suspension contain a delimited contination, which can be + * invoked with `resume` + */ +class Suspension[-T, +R]: + def resume(arg: T): R = ??? + +def suspend[T, R](body: Suspension[T, R] => R)(using Label[R]): T = ??? diff --git a/tests/pos/suspend-strawman/simple-futures.scala b/tests/pos/suspend-strawman/simple-futures.scala new file mode 100644 index 000000000000..0a80a74d49dc --- /dev/null +++ b/tests/pos/suspend-strawman/simple-futures.scala @@ -0,0 +1,53 @@ +package simpleFutures + +import scala.collection.mutable.ListBuffer +import scala.util.boundary, boundary.Label +import runtime.suspend + +object Scheduler: + def schedule(task: Runnable): Unit = ??? + +trait Async: + def await[T](f: Future[T]): T + +class Future[+T](body: Async ?=> T): + private var result: Option[T] = None + private var waiting: ListBuffer[T => Unit] = ListBuffer() + private def addWaiting(k: T => Unit): Unit = waiting += k + + def await(using a: Async): T = a.await(this) + + private def complete(): Unit = + Future.async: + val value = body + val result = Some(value) + for k <- waiting do + Scheduler.schedule(() => k(value)) + waiting.clear() + + Scheduler.schedule(() => complete()) + +object Future: + + // a handler for Async + def async(body: Async ?=> Unit): Unit = + boundary [Unit]: + given Async with + def await[T](f: Future[T]): T = f.result match + case Some(x) => x + case None => suspend[T, Unit](s => f.addWaiting(s.resume)) + body + +end Future + +def Test(x: Future[Int], xs: List[Future[Int]]) = + Future: + x.await + xs.map(_.await).sum + + + + + + + +