If-Then-Else statements inside forA loop in DAML

The easiest way to get something you can copy is probably to run daml build in a terminal and copy it from there. Alternatively, click on View -> Problems via the top menu and that should show you the full error as well.

I also suggest to share the code snippet via a github gist if possible, that way we can sort out the indentation issues fast.

In View → Problems, this is the only error message displayed.

What I’d like to see is the line number which should be displayed there and the line numbers of the original code snippet (or as mentioned above, ideally the full code snippet) so we can see which part of the code is causing the error.

I see one syntax problem in these blocks:

"DV1" -> 
  let 
    data1 = RD 
      with
        a = devSecCon.abc
        b = dvSecCon.def
    return (data1)

Here the value return (data1) is inside the let block. That doesn’t work. Every element in a let block must have the form var = expr.

The thing on the right hand side of the -> must itself be an expression. There are two possibilities:

Using let..in...

"DV1" -> 
  let 
    data1 = RD 
      with
        a = devSecCon.abc
        b = dvSecCon.def
  in return (data1)

or using do

"DV1" ->  do
  let 
    data1 = RD 
      with
        a = devSecCon.abc
        b = dvSecCon.def
  return (data1)
1 Like

@Pris17 as a general pointer, a lot of the topics discussed here are covered in the introductory documentation to Daml: An introduction to Daml — Daml SDK 1.14.0 documentation

I can highly recommend working your way through that.

2 Likes

Both of above possibilities doesn’t work. I still have indentation issues. After going with 2nd option from your reply, I am now getting below error on the closing bracket of forA loop.

parse error (possibly incorrect indentation or mismatched brackets)parser

I even tried removing do and indenting return. return is not inside let now. Still I face below error

parse error on input ‘return’parser

1 Like

I can only echo what @cocreature said above: Without complete, reproducible code-samples, it is very difficult to diagnose and help.

1 Like

I can see two other issues in that latest code snippet. First, the value produced by forA will be a list, so you will not be able to bind it to a pair ((DVData1, DVData2) <- ...). Second, Daml requires that identifiers that start with an uppercase letter be types, while identifiers that start with a lowercase letter be values. Hence, Daml will interpret DVData1 as a type and it is not valid to have a type in that position. You can correct that one by just changing to dvData1 instead.

You have not explained what it is you are actually trying to do, which makes helping you a lot more difficult. From what I can gather so far, the code you’re trying to write will:

  • Take a list of parties and a specific identifier
  • For each party, try to find a contract with that identifier as (part of) its key
  • Return the data from the contract

I don’t know why you want to do that, and there are a few caveats I’ll go into afterwards, but here is a self-contained sample code that does something like that:

module Main where

import Daml.Script

template ContractA
  with
    sig: Party
    id: Text
    arg1: Text
    arg2: Int
  where
    signatory sig
    key (sig, id) : (Party, Text)
    maintainer key._1

data Result = Result with a: Text, b: Int
  deriving (Eq, Show)

template ContractB
  with
    sig: Party
    payload: [Result]
  where
    signatory sig

template DoStuff
  with
    sig: Party
  where
    signatory sig
    preconsuming choice CreateContractB : ContractId ContractB
      with parties: [Party]
           id: Text
      controller sig
      do
        -- Note: results is a list
        results <- forA parties (\party -> do
          (cid, payload) <- fetchByKey @ContractA (party, id)
          case partyToText party of
            "Alice" -> do
              let data1 = Result
                    with
                      a = payload.arg1
                      b = payload.arg2
              return data1
            "Bob" -> do
              let data2 = Result
                    with
                      a = payload.arg1
                      b = payload.arg2
              return data2
            _ -> error "No valid party")
        create ContractB with payload = results, ..

