Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions kyo-kernel/shared/src/main/scala/kyo/kernel/ArrowEffect.scala
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ object ArrowEffect:
handleLoopLoop(Loop.continue(kyo(v, context)), context)
end new
case kyo =>
kyo.asInstanceOf[A]
kyo.unsafeGet
case _ =>
v.asInstanceOf[A < (S & S2)]
end handleLoopLoop
Expand Down Expand Up @@ -536,7 +536,7 @@ object ArrowEffect:
handleLoopLoop(Loop.continue(state, kyo(v, context)), context)
end new
case kyo =>
done(state, kyo.asInstanceOf[A])
done(state, kyo.unsafeGet)
end match
case _ =>
v.asInstanceOf[B < (S & S2)] // Loop.done
Expand Down
165 changes: 165 additions & 0 deletions kyo-kernel/shared/src/test/scala/kyo/kernel/ArrowEffectTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,171 @@ class ArrowEffectTest extends Test:
}
}

"nested effects handling" - {

given [A, B]: CanEqual[A, B] = CanEqual.derived

sealed trait NestedTestEffect extends ArrowEffect[Const[Int], Const[Int]]

def suspendNested(i: Int): Int < NestedTestEffect =
ArrowEffect.suspend[Any](Tag[NestedTestEffect], i)

val nestedTag: Tag[NestedTestEffect] = Tag[NestedTestEffect]

def flatten[A, B, C](v: A < B < C): A < (B & C) = v.map(a => a)

"not handle Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < S =
ArrowEffect.handle(nestedTag, v):
[C] => (input, cont) => cont(input * 10)

"unwraps Nested and returns inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)
val result: Int < NestedTestEffect < Any = handle(nested)

assert(result == nested, "handleSimple should return the nested computation")

val flattened = flatten(result)
val finalResult = handle(flattened)

assert(finalResult.eval == 50)
}
}

"handleFirst on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < (S & NestedTestEffect) =
ArrowEffect.handleFirst(nestedTag, v)(
[C] => (input, cont) => cont(input * 10),
identity
)

"done callback receives unwrapped value" in {
val comp = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)

assert(result == nested, "handleFirst should return the nested computation")

val flattened = flatten(result)
val finalResult: Int < NestedTestEffect = handle(flattened)
assert(finalResult.evalNow == Maybe(50))
}
}

"handleLoop (stateless) on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < S =
ArrowEffect.handleLoop(Tag[NestedTestEffect], v):
[C] => (input, cont) => Loop.continue(cont(input * 10))

"unwraps Nested and handles inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)
assert(result == nested, "handleLoop should return the nested computation")

val flattened = flatten(result)
val finalResult: Int < Any = handle(flattened)

assert(finalResult.eval == 50)
}
}

"handleLoop (stateful) on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < S =
ArrowEffect.handleLoop(nestedTag, 0, v)(
[C] => (input, state, cont) => Loop.continue(state + 1, cont((input + state) * 10))
)

"unwraps Nested and handles inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)
assert(result == nested, "handleLoop should return the nested computation")

val flattened = flatten(result)
val finalResult: Int < Any = handle(flattened)

assert(finalResult.eval == 50)
}

}

"handleLoop (stateful + done) on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < S =
ArrowEffect.handleLoop(nestedTag, 0, v)(
[C] => (input, state, cont) => Loop.continue(state + 1, cont(input * 10)),
(state, v) => v
)

"unwraps Nested and handles inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)
assert(result == nested, "handleLoop should return the nested computation")

val flattened = flatten(result)
val finalResult: Int < Any = handle(flattened)

assert(finalResult.eval == 50)
}
}

"handleCatching on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < S =
ArrowEffect.handleCatching(nestedTag, v)(
[C] => (input, cont) => cont(input * 10),
recover = e => throw e
)

"unwraps Nested and handles inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)
assert(result == nested, "handleLoop should return the nested computation")

val flattened = flatten(result)
val finalResult: Int < Any = handle(flattened)

assert(finalResult.eval == 50)
}
}

"handlePartial on Nested" - {

def handle[A, S](v: A < (S & NestedTestEffect)): A < (S & NestedTestEffect) =
ArrowEffect.handlePartial(nestedTag, nestedTag, v, Context.empty)(
stop =
false,
[C] => (input, cont) => cont(input * 10),
[C] => (input, cont) => cont(input * 10)
)

"unwraps Nested and handles inner suspension" in {
val comp: Int < NestedTestEffect = suspendNested(5)
val nested: Int < NestedTestEffect < Any = Nested(comp)

val result = handle(nested)
assert(result == nested, "handlePartial should return the nested computation")

val flattened = flatten(result)
val finalResult = handle(flattened)
assert(finalResult.evalNow == Maybe(50))
}
}
}

"effects with variance" - {

"delimited continuation" - {
Expand Down