Last active
August 10, 2022 08:41
-
-
Save nomisRev/b6aced8ce552ae718791e187ebd6cdd4 to your computer and use it in GitHub Desktop.
Either-Syntax for SqlDelight transactions
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.continuations.Effect | |
import arrow.continuations.generic.DelimitedScope | |
import arrow.core.Either | |
import arrow.core.right | |
import com.squareup.sqldelight.TransactionWithReturn | |
import kotlin.coroutines.RestrictsSuspension | |
/** | |
* Either-Syntax for SqlDelight transactions. | |
* | |
* Upon encountering a [Either.Left] value it requests a rollback and returns the encountered [Either.Left] value. | |
* Otherwise it returns the returned value wrapped in [Either.Right]. | |
* | |
* ```kotlin | |
* object Error | |
* | |
* fun operations(): List<Either<Error, Int>> = | |
* listOf(Either.Right("test-1"), Either.Left(Error), Either.Right("test-3")) | |
* | |
* fun saveOperations(db: Database, operations: List<Either<Error, String>>): Either<Error, Int> = | |
* db.transactionEither { | |
* operations.map { throwableOrString -> | |
* val string = throwableOrString.bind() | |
* db.table.insertString(string) | |
* db.table.selectIdForString(string) | |
* } | |
* } | |
* ``` | |
* | |
* The above snippet will first insert `test-1` into `table` since `bind` can unwrap it from `Either.Right`, | |
* when it encounters `Left(Error)` it will rollback the transaction, thus the previous insert, and return `Left(Error)` from `transactionEither`. | |
* | |
* If `operations` was `listOf(Either.Right("test-1"), Either.Right("test-2"), Either.Right("test-3"))` | |
* it would insert all 3 values into the `table` and select their ids return `Right(listOf(1, 2, 3))`. | |
*/ | |
fun <E, A> Database.transactionEither(f: suspend RestrictedEitherEffect<E, *>.() -> A): Either<E, A> = | |
transactionWithResult { | |
when (val res = | |
Effect.restricted<RestrictedEitherEffect<E, A>, Either<E, A>, A>(eff = { RestrictedEitherEffect(this, it) }, | |
f = f, | |
just = { it.right() })) { | |
is Either.Left -> rollback(res) | |
is Either.Right -> res | |
} | |
} | |
@RestrictsSuspension | |
class RestrictedEitherEffect<E, A>( | |
db: TransactionWithReturn<Either<E, A>>, | |
private val scope: DelimitedScope<Either<E, A>> | |
) : Effect<Either<E, A>>, TransactionWithReturn<Either<E, A>> by db { | |
override fun control(): DelimitedScope<Either<E, A>> = scope | |
suspend fun <B> Either<E, B>.bind(): B = | |
when (this) { | |
is Either.Right -> value | |
is Either.Left -> control().shift(this@bind) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment