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

[Idea] Effect dedicated to idempotent operations #931

Open
sideeffffect opened this issue Dec 14, 2024 · 6 comments
Open

[Idea] Effect dedicated to idempotent operations #931

sideeffffect opened this issue Dec 14, 2024 · 6 comments

Comments

@sideeffffect
Copy link
Contributor

We already have classes for effects

  • Async, the most powerful kind, anything can happen
  • IO, only synchronous effects can happen

Would it make sense to have an Effect which is only for operations which are idempotent. Those are operations where it doesn't matter if they're executed once, twice or n-times; it only matters if they're executed or not.

This class of effects would be subsumed by those above, I suppose. Or maybe just by Async (there can be async operations which are idempotent)?

@johnhungerford
Copy link
Contributor

Are there any operations specific to idempotency that it would suspend? If not, it would simply track idempotency.

We might be able to make a generic effect-tracking type, which simply wraps other effects:

trait Tracked[S, Marked]:
  def tracked[A](effect: A < S)(using Tag[S], Tag[Marked]): A < Marked = ???
  def untracked[A, S1](effect: A < (Marked & S1))(using Tag[S], Tag[Marked]): A < (S & S1) = ???


trait Idem[-S]
class IdemConstructor[+S] extends Tracked[S, Idem[S]]

object Idem:
   def apply[A, S](effect: A < S): Idem[S] = Idem[S].tracked(effect)
   def handle[A, S, S1](effect: A < (Idem[S] & S1)): A < (S & S1) = Idem[S].untracked(effect)

@sideeffffect
Copy link
Contributor Author

sideeffffect commented Jan 17, 2025

Interesting! Maybe this same mechanism could be also used to track Transaction effect as I've sketched over here

Open question is, what should be the relationship between Idempotent and Transaction.

@fwbrasil
Copy link
Collaborator

I can't form a good picture of how this would be useful yet. Would it make sense to extend the effect to handle both idempotent and reversible computations? I think the bundle could be more useful. The evaluation of the computation could use this information to handle safe retries and rollbacks. It could enable implementing the saga pattern for example.

@sideeffffect
Copy link
Contributor Author

Would it make sense to extend the effect to handle both idempotent and reversible computations?

Maybe? But I think they are different. There are computations that are idempotent, but not reversible. And vice versa. E.g. computation "Ensure the missile Xyz is launched".

@fwbrasil
Copy link
Collaborator

The same effect could handle both:

opaque type Rerunnable = ..
object Rerunnable:
    def idempotent[A, S](v: A < S) : A < (S & Rerunnable) = ...
    def reversible[A, S, S2](v: A < S)(reverse: => Unit < S2): A < (S & S2 & Rerunnable) = ...

It's interesting that Resource.acquireRelease can already be used to implement reversible computations.

I'm still having trouble to see how a new effect would be useful. Just tracking Rerunnable isn't very valuable since the computation could perform arbitrary side effects and freely introduce Rerunnable via effect widening. Something we could try is restricting Rerunnable.run to not accept a pending IO but that would rule out other effects based on it like Async.

@sideeffffect
Copy link
Contributor Author

sideeffffect commented Jan 24, 2025

I'm still having trouble to see how a new effect would be useful.

It can be very useful for a user of Kyo to ensure that certain parts of their program is idempotent. Or that some parts are repeatable. Or that some parts are running within a transaction. Those are very interesting properties which programmers are interested in and they can exploit those properties to their advantage.

I don't think every Kyo users would use these distinctions all the time. But I think there could be great utility and thus demand. None of the other tools (ZIO, Cats Effect, ...) allow you to do that.

not accept a pending IO but that would rule out other effects based on it like Async

Indeed, idempotent program can be composed only of other idempotent programs (+ pure functions, of course).

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

No branches or pull requests

4 participants