#abstraction #computation #join #abort #branch #repeat #context

transaction

transaction abstraction library (a.k.a. transaction monad)

2 unstable releases

Uses old Rust 2015

0.2.1 Jun 23, 2017
0.2.0 Jun 21, 2017
0.1.0 Jun 6, 2017

#1949 in Rust patterns


Used in 2 crates

MIT license

36KB
1K SLoC

Transaction

An zero cost transaction abstraction library. This crate provide the ways to abstract and compinate transactions. Combinated comptations are run under a transaction. Not only it can be composed run under a transaction, it also requires computations are composed and run under a transaction.

To run the transactions, use crates like transaction-stm or transaction-diesel


lib.rs:

Zero-cost transaction abstraction in Rust

This crate abstracts over transactions like STM, SQL transactions and so on. It is composable via combinators and does DI of transactions.

The basic idea is representing contracts of "this computation must be run" as types. The trait Transaction represents o sequence of computation that must be run under a transaction. And transactions are composable (sequencable) using then, and_then, or_else, hence you can use it like values wrapped in Result.Since it represents computation to be run in data, some types respond to control operators are provided: abort for ?, repeat for for, loop_fn for loop and branch for (join point of) if and so on. As all the combinators have its own result type, no dispatches are done at execution time thus it is zero-cost.

Another feature is it does DI of transaction. For database transaction, it means it injects DB connection from the context.

Examples


extern crate transaction;

use self::transaction::prelude::*;


// Since current rust doesn't support `impl Trait`, you need to make a
// trait box
// to return a trait value from a function.
type BoxTx<'a, T> = Box<Transaction<
                          Ctx = FooConnection,
                          Item = T,
                          Err =FooError>
                        + 'a>;

fn find_user<'a>(id: i64) -> BoxTx<'a, Option<User>> {
    // connection is inejected from the context
    with_ctx(move |cn: &mut FooConnection| {
        // ..
        # let _ = (id, cn);
        # unimplemented!()
    }).boxed()

}

fn update_user<'a>(id: i64, name: &'a str) -> BoxTx<'a, Option<()>> {
    with_ctx(move |cn: &mut FooConnection| {
        // ..
        # let _ = (id, cn, name);
        # unimplemented!()
    }).boxed()
}

fn update_find_user<'a>(id: i64, name: &'a str) -> BoxTx<'a, Option<User>> {
    update_user(id, name)
        // transaction can be composed using `and_then`
        .and_then(move |ret| match ret {
            None =>
                // to return a leaf transaction, use `ok`, `err` or `result`
                ok(None)
                // to return from a branch (or, to match types at join
                // point), use `branch` API
                .branch()
                // use `first` in the first arm of the brnach
                .first(),
            Some(()) => find_user(id)
                .branch()
                // use `second` in the second arm of the brnach
                .second(),
        })
        // finally, box it to return `BoxTx`.
        .boxed()
}

Dependencies