<div dir="ltr">I made an STM implementation for work with the ability to give a "reason" for retrying, which I have found very useful. A Haskell version might look something like this:<div><br></div><div>data STM e a</div><div>instance Monad (STM e)</div><div><br></div><div>-- These also form a monad, but we'd need a newtype to define the instance.</div><div>retry :: e -> STM e a</div><div>orElse :: STM e1 a -> (e1 -> STM e2 a) -> STM e2 a</div><div><br></div><div>With this interface, you could define something like this:</div><div><br></div><div>possiblyRollback :: STM (Either e a) b -> STM e (Either a b)</div><div>possiblyRollback stm = fmap stm Right `orElse` either retry (return . Left)</div><div><br></div><div>With this, you can retry as normal with `retry (Left e)`, locally rollback with `retry (Right a)`, or return as normal with `return b`.</div><div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Nov 30, 2020 at 3:47 AM Joachim Durchholz <<a href="mailto:jo@durchholz.org">jo@durchholz.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> From a contract design perspective, rolling back means that an <br>
operation failed and had to be undone. (If everything was fine, you <br>
wouldn't want to rollback, wouldn't you?)<br>
<br>
So if rollback is the reaction to some failure, then triggering it via <br>
an exception is the Right Thing To Do, actually.<br>
Oh, and you always want to rollback if there's an exception somewhere.<br>
<br>
Now that contract thing warrants a few more words.<br>
Yes, you can word your contracts so that they include failure. It's just <br>
that they get more complicated. Usually so much more complicated that <br>
it's not worth it.<br>
Also, you want any exception (i.e. breach of contract) to go into a <br>
rollback. Now in Haskell, rolling back means just abandoning the <br>
computation since things aren't mutable and there is no state that you <br>
need to restore. Technically, not even in STM - so there *is* reason to <br>
make the rollback non-exceptional here: One could argue that STM is <br>
*supposed* to include the rollback in the contract.<br>
In the end, it depends on your use case: Do you want the rollback in the <br>
contract or not?<br>
<br>
Now for STM itself, yeah using an exception to trigger a rollback is a <br>
bit hacky.<br>
OTOH it works.<br>
OT3H it might be a good idea to have a rollback function for STM. <br>
Technically it would just trigger the same exception, but it would <br>
indicate that the rollback is part of the contract.<br>
OT4H throwing Rollback is essentially an indicator of non-failure <br>
rollback (otherwise you'd be throwing whatever exception is appropriate, <br>
or get aborted by some library functionality that throws an exception).<br>
<br>
Regards,<br>
Jo<br>
<br>
Am 28.11.20 um 22:05 schrieb amindfv--- via Haskell-Cafe:<br>
> I'd like to be able to give up on an STM transaction: roll back and don't retry.<br>
> I've cooked up something with exceptions but it feels a bit icky to use exceptions for something like this - is there a better way?:<br>
> <br>
>      data Rollback = Rollback deriving (Show)<br>
>      instance Exception Rollback<br>
> <br>
>      rollback :: STM x<br>
>      rollback = throwSTM Rollback<br>
> <br>
>      atomicallyWithRollback :: STM x -> IO (Maybe x)<br>
>      atomicallyWithRollback a =<br>
>         (Just <$> atomically a)<br>
>            `catch` (\Rollback -> pure Nothing)<br>
> <br>
> The alternative I've found is something like:<br>
> <br>
>      otherWay :: STM x -> IO (Maybe x)<br>
>      otherWay a =<br>
>         atomically $ (Just <$> a) `orElse` pure Nothing<br>
> <br>
> But this turns any "retry" in "a" into a rollback, and I'd like to have the option to do either (retry or rollback).<br>
> <br>
> Thanks,<br>
> Tom<br>
> <br>
> <br>
> _______________________________________________<br>
> Haskell-Cafe mailing list<br>
> To (un)subscribe, modify options or view archives go to:<br>
> <a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
> Only members subscribed via the mailman list are allowed to post.<br>
> <br>
<br>
_______________________________________________<br>
Haskell-Cafe mailing list<br>
To (un)subscribe, modify options or view archives go to:<br>
<a href="http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe" rel="noreferrer" target="_blank">http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe</a><br>
Only members subscribed via the mailman list are allowed to post.</blockquote></div>