Skip to content

Instantly share code, notes, and snippets.

@7h3kk1d
Created October 26, 2022 14:28
Show Gist options
  • Save 7h3kk1d/eeeb1f2e6ee768971c47e2d63f1c688c to your computer and use it in GitHub Desktop.
Save 7h3kk1d/eeeb1f2e6ee768971c47e2d63f1c688c to your computer and use it in GitHub Desktop.
Transactional Example
public final class Transactional<M extends MonadRec<?, M>, Cancel, Context, A> implements MonadT<M, A, Transactional<M, Cancel, Context, ?>, Transactional<?, Cancel, Context, ?>> {
private final Pure<M> pure;
private final ReaderT<Context, M, Either<Cancel, A>> run;
public Transactional(Pure<M> pure, ReaderT<Context, M, Either<Cancel, A>> run) {
this.pure = pure;
this.run = run;
}
@Override
public <B> Transactional<M, Cancel, Context, B> trampolineM(Fn1<? super A, ? extends MonadRec<RecursiveResult<A, B>, Transactional<M, Cancel, Context, ?>>> fn) {
return new Transactional<>(pure, eitherT(run)
.trampolineM(a -> {
Transactional<M, Cancel, Context, RecursiveResult<A, B>> coerce = fn.apply(a)
.coerce();
return eitherT(coerce.run);
}).runEitherT());
}
@Override
public <B> Transactional<M, Cancel, Context, B> flatMap(Fn1<? super A, ? extends Monad<B, Transactional<M, Cancel, Context, ?>>> f) {
EitherT<ReaderT<Context, M, ?>, Cancel, B> readerTCancelBEitherT = eitherT(run).flatMap(a -> {
Transactional<M, Cancel, Context, B> apply = f.apply(a).coerce();
ReaderT<Context, M, Either<Cancel, B>> run1 = apply.run;
return eitherT(run1);
});
return new Transactional<>(pure, readerTCancelBEitherT.runEitherT());
}
@Override
public <B> Transactional<M, Cancel, Context, B> pure(B b) {
Pure<ReaderT<Context, M, ?>> readerTPure = ReaderT.pureReaderT(pure);
ReaderT<Context, M, Either<Cancel, B>> apply = readerTPure.apply(right(b));
return new Transactional<>(pure, apply);
}
@Override
public <B, N extends MonadRec<?, N>> Transactional<N, Cancel, Context, B> lift(MonadRec<B, N> mb) {
Lift<ReaderT<Context, ?, ?>> readerTLift = ReaderT.liftReaderT();
return new Transactional<>(mb::pure, readerTLift.<B, N, ReaderT<Context, N, B>>apply(mb)
.fmap(Either::right));
}
public MonadRec<Either<Cancel, A>, M> run(Context context) {
return run.runReaderT(context);
}
}
public class TransactionExample {
public static void main(String[] args) {
Transactor<IO<?>, Unit, String> transactor = new Transactor<>() {
@Override
public <A> IO<Either<String, A>> runTransactional(Transactional<IO<?>, String, Unit, A> transactional) {
Unit connection = UNIT;
return IO.io(() -> System.out.println("Starting Transaction"))
.discardL(transactional.run(connection)
.<IO<Either<String, A>>>coerce()
.flatMap(e -> e.match(s -> IO.io(() -> System.err.printf("Rolling back transaction due to %s%n", s)),
__ -> IO.io(() -> System.out.println("Committing Transaction")))
.fmap(constantly(e))));
}
};
Transactional<IO<?>, String, Unit, Integer> fetchOddNumber = new Transactional<>(pureIO(), readerT(context -> IO.io(() -> System.out.println("Fetching data"))
.fmap(constantly(7))
.fmap(Either::right)));
Transactional<IO<?>, String, Unit, Integer> fetchEvenNumber = new Transactional<>(pureIO(), readerT(context -> IO.io(() -> System.out.println("Fetching data"))
.fmap(constantly(8))
.fmap(Either::right)));
transactor.runTransactional(fetchOddNumber.flatMap(rollBackWhenOdd()))
.<IO<Either<String, Unit>>>coerce().unsafePerformIO();
transactor.runTransactional(fetchEvenNumber.flatMap(rollBackWhenOdd()))
.<IO<Either<String, Unit>>>coerce().unsafePerformIO();
}
private static Fn1<Integer, Monad<Unit, Transactional<IO<?>, String, Unit, ?>>> rollBackWhenOdd() {
return integer -> {
if (integer % 2 == 0) {
return new Transactional<>(pureIO(), readerT(__ -> io(right(UNIT)))); // Commit
}
return new Transactional<>(pureIO(), readerT(__ -> io(left("Trigger rollback because odd integer")))); // Cancel;
};
}
}
public interface Transactor<M extends MonadRec<?, M>, Context, Cancel> {
<A> MonadRec<Either<Cancel, A>, M> runTransactional(Transactional<M, Cancel, Context, A> transactional);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment