Mod note: As of SDK 1.5.0 Scenarios have been superseded by the more powerful Daml Script. We now recommend using that for all purposes. For more information, and to learn how to use Script please check out @Andreas’ post on our blog.
I just tracked down a bug in my code that I found quite hard (and surprising) to discover. It was related to the difference between error and fail, so I wanted to ask for some clarification on this.
I have the following function:
oneOrNone : Optional a -> Optional a -> Optional a
oneOrNone (Some x) (Some y) = fail "Only one of the optionals may be set"
oneOrNone (Some x) None = Some x
oneOrNone None (Some y) = Some y
oneOrNone None None = None
The following test case passes, which is (at first) unexpected:
test = scenario do
let
x = Some "x"
y = Some "y"
z = oneOrNone x y
assert $ z == None
It means my function above returns None in the case with the fail.
Intuitively, I expected fail to just abort the transaction with the corresponding error message. But apparently fail works according to the ActionFail type class implementation, which in the case of Optional seems to just return None, discarding the error message. The docs for fail don’t really explain this behaviour well, just stating: Fail with an error message.
I think my misconception came from the fact that fail indeed aborts the transaction when I’m in the Update or Scenario monad. I just hadn’t realized that that behaviour changes when I’m in the Optional context. It was in fact error that I should’ve used, which seems to do what I wanted here.
So I guess I’ve figured out why I’ve produced this bug. But could anyone shed some light on the two functions, when to use each, and how exactly error works? The documentation of error is not quite clear: it states that “within a transaction” it will abort it. But in my example test case I’m not in any transaction context afaics. So how exactly does this work?
A related question: does the ActionFail instance on Optional (and also [], which I assume produces an empty list) even make sense? What would be valid use cases for fail within the Optional or list monad, wouldn’t I just return None, resp. [] directly instead, as the error message for fail is discarded anyway?
Appreciate any insights on how to better make sense of these functions.