Skip to content

Commit 2417f19

Browse files
committed
Add suspend strawman
Add a test that combines various experiments using our hypothetical Suspension abstractions.
1 parent 3a830c8 commit 2417f19

File tree

5 files changed

+215
-0
lines changed

5 files changed

+215
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import scala.util.boundary, boundary.Label
2+
import runtime.suspend
3+
4+
trait Choice:
5+
def choose[A](choices: A*): A
6+
7+
// the handler
8+
def choices[T](body: Choice ?=> T): Seq[T] =
9+
boundary[Seq[T]]:
10+
given Choice with
11+
def choose[A](choices: A*): A =
12+
suspend[A, Seq[T]](s => choices.flatMap(s.resume))
13+
Seq(body)
14+
15+
def choose[A](choices: A*)(using c: Choice): A = c.choose(choices*)
16+
17+
def TestChoices: Seq[Int] =
18+
choices:
19+
def x = choose(1, -2, -3)
20+
def y = choose("ab", "cde")
21+
val xx = x;
22+
xx + (
23+
if xx > 0 then
24+
val z = choose(xx / 2, xx * 2)
25+
y.length * z
26+
else y.length
27+
)
28+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import scala.util.boundary
2+
import scala.compiletime.uninitialized
3+
import runtime.suspend
4+
5+
trait CanProduce[-T]:
6+
def produce(x: T): Unit
7+
8+
object generate:
9+
10+
def produce[T](x: T)(using cp: CanProduce[T]): Unit = cp.produce(x)
11+
12+
def apply[T](body: CanProduce[T] ?=> Unit): Iterator[T] = new:
13+
var nextKnown: Boolean = false
14+
var nextElem: Option[T] = uninitialized
15+
16+
var step: () => Unit = () =>
17+
boundary[Unit]:
18+
given CanProduce[T] with
19+
def produce(x: T): Unit =
20+
nextElem = Some(x)
21+
suspend[Unit, Unit]: k =>
22+
step = () => k.resume(())
23+
body
24+
nextElem = None
25+
26+
def hasNext: Boolean =
27+
if !nextKnown then { step(); nextKnown = true }
28+
nextElem.isDefined
29+
30+
def next: T =
31+
require(hasNext)
32+
nextKnown = false
33+
nextElem.get
34+
end generate
35+
36+
enum Tree[T]:
37+
case Leaf(x: T)
38+
case Inner(xs: List[Tree[T]])
39+
40+
def leafs[T](t: Tree[T]): Iterator[T] =
41+
generate:
42+
def recur(t: Tree[T]): Unit = t match
43+
case Tree.Leaf(x) => generate.produce(x)
44+
case Tree.Inner(xs) => xs.foreach(recur)
45+
recur(t)
46+
47+
object Variant2:
48+
trait Generator[T]:
49+
def nextOption: Option[T]
50+
51+
def generate[T](body: CanProduce[T] ?=> Unit): Generator[T] = new:
52+
53+
def nextOption: Option[T] =
54+
step()
55+
56+
var step: () => Option[T] = () =>
57+
boundary:
58+
given CanProduce[T] with
59+
def produce(x: T): Unit =
60+
suspend[Unit, Option[T]]: k =>
61+
step = () => k.resume(())
62+
Some(x)
63+
body
64+
None
65+
end generate
66+
67+
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import scala.util.boundary
2+
import runtime.suspend
3+
4+
trait Monad[F[_]]:
5+
6+
/** The unit value for a monad */
7+
def pure[A](x: A): F[A]
8+
9+
extension [A](x: F[A])
10+
/** The fundamental composition operation */
11+
def flatMap[B](f: A => F[B]): F[B]
12+
13+
/** The `map` operation can now be defined in terms of `flatMap` */
14+
def map[B](f: A => B) = x.flatMap(f.andThen(pure))
15+
16+
end Monad
17+
18+
trait CanReflect[M[_]]:
19+
def reflect[R](mr: M[R]): R
20+
21+
trait Monadic[M[_]: Monad]:
22+
23+
/**
24+
* Embedding of pure values into the monad M
25+
*/
26+
def pure[A](a: A): M[A]
27+
28+
/**
29+
* Sequencing of monadic values
30+
*
31+
* Implementations are required to implement sequencing in a stack-safe
32+
* way, that is they either need to implement trampolining on their own
33+
* or implement `sequence` as a tail recursive function.
34+
*
35+
* Actually the type X can be different for every call to f...
36+
* It is a type aligned sequence, but for simplicity we do not enforce this
37+
* here.
38+
*/
39+
def sequence[X, R](init: M[X])(f: X => Either[M[X], M[R]]): M[R]
40+
41+
/**
42+
* Helper to summon and use an instance of CanReflect[M]
43+
*/
44+
def reflect[R](mr: M[R])(using r: CanReflect[M]): R = r.reflect(mr)
45+
46+
/**
47+
* Reify a computation into a monadic value
48+
*/
49+
def reify[R](prog: CanReflect[M] ?=> R): M[R] =
50+
boundary [M[R]]:
51+
given CanReflect[M] with
52+
def reflect[R2](mr: M[R2]): R2 =
53+
suspend [R2, M[R]] (k => mr.flatMap(k.resume))
54+
pure(prog)
55+
56+
end Monadic
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package runtime
2+
import scala.util.boundary, boundary.Label
3+
4+
/** A hypothetical API for suspensions. Not yet implemented.
5+
* Suspension contain a delimited contination, which can be
6+
* invoked with `resume`
7+
*/
8+
class Suspension[-T, +R]:
9+
def resume(arg: T): R = ???
10+
11+
def suspend[T, R](body: Suspension[T, R] => R)(using Label[R]): T = ???
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package simpleFutures
2+
3+
import scala.collection.mutable.ListBuffer
4+
import scala.util.boundary, boundary.Label
5+
import runtime.suspend
6+
7+
object Scheduler:
8+
def schedule(task: Runnable): Unit = ???
9+
10+
trait Async:
11+
def await[T](f: Future[T]): T
12+
13+
class Future[+T](body: Async ?=> T):
14+
private var result: Option[T] = None
15+
private var waiting: ListBuffer[T => Unit] = ListBuffer()
16+
private def addWaiting(k: T => Unit): Unit = waiting += k
17+
18+
def await(using a: Async): T = a.await(this)
19+
20+
private def complete(): Unit =
21+
Future.async:
22+
val value = body
23+
val result = Some(value)
24+
for k <- waiting do
25+
Scheduler.schedule(() => k(value))
26+
waiting.clear()
27+
28+
Scheduler.schedule(() => complete())
29+
30+
object Future:
31+
32+
// a handler for Async
33+
def async(body: Async ?=> Unit): Unit =
34+
boundary [Unit]:
35+
given Async with
36+
def await[T](f: Future[T]): T = f.result match
37+
case Some(x) => x
38+
case None => suspend[T, Unit](s => f.addWaiting(s.resume))
39+
body
40+
41+
end Future
42+
43+
def Test(x: Future[Int], xs: List[Future[Int]]) =
44+
Future:
45+
x.await + xs.map(_.await).sum
46+
47+
48+
49+
50+
51+
52+
53+

0 commit comments

Comments
 (0)