Indentation error in record creation

Before I implemented @Gary_Verhaegen suggestions from Variable not in scope: submitMulti - #27 with the additional templates, I just wanted to see if the Proposal template itself would work. I set it up as you suggested @anthony but I can’t get the script to work. I’ve tried a number of permutations of the below but I don’t think I’m passing the members for the Guild correctly.

This is my script code

-- RUN_SCRIPT_PROPOSAL --
exampleproposal = do
    -- Creates each party
    alice <- getParty "Alice"
    hakan <- getParty "Hakan"
    martin <- allocateParty "Martin"
    rita <- allocateParty "Rita"
    john <- allocateParty "John"
    michael <- allocateParty "Michael"
    sam <- allocateParty "Sam"
    james <- allocateParty "James"
    
    -- Creates an instance of the Investor contract, authorized by "Hakan"
    submit alice do
      create Proposal with
        issuer = alice; investor = hakan; projectdescription = "test"; unitsrequired = 5; marketingcost = 5; distributioncost = 10; additionalcost =  10; proposalId = "p1";
         guild = Guild with
          members = [martin, rita, john, michael, sam, james]
         --- because this was a data declaration
         item = Item with
          unitdesignation = "kilos"
          priceperunit = 5
      return()
      
  -- END_SCRIPT --

There’s a parser error on input. Screenshot below

Any suggestions please?

1 Like

Hi @krmmalik,
Daml is indentation sensitive. If you start a with-block the fields of that record should all be aligned at the same indentation level. In your example you are mixing ; which sidesteps the indentation stuff and indentation which gives you this confusing error. I recommend to use with + each field on a separate line. Here is a minimized standalone example of your example that fixes the issue (note that there is another issue that I also fixed: You mixed scenario’s getParty and create with Daml Script’s allocateParty).

module Main where

import Daml.Script


data Guild = Guild
  with
    members : [Party]
  deriving (Eq, Show)

data Item = Item
  with
    unitdesignation : Text
    priceperunit : Int
  deriving (Eq, Show)

template Proposal
  with
    issuer : Party
    investor : Party
    guild : Guild
    item : Item
  where
    signatory issuer
    observer investor

exampleproposal = do
    -- Creates each party
    alice <- allocateParty "Alice"
    hakan <- allocateParty "Hakan"
    martin <- allocateParty "Martin"
    rita <- allocateParty "Rita"
    john <- allocateParty "John"
    michael <- allocateParty "Michael"
    sam <- allocateParty "Sam"
    james <- allocateParty "James"

    -- Creates an instance of the Investor contract, authorized by "Hakan"
    submit alice do
      createCmd Proposal with
        issuer = alice
        investor = hakan
        guild = Guild with
          members = [martin, rita, john, michael, sam, james]
         --- because this was a data declaration
        item = Item with
          unitdesignation = "kilos"
          priceperunit = 5
1 Like

Thanks @cocreature

I was confused about the indentation when it comes to with - blocks so thank you for clearing that up.
Also, I was upgraded this script from a former scenario and completely forgot about some of the syntax changes.

