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.