Skip to content

Commit

Permalink
Remove fun interface utilities and use delegation instead
Browse files Browse the repository at this point in the history
It was initially an interesting way to implement the horizontal composition from b-studios's thesis, but delegation seems like a more robust solution
  • Loading branch information
kyay10 committed Oct 12, 2024
1 parent 486d0ef commit 3bae940
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 69 deletions.
13 changes: 0 additions & 13 deletions library/src/commonMain/kotlin/effekt/handler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,13 @@ public fun <E> Handler<E>.discardWith(value: Result<E>): Nothing = prompt().prom

public suspend fun <E> Handler<E>.discardWithFast(value: Result<E>): Nothing = prompt().prompt.abortWithFast(deleteDelimiter = true, value)

public suspend fun <E, H> handle(
handler: ((() -> HandlerPrompt<E>) -> H), body: suspend H.() -> E
): E = handle { handler { this }.body() }

public suspend fun <E> handle(body: suspend HandlerPrompt<E>.() -> E): E = with(HandlerPrompt<E>()) {
rehandle { body() }
}

// TODO maybe we should remove this? Effekt gets by without it (but their lambdas are restricted)
public suspend fun <E> Handler<E>.rehandle(body: suspend () -> E): E = prompt().prompt.reset(body)

public suspend fun <E, H : StatefulHandler<E, S>, S> handleStateful(
handler: ((() -> StatefulPrompt<E, S>) -> H), value: S, fork: S.() -> S,
body: suspend H.() -> E
): E {
val p = StatefulPrompt<E, S>()
val h = handler { p }
return h.rehandleStateful(value, fork) { h.body() }
}

