Say I have some function that accepts a monadic action, of type m a
. I’ll use the result of this action zero or more times, to be determined at run time. If the action has a side effect, I want that effect to happen at most once, and not at all if the result isn’t needed.
It’d be nice to have an operator idem :: m a -> m (m a)
that would wrap the action and provide that guarantee with laws in the spirit of:
idem ma $> () = pure ()
join (idem ma) = ma
idem ma >>= (\ma' -> ma' *> ma') = ma
Some monads (Either
, e.g.) don’t have effects that can happen more than once; these actions are trivially idempotent, and idem = pure
suffices for them.
Other monads (IO
, e.g.) allow for creating refs of some variety, which I can use to memoize a result:
idem ma = newIORef Nothing <&> \ref ->
readIORef ref >>= maybe (ma >>= \a -> writeIORef ref (Just a) $> a) pure
Still other monads ([]
, I think) probably can’t support this operator at all.
Is there a MonadIdem
class or something that already contains this operator, or something equivalent? Or is this problem generally solved a different way, perhaps by wrapping any effectful actions with monad-specific logic outside of the code region that parameterizes over the application’s monad of choice?