Skip to content

Commit e2b087c

Browse files
authored
Add the possibility to ok() a forked-either computation (#155)
1 parent 04f8a69 commit e2b087c

File tree

5 files changed

+45
-5
lines changed

5 files changed

+45
-5
lines changed

core/src/main/scala/ox/either.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ object either:
7070
case _ => error("`.ok()` can only be used within an `either` call.\nIs it present?")
7171
}
7272

73+
extension [E, A](inline f: Fork[Either[E, A]])
74+
/** Join the fork and unwrap the value of its `Either` result, short-circuiting the computation to the enclosing [[either]], in case
75+
* this is a left-value.
76+
*
77+
* If the fork fails with an exception, the enclosing scope ends (when it's [[supervised]]), or the exception is re-thrown by this
78+
* method (when the enclosing scope is [[unsupervised]]).
79+
*/
80+
transparent inline def ok(): A = f.join().ok()
81+
7382
extension [E](e: E)
7483
transparent inline def fail(): Nothing =
7584
summonFrom {

core/src/main/scala/ox/fork.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,15 @@ private[ox] inline def unwrapExecutionException[T](f: => T): T =
193193
*/
194194
trait Fork[T]:
195195
/** Blocks until the fork completes with a result. Throws an exception, if the fork completed with an exception, and is unsupervised.
196+
*
197+
* @see
198+
* If `T` is an `Either`, and there's an enclosing [[either]] block, the result can be unwrapped using [[either.ok]].
196199
*/
197200
def join(): T
198201

199-
/** Blocks until the fork completes with a result. */
202+
/** Blocks until the fork completes with a result. Only makes sens in unsupervised scoped: otherwise a thrown exception causes the scope
203+
* to end, and is re-thrown by the [[supervised]] block.
204+
*/
200205
def joinEither(): Either[Throwable, T] =
201206
try Right(join())
202207
catch

core/src/test/scala/ox/EitherTest.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,17 @@ class EitherTest extends AnyFlatSpec with Matchers:
5959
r3 shouldBe Left(())
6060
}
6161

62+
it should "work correctly when invoked on fork" in {
63+
supervised:
64+
val f =
65+
fork:
66+
either:
67+
ok1.ok()
68+
69+
either:
70+
f.ok() shouldBe "x"
71+
}
72+
6273
it should "report a proper compilation error when used outside of either:" in {
6374
val e = intercept[TestFailedException](assertCompiles("ok1.ok()"))
6475

core/src/test/scala/ox/ForkTest.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import ox.util.Trail
77

88
import scala.concurrent.duration.*
99

10-
class ForkTest extends AnyFlatSpec with Matchers {
10+
class ForkTest extends AnyFlatSpec with Matchers:
1111
"fork" should "run two forks concurrently" in {
1212
val trail = Trail()
1313
unsupervised {
@@ -73,4 +73,4 @@ class ForkTest extends AnyFlatSpec with Matchers {
7373

7474
trail.get shouldBe Vector("main mid", "f1 complete", "result = 5", "f2 interrupted")
7575
}
76-
}
76+

doc/basics/error-handling.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ val result: Either[Int | Long, String] = either:
8787
v1.ok() ++ v2.ok()
8888
```
8989

90-
Finally, options can be unwrapped as well; the error type is then `Unit`:
90+
Options can be unwrapped as well; the error type is then `Unit`:
9191

9292
```scala mdoc:compile-only
9393
import ox.either
@@ -100,6 +100,21 @@ val result: Either[Unit, String] = either:
100100
v1.ok() * v2.ok()
101101
```
102102

103+
Finally, a forked computation, resulting in an `Either`, can be joined & unwrapped using a single `ok()` invocation:
104+
105+
```scala mdoc:compile-only
106+
import ox.{either, fork, Fork, supervised}
107+
import ox.either.ok
108+
109+
val v1: Either[Int, String] = ???
110+
111+
supervised:
112+
val forkedResult: Fork[Either[Int, String]] = fork(either(v1.ok()))
113+
114+
val result: Either[Int, String] = either:
115+
forkedResult.ok()
116+
```
117+
103118
Failures can be reported using `.fail()`. For example (although a pattern match would be better in such a simple case):
104119

105120
```scala mdoc:compile-only
@@ -173,4 +188,4 @@ val outerResult: Either[Exception, Unit] = either:
173188
()
174189
```
175190

176-
After this change refactoring `returnsEither` to return `Either[Exception, Int]` would yield a compile error on `returnsEither.ok()`.
191+
After this change refactoring `returnsEither` to return `Either[Exception, Int]` would yield a compile error on `returnsEither.ok()`.

0 commit comments

Comments
 (0)