1

I have the following code:

template Iou
  with
    issuer : Party
    owner : Party
    amount : Decimal
    currency : Text
  where
    signatory issuer

mergeIou : Iou -> Iou -> Iou
mergeIou a b =
--  assert $ a.issuer == b.issuer
--  assert $ a.owner == b.owner
--  assert $ a.currency == b.currency
  a with amount = a.amount + b.amount

When I uncomment any of the asserts I get the following error:

* Couldn't match expected type `Iou' with actual type `m0 ()'
    * In the expression:
        assert
          $ (DA.Internal.Record.getField @"issuer" a)
...

What am I doing wrong?

GeorgS
  • 741
  • 5
  • 12

2 Answers2

3

There's actually a way to define all three versions of mergeIou from @Recurse's answer in one go if you leverage DAML's ActionFail type class:

mergeIou : ActionFail m => Iou -> Iou -> m Iou
mergeIou a b = do
  unless (a.issuer == b.issuer) $ fail "IOU issuers did not match"
  unless (a.owner == b.owner) $ fail "IOU owners did not match"
  unless (a.currency == b.currency) $ fail "IOU currencies did not match"
  pure $ a with amount = a.amount + b.amount
1

The problem here is assert has an impure effect, so can't be used in a pure function like mergeIou. The easiest way to solve this is to change mergeIou to have type Iou -> Iou -> Update Iou and put the function in a do-block.

ie.

mergeIou : Iou -> Iou -> Update Iou
mergeIou a b = do
  assert $ a.issuer == b.issuer
  assert $ a.owner == b.owner
  assert $ a.currency == b.currency
  pure $ a with amount = a.amount + b.amount

If you need the function to be pure, you can't use assert. The simplest alternative is to use Optional to make the failure explicit in the type:

mergeIou : Iou -> Iou -> Optional Iou
mergeIou a b = do
  unless (a.issuer == b.issuer) None
  unless (a.owner == b.owner) None
  unless (a.currency == b.currency) None
  pure $ a with amount = a.amount + b.amount

To aid with debugging I suggest you use Either instead, so you can identify which of the assertions failed:

mergeIou : Iou -> Iou -> Either Text Iou
mergeIou a b = do
  unless (a.issuer == b.issuer) $ Left "IOU issuers did not match"
  unless (a.owner == b.owner) $ Left "IOU owners did not match"
  unless (a.currency == b.currency) $ Left "IOU currencies did not match"
  pure $ a with amount = a.amount + b.amount

For a fuller discussion of exactly what is going on here, I suggest you read my extended answer to Trouble using the getTime function where I discuss the concepts of purity and encapsulating ledger interactions in DAML.

Recurse
  • 3,557
  • 1
  • 23
  • 36