Assertions in triggers

I’d like to assert an invariant in my trigger, but TriggerA doesn’t have an instance of CanAbort so I can’t use assertMsg and the like. Is this on purpose? If so, what’s the recommended way?

1 Like

Good question! I thought about this a while back and decided against adding it.

The reason for that was that assertions in triggers work quite different from assertions in choices. In choices, assertions are commonly used for input validation. If the input does not match the assertion, your transaction is aborted. In that setting, it is absolutely valid for the assertion to fail.

In triggers the situation is a bit different. Here assertions behave more like assertions in general-purpose languages like Java,C++,…. If they fail your trigger will crash so those are assertions that should never fail.

If you really want your trigger to crash, you can call error.

That said, while these were my thoughts originally around not adding it, I’m very interested in hearing other people’s thoughts. If we’re not worried about people having the wrong expectation around assert, we could definitely add it.

3 Likes

Can you elaborate a bit on the nuance here? At first glance “your transaction is aborted” does seem very similar to “your trigger will crash”: in both cases execution stops and nothing happens.

What am I missing? Is this about atomicity? Are there side-effects the trigger could have created before crashing that don’t get undone?

Transaction abort due to input validation is something that you expect to happen without any bugs in your DAML code. The input that was provided was just invalid. The input is often outside of your control and definitely outside of your DAML model so in a lot of cases there is no way to avoid this (unless you want to follow certain coding styles where you don’t abort and instead return an Either and make every choice nonconsuming)

A crashing trigger on the other hand is clearly a bug. There is no point in having a trigger that crashes on certain states. (there is a point in having a trigger that defaults ta noop on certain states).

So in one case, assertions failures are often expected and there is no way to prevent them (from within your code) whereas in the other case an assertion failure points to a bug and you only want to make sure that if that bug arises, your trigger crashes instead of doing something else.

1 Like

@cocreature I see the difference, but you could apply the same reasoning to error. So that’d be an argument to allow assert in triggers (at least for me, assert mentally desugars into a guarded error).

That’s a fair point but we cannot forbid error in triggers and imho the name there is a bit less misleading. I generally take the stance that just because you cannot prevent all errors, you shouldn’t stop to try to prevent some of them. But as I said, I’m happy to change this if enough users would like assert in triggers.

From my point of view assert is not misleading because all programming languages I know will crash the program on a failed assertion. But I guess my original question got answered, thanks!

I think there are two possible meanings for abort:

  1. Abort the trigger altogether
  2. Abort the current rule application

Option 1 would give you a way to “quit” a trigger which may be useful in some circumstances. Currently it’s not easy to express “run this rule until a given condition is met”. However, I’d prefer a way to distinguish “successful” exists from aborts on errors.
Option 2 would give you a “atomic” semantics for rule applications in the sense that we’d have to make sure state changes and command emission only happen after the entire rule is executed. That may also be useful in some situation, though you can easily make it so already with some simple patterns.

In short, I think neither option adds enough value to add them.

1 Like