setup : Script ()
setup = script do
  alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
  bob <- allocatePartyWithHint "Bob" (PartyIdHint "Bob")
  carol <- allocatePartyWithHint "Carol" (PartyIdHint "Carol")

  -- Fails because no ContractA contract exists yet
  submitMustFail alice do
    createAndExerciseCmd (DoStuff alice) CreateContractB with parties = [alice, bob], id = "id"
  
  submit alice do
    createCmd ContractA with sig = alice, id = "id", arg1 = "some text", arg2 = 42
  
  -- succeeds because there is one contract for Alice and Alice can see it
  submit alice do
    createAndExerciseCmd (DoStuff alice) CreateContractB with parties = [alice], id = "id"

  -- fails because there is no contract for Bob, so the `fetchByKey` command fails
  submitMustFail alice do
    createAndExerciseCmd (DoStuff alice) CreateContractB with parties = [alice, bob], id = "id"

  return ()

This is all syntactically correct, and vaguely looks like your code. However, there are a number of issues with this design:

  • The CreateContractB choice will fail as soon as you cannot fetch a contract in the given list of parties, either because you cannot see it (see Daml privacy rules and specifically rules for fetching a contract by key).
  • The choice will also fail as soon as the list passed in as a parameter contains anything else than alice or bob, because of the explicit case on party name (and the explicit error rather than just ignoring non-matching names). It will also fail on most other ledgers than sandbox, because in general party names are not strictly equal to the given hint.
  • You’d be much better off filtering the list of parties before sending it to the choice.
  • This code is, ultimately, doing the same thing for both "Bob" and "Alice" cases, so it could be rewritten without a case at all.

Without knowing what you are trying to achieve, it’s hard to tell you where to go from here, but hopefully this will help a bit.

1 Like

TC.daml (1.2 KB)

I have attached the code here. There is parse error on line number 38.

@bernhard @Gary_Verhaegen Can you please quickly take a look at the code snippet and help me resolve the issue. Thanks

You didn’t follow either of my suggestions. The blocks after -> contain neither do nor in.

There are a couple of other indentation issues. The closing bracket in line 46 needs to be indented more, and the create in line 48, too. Then you have a missing semicolon in line 48.

TC.daml (1.3 KB)

Attached is updated file. Neither of both the above mentioned works. Using let …in as well as do…They give errors on line number 47. Attached code has do inside the case. Still it throws error on line 47.
Error is:

parse error (possibly incorrect indentation or mismatched brackets)parser

Here is the module free of syntax errors:

module TC where

data SRData = SRData
  with
    a: Text
    b: Text
  deriving (Eq, Show)

template MRCreate
  with
    partyA : Party
    id : Text
  where
    signatory partyA
    controller partyA can
      nonconsuming TestMR : ContractId MR
        with
          otherparties : [Party]
        do
          mcID <- lookupByKey @MR (partyA, id)
          case (mcID) of
            Some mcId -> return mcId 
            None -> do
              [dv1data, dv2data] <- forA otherparties (\dv -> do
                (cID, dvCon) <- fetchByKey @ContractB (dv, id)
                case partyToText dv of
                  "DV1" -> do
                    let srdata1 = SRData 
                          with a = dvCon.abc
                               b = dvCon.def
                    return (srdata1)
                  "DV2" -> do
                    let srdata2 = SRData 
                          with a = dvCon.abc
                               b = dvCon.def
                    return (srdata2)
                  _ -> error "No valid DV")
              return [dv1data, dv2data]
          create MR with sRData1= dv1data; sRData2= dv2data; ..

template MR
  with
    partyA: Party
    sRData1: SRData
    sRData2 : SRData
  where
    signatory partyA
    key (partyA, sRData1.a): (Party, Text)
    maintainer key._1

It is still failing on the absence of a definition for ContractB, though.

4 Likes

As a side note, your code uses tabs instead of spaces. I highly recommend against doing that in indentation sensitive languages like python or Daml. It makes it super easy to end up with situations that look like they are indented properly but are not.

2 Likes

Thanks @Gary_Verhaegen …It is working as expected now. Thanks all for your help.

1 Like