That said, the script still isn’t working, and I have a feeling it’s because I have a proposal-accept pattern for my Guild creation (although for the proposal template, I don’t actually want to create a new Guild, I just want to be able to issue a proposal. Do you think that’s why the script is failing?

Here’s my full template code for the Guild and Proposal for reference (I see you declared the Guild as data in this case which is why the script might have worked for you)

template Guild with
    guildName: Text
    creator: Party
    members: [Party]
  where
    signatory creator, members
    ensure length members >= 4 && DA.List.unique members

-- initial proposal accept pattern for formation of Guild
template ProposeGuild with
    guildName: Text
    creator: Party
    requestedMembers: [Party]
    agreedMembers: [Party]
  where
    signatory creator, agreedMembers
    observer requestedMembers

    preconsuming choice Agree: ContractId ProposeGuild
        with member: Party
        controller member
      do
        assert $ member `elem` requestedMembers -- if member is in the requestedmembers list is = true 
        create this with agreedMembers = member::agreedMembers -- :: is cons operator to construct a list, so populate agreedmember list with member list
                         requestedMembers = DA.List.delete member requestedMembers -- now delete members from the requestedmember list cos we're done
    preconsuming choice Create: ContractId Guild
        controller creator
      do
        create Guild with guildName, creator, members = agreedMembers

which has a Script that goes like this


guildtest = do
  martin <- allocateParty "Martin"
  rita <- allocateParty "Rita"
  john <- allocateParty "John"
  michael <- allocateParty "Michael"
  sam <- allocateParty "Sam"
  james <- allocateParty "James"

  propId1 <- submit martin do
    createCmd ProposeGuild with
      guildName = "Martin's guild"
      creator = martin
      requestedMembers = [rita, john, michael, sam, james]
      agreedMembers = []
  propId2 <- submit rita do
    exerciseCmd propId1 Agree with member = rita
  propId3 <- submit john do
    exerciseCmd propId2 Agree with member = john
  propId4 <- submit michael do
    exerciseCmd propId3 Agree with member = michael
  propId5 <- submit sam do
    exerciseCmd propId4 Agree with member = sam
  guildId <- submit martin do
    exerciseCmd propId5 Create
  pure()

This is the modified script i’ve now put together with your input to test Proposal

 -- RUN_SCRIPT_PROPOSAL --
exampleproposal = do
    -- Creates each party
    alice <- allocateParty "Alice"
    hakan <- allocateParty "Hakan"
    martin <- allocateParty "Martin"
    rita <- allocateParty "Rita"
    john <- allocateParty "John"
    michael <- allocateParty "Michael"
    sam <- allocateParty "Sam"
    james <- allocateParty "James"
    
    -- Creates an instance of the Investor contract, authorized by "Hakan"
    submit alice do
      createCmd Proposal with
        issuer = alice 
        investor = hakan
        guild = Guild with
          guildName = "Martin's Guild"
          creator = martin
          members = [martin, rita, john, michael, sam, james] 
        projectdescription = "test" 
        unitsrequired = 5 
        marketingcost = 5 
        distributioncost = 10 
        additionalcost =  10 
        proposalId = "p1"
         --- because this was a data declaration
        item = Item with
          unitdesignation = "kilos"
          priceperunit = 5
      return()
      
  -- END_SCRIPT --

But I get the following error

> /Users/khurammalik/DevTree/Qirad Agent Network/app/daml/Qirad.daml:188:7: error:
    • No instance for (Action Commands) arising from a do statement
    • In a stmt of a 'do' block:
        createCmd
          Proposal
            {issuer = alice, investor = hakan,
             guild = Guild
                       {guildName = "Martin's Guild", creator = martin,
                        members = [martin, rita, ....]},
             projectdescription = "test", unitsrequired = 5, marketingcost = 5,
             distributioncost = 10, additionalcost = 10, proposalId = "p1",
             item = Item {unitdesignation = "kilos", priceperunit = 5}}
      In the second argument of ‘submit’, namely
        ‘do createCmd
              Proposal
                {issuer = alice, investor = hakan,
                 guild = Guild
                           {guildName = "Martin's Guild", creator = martin, members = [...]},
                 projectdescription = "test", unitsrequired = 5, marketingcost = 5,
                 distributioncost = 10, additionalcost = 10, proposalId = "p1",
                 item = Item {unitdesignation = "kilos", priceperunit = 5}}
            return ()’
      In a stmt of a 'do' block:
        submit
          alice
          do createCmd
               Proposal
                 {issuer = alice, investor = hakan,
                  guild = Guild
                            {guildName = "Martin's Guild", creator = martin, members = [...]},
                  projectdescription = "test", unitsrequired = 5, marketingcost = 5,
                  distributioncost = 10, additionalcost = 10, proposalId = "p1",
                  item = Item {unitdesignation = "kilos", priceperunit = 5}}
             return ()

Your example doesn’t quite fit together, your script references Proposal while you’ve shown a different template called ProposeGuild.

Here is an adapted version of the script to match ProposalGuild that throws roughly the same error:

    submit martin do
      createCmd ProposeGuild with
        guildName = "Martin's Guild"
        creator = martin
        requestedMembers = [martin, rita, john, michael, sam, james]
        agreedMembers = []
      return()

The issue here is the do block passed to submit. Daml Script needs a special form of do blocks that does not allow for dependencies between individual statements. There are two ways to fix this:

  1. Enable ApplicativeDo in your file. This enables the special form of do blocks. To do so, insert the following in the first line of your file:
{-# LANGUAGE ApplicativeDo #-}
  1. Simply remove the return () at the end. Then you have a single-statement do block which doesn’t need anything special. You can also just remove the do at this point since it doesn’t do anything.

Thanks, could you please elaborate on what you mean by “does not allow for dependencies between individual statements” ?

1 Like
do x <- something
     y <- f x
    pure ()

Here the second statement f x depends on the result of the first statement.

In Daml Script the argument to submit must be of the form

do x0 <- f0
   x1 <- f1
   …
   xn <- f n
  return g

where x_i does not occur in any of the f_i (it can however occur in g).

This might seem like a weird restriction at first. However, it makes sense once you look at the Ledger API: When you submit a command, you need to pass a list of commands with no way for one of those commands to refer directly to the result of a previous command (you can have indirect references via contract keys). So Daml Script, has to restrict what you can do in such a way that it can extract the list of independent commands from it which it then submits to the Ledger API.

2 Likes

Oh i see! Thanks for clarifying that. I didn’t come across this in any of the documentation or at least I don’t remember seeing it so I was very confused when I’d had some help previously on this forum why some scripts ended with pure () and others with return()

1 Like

pure and return are synonyms for each other. See What is the diff between pure() and return() ?Could you give me an example? - #2 by Luciano for more details. ApplicativeDo is documented in a few different places, e.g., the API docs for Daml Script’s Command type.

1 Like

To clarify, the limitation @cocreature mentions is for the argument to submit, not for all do blocks; the do block that defines a Script can (and generally does) have dependencies between lines, and can safely end in a return () (or pure ()) if it has been declared to be of type Script ().

2 Likes