@@ -25,93 +25,90 @@ object Result:
2525 case Left (value) => boundary.break(Left (value))
2626 case Right (value) => value
2727
28- class CaptureCheckingBehavior extends munit.FunSuite :
29- import Result .*
30- import caps .use
31- import scala .collection .mutable
32-
33- test(" good" ) {
34- // don't do this in real code! capturing Async.blocking's Async context across functions is hard to track
35- Async .blocking: async ?=>
36- def good1 [T , E ](@ use frs : List [Future [Result [T , E ]]^ ]): Future [Result [List [T ], E ]]^ {frs* , async} =
28+ import Result .*
29+
30+ def good =
31+ // don't do this in real code! capturing Async.blocking's Async context across functions is hard to track
32+ Async .blocking: async ?=>
33+ def good1 [T , E ](@ caps.use frs : List [Future [Result [T , E ]]^ ]): Future [Result [List [T ], E ]]^ {frs* , async} =
34+ Future : fut ?=>
35+ Result : ret ?=>
36+ frs.map(_.await.ok)
37+
38+ def good2 [T , E ](@ caps.use rf : Result [Future [T ]^ , E ]): Future [Result [T , E ]]^ {rf* , async} =
39+ Future :
40+ Result :
41+ rf.ok.await // OK, Future argument has type Result[T]
42+
43+ def useless4 [T , E ](fr : Future [Result [T , E ]]^ ) =
44+ fr.await.map(Future (_))
45+
46+
47+ def `bad - collectors` =
48+ val futs : Seq [Future [Int ]^ ] = Async .blocking: async ?=> // error
49+ val fs : Seq [Future [Int ]^ {async}] = (0 to 10 ).map(i => Future { i })
50+ fs
51+ Async .blocking:
52+ futs.awaitAll // error
53+
54+
55+ def `future withResolver capturing` = {
56+ class File () extends caps.Capability :
57+ def close () = ()
58+ def read (callback : Int => Unit ) = ()
59+ val f = File ()
60+ val read = Future .withResolver[Int , caps.CapSet ^ {f}]: r =>
61+ f.read(r.resolve)
62+ r.onCancel(f.close)
63+ }
64+
65+ def `awaitAll/awaitFirst` = {
66+ trait File extends caps.Capability :
67+ def readFut (): Future [Int ]^ {this }
68+ object File :
69+ def open [T ](filename : String )(body : File => T )(using Async ): T = ???
70+
71+ def readAll (@ caps.use files : (File ^ )* ) = files.map(_.readFut())
72+
73+ Async .blocking: // error
74+ File .open(" a.txt" ): a => // error
75+ File .open(" b.txt" ): b => // error
76+ val futs = readAll(a, b)
77+ val allFut = Future (futs.awaitAll)
78+ allFut
79+ // .await // uncomment to leak
80+ }
81+
82+ def `channel` = {
83+ trait File extends caps.Capability :
84+ def read (): Int = ???
85+ Async .blocking:
86+ val ch = SyncChannel [File ]() // error
87+ // Sender
88+ val sender = Future :
89+ val f = new File {}
90+ ch.send(f)
91+ val recv = Future :
92+ val f = ch.read().right.get
93+ f.read()
94+ }
95+
96+ def `upcasting to any` = {
97+ Async .blocking: async ?=>
98+ def fail3 [T , E ](fr : Future [Result [T , E ]]^ ): Result [Any , Any ] =
99+ Result : label ?=> // escaping label from Result, not yet err
37100 Future : fut ?=>
38- Result : ret ?=>
39- frs.map(_.await.ok)
40-
41- def good2 [T , E ](@ use rf : Result [Future [T ]^ , E ]): Future [Result [T , E ]]^ {rf* , async} =
42- Future :
43- Result :
44- rf.ok.await // OK, Future argument has type Result[T]
45-
46- def useless4 [T , E ](fr : Future [Result [T , E ]]^ ) =
47- fr.await.map(Future (_))
48- }
49-
50- // test("bad - collectors") {
51- // val futs: Seq[Future[Int]^] = Async.blocking: async ?=>
52- // val fs: Seq[Future[Int]^{async}] = (0 to 10).map(i => Future { i })
53- // fs
54- // Async.blocking:
55- // futs.awaitAll // should not compile
56- // }
57-
58- test(" future withResolver capturing" ) {
59- class File () extends caps.Capability :
60- def close () = ()
61- def read (callback : Int => Unit ) = ()
62- val f = File ()
63- val read = Future .withResolver[Int , caps.CapSet ^ {f}]: r =>
64- f.read(r.resolve)
65- r.onCancel(f.close)
66- }
67-
68- test(" awaitAll/awaitFirst" ) {
69- trait File extends caps.Capability :
70- def readFut (): Future [Int ]^ {this }
71- object File :
72- def open [T ](filename : String )(body : File => T )(using Async ): T = ???
73-
74- def readAll (@ caps.use files : (File ^ )* ) = files.map(_.readFut())
75-
76- Async .blocking:
77- File .open(" a.txt" ): a =>
78- File .open(" b.txt" ): b =>
79- val futs = readAll(a, b)
80- val allFut = Future (futs.awaitAll)
81- allFut
82- .await // uncomment to leak
83- }
84-
85- // test("channel") {
86- // trait File extends caps.Capability:
87- // def read(): Int = ???
88- // Async.blocking:
89- // val ch = SyncChannel[File]()
90- // // Sender
91- // val sender = Future:
92- // val f = new File {}
93- // ch.send(f)
94- // val recv = Future:
95- // val f = ch.read().right.get
96- // f.read()
97- // }
98-
99- test(" very bad" ) {
100- Async .blocking: async ?=>
101- def fail3 [T , E ](fr : Future [Result [T , E ]]^ ): Result [Any , Any ] =
102- Result : label ?=>
103- Future : fut ?=>
104- fr.await.ok // error, escaping label from Result
105-
106- // val fut = Future(Left(5))
107- // val res = fail3(fut)
108- // println(res.right.get.asInstanceOf[Future[Any]].awaitResult)
109- }
110-
111- // test("bad") {
112- // Async.blocking: async ?=>
113- // def fail3[T, E](fr: Future[Result[T, E]]^): Result[Future[T]^{async}, E] =
114- // Result: label ?=>
115- // Future: fut ?=>
116- // fr.await.ok // error, escaping label from Result
117- // }
101+ fr.await.ok
102+
103+ // val fut = Future(Left(5))
104+ // val res = fail3(fut)
105+ // println(res.right.get.asInstanceOf[Future[Any]].awaitResult)
106+ }
107+
108+ def `bad` = {
109+ Async .blocking: async ?=>
110+ def fail3 [T , E ](fr : Future [Result [T , E ]]^ ): Result [Future [T ]^ {async}, E ] =
111+ Result : label ?=> // error, escaping label from Result
112+ Future : fut ?=>
113+ fr.await.ok
114+ }
0 commit comments