Created
October 29, 2021 16:10
-
-
Save nomisRev/d12beb058acc4db5f86995024836b1c5 to your computer and use it in GitHub Desktop.
Cont<R, A> implementation in Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import arrow.core.identity | |
import kotlin.coroutines.Continuation | |
import kotlin.coroutines.resume | |
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn | |
import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn | |
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED | |
interface ContEffect<R, A> { | |
suspend fun <B> shift(r: R): B | |
suspend fun <B> Cont<R, B>.bind(): B = | |
fold(this@ContEffect::shift, ::identity) | |
} | |
/** | |
* A Continuation of type [R] and [A] will | |
* - "Successfully" complete with a value of [A]. | |
* - Short-circuit with a value of [R]. | |
*/ | |
interface Cont<R, A> { | |
suspend fun <B> fold(f: suspend (R) -> B, g: suspend (A) -> B): B | |
} | |
fun <R, A> cont(f: suspend ContEffect<R, A>.() -> A): Cont<R, A> = | |
object : Cont<R, A> { | |
override suspend fun <B> fold(f: suspend (R) -> B, g: suspend (A) -> B): B = | |
suspendCoroutineUninterceptedOrReturn { cont -> | |
val effect = object : ContEffect<R, A> { | |
// Shift away from this Continuation by intercepting it, and completing it. | |
// The Continuation we grab here, is the same one from line 25 which is constructed on line 40 | |
// So this is a single continuation, completing it with it's final value means the remainder of the | |
// continuation program is short-circuited | |
override suspend fun <B> shift(r: R): B { | |
val b = f(r) | |
return suspendCoroutineUninterceptedOrReturn { _ -> | |
cont.resume(b) | |
COROUTINE_SUSPENDED | |
} | |
} | |
} | |
suspend { g(f(effect)) } | |
.startCoroutineUninterceptedOrReturn(Continuation(cont.context, cont::resumeWith)) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import arrow.core.Either | |
import arrow.core.identity | |
interface EitherEffect<E> : ContEffect<Either.Left<E>, Any?> { | |
suspend fun <A> Either<E, A>.bind(): A = | |
when (this) { | |
is Either.Left -> shift(this) | |
is Either.Right -> value | |
} | |
} | |
suspend fun <E, A> either(f: suspend EitherEffect<E>.() -> A): Either<E, A> = | |
cont<Either.Left<E>, A> { | |
val effect = EitherEffect(this@cont) | |
f(effect) | |
}.fold(::identity) { Either.Right(it) } | |
@Suppress("UNCHECKED_CAST") | |
fun <E, A> EitherEffect(cont: ContEffect<Either.Left<E>, A>): EitherEffect<E> = | |
object : EitherEffect<E>, | |
ContSyntax<Either.Left<E>, Any?> by cont as ContEffect<Either.Left<E>, Any?> { | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import arrow.core.Either | |
import arrow.core.identity | |
suspend fun main() { | |
// A Cont<R, A> that either results in `Int` or shifts (short-circuits) with String. | |
val cont: Cont<String, Int> = cont { | |
val x: Long = shift("Hello World!") | |
10 | |
} | |
// Run Cont<String, Int> to String by mapping the `Int` case to `String`. | |
val x: String = cont.fold(::identity, Int::toString) | |
println(x) | |
// Either Monad program inside Cont | |
val eitherCont = cont<Either<String, Nothing>, Int> { | |
val double: Double = shift(Either.Left("test")) | |
1 | |
} | |
// Converge Cont<Either<String, Nothing>, Int> to Either<String, Int> | |
val res: Either<String, Int> = eitherCont.fold(::identity) { Either.Right(it) } | |
println(res) | |
// Consume both values of `Cont<Either<String, Nothing>` into `Unit` | |
val unit = eitherCont.fold(::println, ::println) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment