Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancelable task #77

Open
dan-leech opened this issue Mar 18, 2021 · 1 comment
Open

Cancelable task #77

dan-leech opened this issue Mar 18, 2021 · 1 comment

Comments

@dan-leech
Copy link

I'm a newbie in FP. FP seems very powerful but I cannot catch the idea yet...

I need to tie two repositories transactions which don't know nothing about each other.
I've found probably a strange way to do this:

  Function(Completer<int> result, Completer<bool> rollBack) getData() {
    return (Completer<int> result, Completer<bool> rollBack) => db.transaction(() async {
      result.complete(45);

      final res  = await rollBack.future;
      if(!res) {
        // ex will be rethrown and the transaction will be rolled back
        throw Exception('rollback');
      }
    });
  }

I want to return a cancelable kind of task. That carries a possible result and can be rolled back from another task that this serves for.
Do you know any way how to do this in FP with dartz?

@spebbe
Copy link
Owner

spebbe commented Aug 13, 2021

Hi @dan-leech! Sorry for the late reply.

From your example, it looks like you do want result to complete successfully even in a rollback scenario, which makes me a bit unsure of what semantics you need. Either way, below are some pretty general pointers that might be useful to you:

One thing to try is to model your domain objects using immutable data structures. IMap, IList and the other immutable collections in dartz provide a kind of in-memory transactionality by simply never overwriting/mutating anything. This means that you can safely and incrementally create new versions of domain objects in your application logic and later choose to either commit them atomically to some backing store or "roll back" by simply discarding the references to the new object graphs.

Another thing you might want to try is Evaluation, which provides ways of both sequencing the results of asynchronous/parallel tasks and propagating errors through the use of Either values, which can be used to decide whether to commit or roll back at the end of a composed operation. The resulting code would be still be impure, assuming the db.transaction operations in your example are side effecting, but it would likely improve composability and testability.

A somewhat more involved approach is to capture all database operations in a Free algebra. This is what doobie does for the JDBC API and a similar approach is likely possible for your choice of database API. This would allow your application logic to be fully pure and would push away all database interaction into Free interpreters, which in turn can be side effecting (i.e., actually talk to the database) for production runs and integration tests, but can remain pure for unit and functional tests. This would also allow you to use Conveyor to construct streaming programs with semantics determined by your Free algebra. See https://github.com/spebbe/dartz/blob/master/lib/src/io.dart, https://github.com/spebbe/dartz/blob/master/lib/src/unsafe/io.dart and https://github.com/spebbe/dartz/blob/master/example/free_io/mock_io.dart for an example on how to capture and interpret impure APIs using Free.

I hope that helped, but I understand if the tips above are a bit too general to be immediately useful to you. Please reply with more detailed information about your use case if you want and maybe we can uncover something more concrete then!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants