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?
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
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:
- Enable
ApplicativeDoin your file. This enables the special form ofdoblocks. To do so, insert the following in the first line of your file:
{-# LANGUAGE ApplicativeDo #-}
- Simply remove the
return ()at the end. Then you have a single-statementdoblock which doesn’t need anything special. You can also just remove thedoat 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” ?
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.
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()
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.
I was very confused when I’d had some help previously on this forum why some scripts ended with
pure ()and others withreturn()
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 ().
