Automation with Daml Triggers - Interactive Tutorial Help
Hello! I’m receiving an error when I attempt to run daml start in the interactive Terminal for the exercise (page 5) of the Automation with Daml Triggers tutorial.
Here’s the error:
$ daml start
Compiling market to a DAR.
File: daml/Market.daml
Hidden: no
Range: 109:30-109:33
Source: typecheck
Severity: DsError
Message: daml/Market.daml:109:30: error:Not in scope: type constructor or class ‘ACS’
ERROR: Creation of DAR file failed.
daml-helper: Received ExitFailure 1 when running
Shell command: /root/.daml/bin/daml build
I’ve tried messing with the indentation but that triggers another error. What am I missing?
Here’s the code that I copied into the browser IDE:
module Market where
import DA.Date
import Daml.Trigger
import DA.Next.Map
import DA.Foldable(forA_)
template User
with
party : Party
where
signatory party
key party : Party
maintainer key
nonconsuming choice NewSellOffer : ()
with
observers : [Party]
title : Text
description : Text
price : Int
controller party
do
now <- getTime
create $ SellOffer {seller = party, date = toDateUTC now, ..}
pure ()
nonconsuming choice TakeSellOffer : ()
with
offer : ContractId SellOffer
controller party
do
exercise offer DoTrade with tradePartner = party
pure ()
nonconsuming choice ConfirmPayment : ()
with
invoice : ContractId Invoice
controller party
do
Invoice{..} <- fetch invoice
assert $ owner == party
create $ PaymentConfirmation
with
invoice = invoice
party = party
obligor = obligor
pure ()
template SellOffer
with
observers : [Party]
title : Text
description : Text
price : Int
seller : Party
date : Date
where
signatory seller
observer observers
nonconsuming choice DoTrade : ()
with
tradePartner : Party
controller tradePartner
do
assert $ tradePartner `elem` observers
archive self
create $ Invoice {owner = seller, obligor = tradePartner, amount = price, description = title}
pure ()
template Invoice
with
owner : Party
obligor : Party
amount : Int
description : Text
where
signatory obligor
observer owner
template PaymentConfirmation
with
invoice : ContractId Invoice
party : Party
obligor : Party
where
signatory party
observer obligor
nonconsuming choice ArchiveInvoice : ()
controller obligor
do
archive invoice
archive self
deleteInvoiceTrigger : Trigger ()
deleteInvoiceTrigger = Trigger
{ initialize = const ()
, updateState = \_acs _message () -> ()
, rule = deleteInvoiceRule
, registeredTemplates = RegisteredTemplates
[ registeredTemplate @Invoice
, registeredTemplate @PaymentConfirmation
]
, heartbeat = None
}
deleteInvoiceRule : Party -> ACS -> Time -> Map CommandId [Command] -> () -> TriggerA ()
deleteInvoiceRule party acs _t commandsInFlight () = do
let invoices = getContracts @Invoice acs
let confirmations = getContracts @PaymentConfirmation acs
let ready = do
(confirmationCid, PaymentConfirmation{invoice, obligor}) <- confirmations
(invoiceCid, Invoice{}) <- invoices
guard $ invoiceCid == invoice
guard $ party == obligor
pure confirmationCid
forA_ ready $ \confirmationCid -> dedupExercise confirmationCid ArchiveInvoiceHi @kirksudduth,
This is not an indentation issue. The compiler is complaining about this line:
deleteInvoiceRule : Party -> ACS -> Time -> Map CommandId [Command] -> () -> TriggerA ()
Specifically, the fact that you do not have a type ACS defined.
I’m not familiar with the tutorial you mentioned, so I can’t comment where that type is supposed to be coming from. Happy to take a deeper look if you can share a link.
Rules in Daml Triggers no longer take so many arguments; ACS is not defined because there is no longer an ACS type exported by the triggers library, as it is not needed. You can see the current signature in the Daml Triggers documentation.
I assume you got the sample source code from another part of the tutorial; I am unsure of the provenance of this sample.
Looking at your code more closely (and cross-referencing with the Daml.Trigger module documentation) it looks like you have a handful of types and names wrong here (at least as of SDK 1.12.0). Changing the last two blocks of code to:
deleteInvoiceTrigger : Trigger ()
deleteInvoiceTrigger = Trigger
{ initialize = return ()
, updateState = \ _message -> return ()
, rule = deleteInvoiceRule
, registeredTemplates = RegisteredTemplates
[ registeredTemplate @Invoice
, registeredTemplate @PaymentConfirmation
]
, heartbeat = None
}
deleteInvoiceRule : Party -> TriggerA () ()
deleteInvoiceRule party = do
invoices <- query @Invoice
confirmations <- query @PaymentConfirmation
let ready = do
(confirmationCid, PaymentConfirmation{invoice, obligor}) <- confirmations
(invoiceCid, Invoice{}) <- invoices
guard $ invoiceCid == invoice
guard $ party == obligor
pure confirmationCid
forA_ ready $ \confirmationCid -> dedupExercise confirmationCid ArchiveInvoice
gets your file to compile for me. Specifically:
- The
initializefield expects a typeTriggerInitializeA s, whereas you are providing a functiona -> (). If you don’t care about carrying your own state (of types), you can create a value of typeTriggerInitializeA ()(i.e. setsto()) by writingreturn ()(orpure ()if that makes more sense to you;pureis literally defined bypure = return). - The
updateStatefield requires aMessage -> TriggerUpdateA s ()while you’re giving it aa -> b -> () -> (). To construct a no-opMessage -> TriggerUpdateA s ()(which seems to be what you want) you can write\_ -> return ()or, alternatively,const (return ()). - The type of a rule should be
Party -> TriggerA s (), whereas you re trying to give it aParty -> ACS -> Time -> Map CommandId [Command] -> () -> TriggerA (). So the type ofdeleteInvoiceRuleneeds to change toParty -> TriggerA () ()(thatsmust be the samesas ininitializeandupdateState, so in our case()).
This last point means deleteInvoiceRule is a function of a single argument, which raises the question of where you can get all the other things from (ACS, current time, commands in flight). On that front, it seems plausible you may be a little bit confused about do notation, so I’ll try to explain it here (though very briefly). A do block represents what we call an Action in Daml (hence the A at the end of the corresponding types). You can think of the do block as wrapping a computation for execution somewhere else, in some other context. Often in Daml that other context is the ledger, but here it is the trigger engine.
What does it mean that “the computation will be run in a context”? It means you can ask questions of the context, for example in this case “get me all the contracts in the current ACS” or “get me the current time”. Those questions are a bit special because they are asked of the context: they are not explicit parameters to the function. So they have a slightly different syntax, using the <- notation. For example, this line:
invoices <- query @Invoice
can be understood as "when this code runs in a context, query the context for all the contracts of type invoice, and store the result in the variable invoices". This is in contrast with a line like:
let a = 4 + 5
which represents a “pure” computation (of which the result is stored in a), i.e. one that is independent of the context.
Hope that helped. Don’t hesitate to ask further questions if anything is still unclear. Also, please do point us to wherever you got your code from, as it seems we may need to update it to match the latest version.
Thank you for the reference @Stephen!
I guess the tutorial is using an outdated process.
Thank you so much for the detailed response, @Gary_Verhaegen!
I’m cutting my teeth with a lot of these concepts so this is very helpful!
Link to the tutorial
Would you mind sharing a link to the tutorial you’re following, so we can make sure to update it?
If you’re looking for an up2date tutorial, the one in our documentation should always be updated to the latest SDK version.
Thanks, @cocreature!
Appreciate all the responses and enjoying learning more about Daml
Thank you so much for the detailed response, @Gary_Verhaegen!
I’m cutting my teeth with a lot of these concepts so this is very helpful!Link to the tutorial
Thanks for pointing this out, we’ll make sure to update it!
Hi @kirksudduth 
Sorry for experiencing the issue with the tutorial, my bad really (I was away for some time). We’re in the process of updating it.
@Gary_Verhaegen @Stephen @cocreature @anthony Thanks for all the help and pointing out that the tutorial needs to be fixed 
Hi @kirksudduth 
We’ve updated the tutorial with the working code. Thnx for your feedback!
Thank you @nemanja!
Learning more every day 