|
| 1 | +import language.experimental.captureChecking |
| 2 | + |
| 3 | +import gears.async.AsyncOperations.* |
| 4 | +import gears.async.default.given |
| 5 | +import gears.async.{Async, AsyncSupport, Future, uninterruptible} |
| 6 | + |
| 7 | +import java.util.concurrent.CancellationException |
| 8 | +import scala.annotation.capability |
| 9 | +import scala.concurrent.duration.{Duration, DurationInt} |
| 10 | +import scala.util.Success |
| 11 | +import scala.util.boundary |
| 12 | + |
| 13 | +type Result[+T, +E] = Either[E, T] |
| 14 | +object Result: |
| 15 | + @capability opaque type Label[-T, -E] = boundary.Label[Result[T, E]] |
| 16 | + // ^ doesn't work? |
| 17 | + |
| 18 | + def apply[T, E](body: Label[T, E] ?=> T): Result[T, E] = |
| 19 | + boundary(Right(body)) |
| 20 | + |
| 21 | + extension [U, E](r: Result[U, E]^)(using Label[Nothing, E]^) |
| 22 | + def ok: U = r match |
| 23 | + case Left(value) => boundary.break(Left(value)) |
| 24 | + case Right(value) => value |
| 25 | + |
| 26 | +class CaptureCheckingBehavior extends munit.FunSuite: |
| 27 | + import Result.* |
| 28 | + |
| 29 | + test("good") { |
| 30 | + // don't do this in real code! capturing Async.blocking's Async context across functions is hard to track |
| 31 | + Async.blocking: async ?=> |
| 32 | + def good1[T, E](frs: List[Future[Result[T, E]]^]): Future[Result[List[T], E]]^{async} = |
| 33 | + Future: |
| 34 | + Result: |
| 35 | + frs.map(_.await.ok) |
| 36 | + |
| 37 | + def good2[T, E](rf: Result[Future[T]^, E]): Future[Result[T, E]]^{async} = |
| 38 | + Future: |
| 39 | + Result: |
| 40 | + rf.ok.await // OK, Future argument has type Result[T] |
| 41 | + |
| 42 | + def useless4[T, E](fr: Future[Result[T, E]]^) = |
| 43 | + fr.await.map(Future(_)) |
| 44 | + } |
| 45 | + |
| 46 | + test("very bad") { |
| 47 | + Async.blocking: async ?=> |
| 48 | + def fail3[T, E](fr: Future[Result[T, E]]^) = |
| 49 | + Result: label ?=> |
| 50 | + Future: fut ?=> |
| 51 | + fr.await.ok // error, escaping label from Result |
| 52 | + |
| 53 | + val fut = Future(Left(5)) |
| 54 | + val res = fail3(fut) |
| 55 | + println(res.right.get.asInstanceOf[Future[Any]].awaitResult) |
| 56 | + } |
| 57 | + |
| 58 | + // test("bad") { |
| 59 | + // Async.blocking: async ?=> |
| 60 | + // def fail3[T, E](fr: Future[Result[T, E]]^): Result[Future[T]^{async}, E] = |
| 61 | + // Result: label ?=> |
| 62 | + // Future: fut ?=> |
| 63 | + // fr.await.ok // error, escaping label from Result |
| 64 | + // } |
0 commit comments