Parse error: Couldn't match type ContractId X with 'Optional a2' (constraint HasExercise)

Hello to Everyone,

Here is a question from a total beginner in Daml (and functional programming).

Why do I get a parse error on the following line?:
(iouSplitCid, iouRestCid) <- exercise iouCid Iou_Split with splitAmount=amount

The wording of the error:

Couldn't match type ‘ContractId Iou’ with ‘Optional a2’ arising from a functional dependency between: constraint ‘HasExercise Iou Iou_Split (Optional a1, Optional a2)’ arising from a use of ‘exercise’ instance ‘HasExercise Iou Iou_Split (IouCid, IouCid)’ at <no location info> .

Related instructions/declarations:
amount: Decimal
skier: Party

(iouCid, iouData) ← fetchByKey @Iou skier

template Iou
  with
    owner : Party
...
    signatory owner

    key owner: Party
    maintainer key

    controller owner can
      Iou_Split : (IouCid, IouCid)
        with
          splitAmount: Decimal
        do
...

Thank you for any clarifications!

2 Likes

Hi @mlyczak, welcome to the forum!

Judging from the error, it looks like it’s not a parse error but a type error instead. It looks like the choice Iou_Split returns a pair (IouCid, IouCid) but you are using it in a context where an Optional a for some type a is expected.

Can you share the full example? That might make it easier to suggest a solution.

3 Likes

Hi @cocreature,

Below is the related code:


template SkiCandidate
    with
        skier: Party
        reduction: SkipassReductionType
    where
        signatory skier

        key skier: Party
        maintainer key

        nonconsuming choice PrepareTransfer: ContractId Iou
            with
                amount: Decimal
                currency: Text
            controller skier
            do
                -- assumption: a single Iou is present for a given owner
                (iouCid, iouData) <- fetchByKey @Iou skier
                iouData.owner === skier
                assertMsg "IOU with a different currency - payment impossible" (iouData.currency == currency)
                assertMsg "Not enough IOU-balance to send the transfer" (iouData.amount >= amount)

                (iouSplitCid, iouRestCid) <- exercise iouCid Iou_Split with splitAmount=amount
...


type IouCid = ContractId Iou

template Iou
  with
    owner : Party
    currency : Text
    amount : Decimal
    observers : [Party]
  where
    ensure amount > 0.0

    signatory owner
    observer observers

    key owner: Party
    maintainer key

    controller owner can

      -- Split the IOU by dividing the amount.
      Iou_Split : (IouCid, IouCid)
        with
          splitAmount: Decimal
        do
          let restAmount = amount - splitAmount
          splitCid <- create this with amount = splitAmount
          restCid <- create this with amount = restAmount
          return (splitCid, restCid)


Is that enough context? Thank you!

2 Likes

I can’t quite reproduce the error in your example. There is one issue in PrepareTransfer: You need to return the contract id on the last line of the choice. The following code compiles for me:

module Main where

import DA.Assert

data SkipassReductionType = SkipassReductionType
  deriving (Eq, Show)

template SkiCandidate
    with
        skier: Party
        reduction: SkipassReductionType
    where
        signatory skier

        key skier: Party
        maintainer key

        nonconsuming choice PrepareTransfer: ContractId Iou
            with
                amount: Decimal
                currency: Text
            controller skier
            do
                -- assumption: a single Iou is present for a given owner
                (iouCid, iouData) <- fetchByKey @Iou skier
                iouData.owner === skier
                assertMsg "IOU with a different currency - payment impossible" (iouData.currency == currency)
                assertMsg "Not enough IOU-balance to send the transfer" (iouData.amount >= amount)

                (iouSplitCid, iouRestCid) <- exercise iouCid Iou_Split with splitAmount=amount
                pure iouSplitCid


type IouCid = ContractId Iou

template Iou
  with
    owner : Party
    currency : Text
    amount : Decimal
    observers : [Party]
  where
    ensure amount > 0.0

    signatory owner
    observer observers

    key owner: Party
    maintainer key

    controller owner can

      -- Split the IOU by dividing the amount.
      Iou_Split : (IouCid, IouCid)
        with
          splitAmount: Decimal
        do
          let restAmount = amount - splitAmount
          splitCid <- create this with amount = splitAmount
          restCid <- create this with amount = restAmount
          return (splitCid, restCid)
1 Like

Due to my lack of understanding on how the Daml parser works, I have not included lines that directly follow the one with the error.
One of these lines:
assert(isSome iouSplitCid)

And here’s where the problem comes from. (Now I’m after a long explanatory session regarding some theory related to the issue.) The assert statement assumes iouSplitCid to be of type Optional, and carries this assumption over to the previous instruction (exercise iouCid…) where it is not met, thus the complaining.
Removing the erroneous assertion gets rid of the initial error.

@cocreature I apologize for providing you with incomplete information.
Thank you very much for all your efforts!

I’m guessing this is the same issue that I reported in Optional type mismatch errors in a confusing place · Issue #8202 · digital-asset/daml · GitHub, unfortunately it sounds like it’s not trivial to make the error message more user friendly.

1 Like