-
Notifications
You must be signed in to change notification settings - Fork 31
Description
Could we add a Future constructor in the gears library that allows computations to be defined without starting them immediately?
This would enable defining a DAG of inter-dependent asynchronous tasks in an arbitrary order. For example, in pseudo-code:
t0 = t1.result + t2.result
t1 = t3.result + 4
t2 = t3.result + 2
t3 = 1The key requirement here is that tasks should not execute until explicitly triggered, allowing dependencies to be set up first.
Below is a working user-land implementation:
// async-lazy.scala
//> using dep "ch.epfl.lamp::gears::0.2.0"
import gears.async.{Future, Async}
import gears.async.default.given
import Async.await
final class AsyncLazy[T](body: (Async, Async.Spawn) ?=> T):
private var _future: Future[T] | Null = null
def get()(using Async, Async.Spawn): T =
if _future == null then
this.synchronized:
if _future == null then
_future = Future(body)
_future.nn.await
@main def main =
println("starting")
val a = new Array(4)
a(0) = AsyncLazy:
println("compute a(0)")
a(1).get() + a(2).get()
a(1) = AsyncLazy:
println("compute a(1)")
a(3).get() + 4
a(2) = AsyncLazy:
println("compute a(2)")
a(3).get() + 2
a(3) = AsyncLazy:
println("compute a(3)")
1
Async.blocking:
println(a(0).get())Running this code produces the following output:
$ scala -S 3.5.1 --server=false async-lazy.scala
starting
compute a(0)
compute a(1)
compute a(3)
compute a(2)
8
I believe a dedicated feature in gears could achieve something similar more efficiently, by saving the extra synchronization.
A Future constructor could return a deferred-start Future, of which the execution begins only when explicitly triggered. In the example above, one could then loop over Futures to start them after they have all been defined.