public suspend fun <E, S> handleStateful(
value: S, fork: S.() -> S, body: suspend StatefulPrompt<E, S>.() -> E
): E = with(StatefulPrompt<E, S>()) {
Expand Down
5 changes: 0 additions & 5 deletions library/src/commonMain/kotlin/effekt/state.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ public interface Stateful<S : Stateful<S>> {
public fun fork(): S
}

public suspend fun <E, H : StatefulHandler<E, S>, S : Stateful<S>> handleStateful(
handler: ((() -> StatefulPrompt<E, S>) -> H), value: S,
body: suspend H.() -> E
): E = handleStateful(handler, value, Stateful<S>::fork, body)

public suspend fun <E, S : Stateful<S>> handleStateful(
value: S, body: suspend StatefulPrompt<E, S>.() -> E
): E = handleStateful(value, Stateful<S>::fork, body)
Expand Down
6 changes: 3 additions & 3 deletions library/src/commonTest/kotlin/effekt/AmbTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,14 @@ interface Amb {
suspend fun flip(): Boolean
}

fun interface AmbList<E> : Handler<List<E>>, Amb {
class AmbList<E>(p: HandlerPrompt<List<E>>) : Handler<List<E>> by p, Amb {
override suspend fun flip(): Boolean = use { resume ->
val ts = resume(true)
val fs = resume(false)
ts + fs
}
}

suspend fun <E> ambList(block: suspend Amb.() -> E): List<E> = handle(::AmbList) {
listOf(block())
suspend fun <E> ambList(block: suspend Amb.() -> E): List<E> = handle {
listOf(block(AmbList(this)))
}
6 changes: 3 additions & 3 deletions library/src/commonTest/kotlin/effekt/Exc.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ interface Exc {

suspend fun Exc.raise(): Nothing = raise("")

fun interface Maybe<R> : Exc, Handler<Option<R>> {
class Maybe<R>(p: HandlerPrompt<Option<R>>) : Exc, Handler<Option<R>> by p {
override suspend fun raise(msg: String): Nothing = discard { None }
}

suspend fun <R> maybe(block: suspend Exc.() -> R): Option<R> = handle(::Maybe) {
Some(block())
suspend fun <R> maybe(block: suspend Exc.() -> R): Option<R> = handle {
Some(block(Maybe(this)))
}
14 changes: 5 additions & 9 deletions library/src/commonTest/kotlin/effekt/PaperTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,16 @@ private suspend fun drunkFlip(amb: Amb, exc: Exc): String {
return if (heads) "Heads" else "Tails"
}

fun interface Collect<R> : Amb, Handler<List<R>> {
class Collect<R>(p: HandlerPrompt<List<R>>) : Amb, Handler<List<R>> by p {
override suspend fun flip(): Boolean = use { resume ->
val ts = resume(true)
val fs = resume(false)
ts + fs
}
}

suspend fun <R> collect(block: suspend Amb.() -> R): List<R> = handle(::Collect) {
listOf(block())
suspend fun <R> collect(block: suspend Amb.() -> R): List<R> = handle {
listOf(block(Collect(this)))
}

interface Fiber {
Expand Down Expand Up @@ -188,12 +188,8 @@ suspend inline fun poll(state: StateScope, fiber: Fiber, block: suspend Async.()
override val fiber = fiber
})

interface NonDetermined : Amb, Exc

fun interface FirstResult<R> : NonDetermined, Maybe<R>, Backtrack<R>

suspend fun <R> firstResult(block: suspend NonDetermined.() -> R): Option<R> = handle(::FirstResult) {
Some(block())
suspend fun <R> firstResult(block: suspend AmbExc.() -> R): Option<R> = handle {
Some(block(Backtrack(this)))
}

class Scheduler(prompt: StatefulPrompt<Unit, Queue>) : Fiber, StatefulHandler<Unit, Queue> by prompt {
Expand Down
8 changes: 4 additions & 4 deletions library/src/commonTest/kotlin/effekt/SelectTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,15 @@ interface Select {
suspend fun <A> Iterable<A>.select(): A
}

fun interface SelectAll<R> : Select, Handler<List<R>> {
class SelectAll<R>(p: HandlerPrompt<List<R>>) : Select, Handler<List<R>> by p{
override suspend fun <A> Iterable<A>.select(): A = use { k ->
fold(emptyList()) { acc, elem -> acc + k(elem) }
}
}

suspend fun <R> selectAll(body: suspend Select.() -> R): List<R> = handle(::SelectAll) { listOf(body()) }
suspend fun <R> selectAll(body: suspend Select.() -> R): List<R> = handle { listOf(body(SelectAll(this))) }

fun interface SelectFirst<R> : Select, Handler<Option<R>> {
class SelectFirst<R>(p: HandlerPrompt<Option<R>>) : Select, Handler<Option<R>> by p {
override suspend fun <A> Iterable<A>.select(): A = use { k ->
forEach {
val res = k(it)
Expand All @@ -82,4 +82,4 @@ fun interface SelectFirst<R> : Select, Handler<Option<R>> {
}
}

suspend fun <R> selectFirst(body: suspend Select.() -> R): Option<R> = handle(::SelectFirst) { Some(body()) }
suspend fun <R> selectFirst(body: suspend Select.() -> R): Option<R> = handle { Some(body(SelectFirst(this))) }
6 changes: 3 additions & 3 deletions library/src/commonTest/kotlin/effekt/StateTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ suspend fun <R, S> specialState(init: S, block: suspend State<S>.() -> R): R =
block(SpecialState(this))
}

fun interface StateFun<R, S> : State<S>, Handler<suspend (S) -> R> {
class StateFun<R, S>(p: HandlerPrompt<suspend (S) -> R>) : State<S>, Handler<suspend (S) -> R> by p {
override suspend fun get(): S = useOnce { k ->
{ s ->
k(s, shouldClear = true)(s)
Expand All @@ -198,8 +198,8 @@ fun interface StateFun<R, S> : State<S>, Handler<suspend (S) -> R> {
}
}

suspend fun <R, S> stateFun(init: S, block: suspend State<S>.() -> R): R = handle<suspend (S) -> R, _>(::StateFun) {
val res = block()
suspend fun <R, S> stateFun(init: S, block: suspend State<S>.() -> R): R = handle {
val res = block(StateFun(this))
suspendOneArg { res }
}(init)

Expand Down
16 changes: 6 additions & 10 deletions library/src/commonTest/kotlin/effekt/UseCasesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,28 +62,24 @@ suspend fun <R> Exc.stringReader(input: String, block: suspend Receive<Char>.()

interface AmbExc : Amb, Exc

fun interface NonDet<E> : AmbList<E>, AmbExc {
class NonDet<E>(p: HandlerPrompt<List<E>>) : Amb by AmbList<E>(p), AmbExc, Handler<List<E>> by p {
override suspend fun raise(msg: String): Nothing = discard { emptyList() }
}

suspend fun <E> nonDet(block: suspend AmbExc.() -> E): List<E> = handle(::NonDet) {
listOf(block())
suspend fun <E> nonDet(block: suspend AmbExc.() -> E): List<E> = handle {
listOf(block(NonDet(this)))
}

fun interface Backtrack<R> : Maybe<R>, AmbExc {
class Backtrack<R>(p: HandlerPrompt<Option<R>>) : Exc by Maybe(p), Handler<Option<R>> by p, AmbExc {
override suspend fun flip(): Boolean = use { resume ->
resume(true).recover { resume(false).bind() }
}
}

suspend fun <R> backtrack(block: suspend AmbExc.() -> R): Option<R> = handle(::Backtrack) {
Some(block())
suspend fun <R> backtrack(block: suspend AmbExc.() -> R): Option<R> = handle {
Some(block(Backtrack(this)))
}

interface Receive<A> {
suspend fun receive(): A
}

interface Send<A> {
suspend fun send(a: A)
}
30 changes: 11 additions & 19 deletions library/src/jvmTest/kotlin/effekt/HandlerJvmTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -120,22 +120,22 @@ suspend fun <R> stringInput(input: String, block: suspend StringInput<R>.() -> R
})
}

fun interface Nondet2<R> : Collect<R>, NonDetermined {
class Nondet2<R>(p: HandlerPrompt<List<R>>) : Amb by Collect(p), AmbExc, Handler<List<R>> by p {
override suspend fun raise(msg: String): Nothing = discard { emptyList() }
}

suspend fun <R> nondet(block: suspend NonDetermined.() -> R): List<R> = handle(::Nondet2) {
listOf(block())
suspend fun <R> nondet(block: suspend AmbExc.() -> R): List<R> = handle {
listOf(block(Nondet2(this)))
}

fun interface Backtrack2<R> : Maybe<R>, NonDetermined {
class Backtrack2<R>(p: HandlerPrompt<Option<R>>) : Exc by Maybe(p), AmbExc, Handler<Option<R>> by p {
override suspend fun flip(): Boolean = use { resume ->
resume(true).recover { resume(false).bind() }
}
}

suspend fun <R> backtrack2(block: suspend NonDetermined.() -> R): Option<R> = handle(::Backtrack2) {
Some(block())
suspend fun <R> backtrack2(block: suspend AmbExc.() -> R): Option<R> = handle {
Some(block(Backtrack2(this)))
}

interface Generator2<A> {
Expand All @@ -146,8 +146,7 @@ private suspend fun Generator2<Int>.numbers(upto: Int) {
for (i in 0..upto) yield(i)
}

interface Iterate2<A> : Generator2<A>, Handler<EffectfulIterator2<A>> {
val nextValue: StateScope.Field<(suspend (Unit) -> A)?>
class Iterate2<A>(val nextValue: StateScope.Field<(suspend (Unit) -> A)?>, p: HandlerPrompt<EffectfulIterator2<A>>) : Generator2<A>, Handler<EffectfulIterator2<A>> by p {
override suspend fun yield(value: A) = use { resume ->
nextValue.set {
resume(Unit)
Expand All @@ -163,16 +162,9 @@ interface Iterate2<A> : Generator2<A>, Handler<EffectfulIterator2<A>> {
}
}

suspend fun <A> StateScope.iterate2(block: suspend Generator2<A>.() -> Unit): EffectfulIterator2<A> {
val nextValue = field<(suspend (Unit) -> A)?>(null)
return handle({
object : Iterate2<A>, Handler<EffectfulIterator2<A>> by it() {
override val nextValue = nextValue
}
}) {
block()
EmptyEffectfulIterator
}
suspend fun <A> StateScope.iterate2(block: suspend Generator2<A>.() -> Unit) = handle {
block(Iterate2(field(null), this))
EmptyEffectfulIterator
}

private object EmptyEffectfulIterator : EffectfulIterator2<Nothing> {
Expand All @@ -185,4 +177,4 @@ interface EffectfulIterator2<out A> {
suspend operator fun next(): A
}

suspend operator fun <A> EffectfulIterator2<A>.iterator() = this
operator fun <A> EffectfulIterator2<A>.iterator() = this

0 comments on commit 3bae940

Please sign in to comment.