Guys, I have a rookie Daml question I hope someone can help me with. The functionality I need to implement is as follows. I have a template named Asset with a field “amount : Decimal”. In another template I need to create a choice that takes [ContractId Asset] as argument, loops through the list, fetches each contract and calculates the sum of the values of the amount field. Can anyone provide a quick example? I’ve tried a bunch of ways I could think of, but I can’t make it work. I always run into typecheck problems. E.g. I tried using the following function inside choice body
let fetchAssetAmount aCid = do
a <- fetch aCid
return a.amount
The function signature is
Contract Id r → Update b. In other words, it returns an action. I want it to return a Decimal, so I could use it in foldl. I don’t understand why the function returns an action and not the value provided using the “return” keyword.
Since the function does return an action, I tried using it with foldlA, but I run into typecheck error again. Here’s a complete code snippet:
template Asset
with
amount : Decimal
owner : Party
where
signatory: owner
template Bla
with
owner : Party
where
signatory : Party
nonconsuming choice Create_New_Asset : ContractId Asset
with
cids : [ContractId Asset]
controller owner
do
let fetchAssetAmount aCid = do
a <- fetch aCid
return a.amount
totAssetAmount <- foldl1A fetchAssetAmount assetsCids
create Asset with
owner
amount = totAssetAmount
In the above the compiler complaints about typecheck error within fetchAssetAmount in the statement “totAssetAmount ← foldl1A fetchAssetAmount assetsCids”
module Main where
import DA.Action
type AssetId = ContractId Asset
template Asset
with
amount : Decimal
owner : Party
where
signatory owner
template Bla
with
owner : Party
where
signatory owner
nonconsuming choice Create_New_Asset : ContractId Asset
with
cids : [ContractId Asset]
controller owner
do
let fetchAssetAmount acc aCid = do
a <- fetch aCid
return $ a.amount + acc
totAssetAmount <- foldlA fetchAssetAmount 0.0 cids
create Asset with
owner
amount = totAssetAmount
A few issues:
You have some small syntax errors, e.g., signatory : owner instead of signatory owner. Just look at what your IDE complains about and they should be easy enough to sort out.
assetCids needs to be cids, otherwise you get an out of scope error.
Now you get to the interesting part: First, foldl1A crashes on empty lists. This is sometimes acceptable if you don’t have a good way to handle an empty list. However a sum has a trivial empty case with 0 so let’s use foldlA and use 0.0 as the base.
Lastly, you are missing the accumulator. your folding function needs to take the accumulator so far (in your example the sum) and combine it with the new element (by adding the two).
A slightly more idiomatic version might look like this:
foldl1A expects an a -> a -> m a, but the function you’re passing has the shape ContractId Asset -> Update Decimal. There is no way to assign types to the variables a and m such that fetchAssetAmount will fit.
The hint about the mismatch here is those two arguments. You’re passing a list of arguments to call fetchAssetAmount with, but expecting them to percolate down to a single result. As with most fold functions, the question you’re being asked is “how do I combine these values?”
Another problem is specific to the 1 variants of these folding functions: the two input types a must match the output type a. Since your input type is ContractId Asset and output type is Decimal, there’s no way to make that make sense; what would each step mean, if it discarded the Decimal from the prior step and expected you to figure the result from twoContractId Assets?
You can solve these issues by using foldlA instead.
Our initial value is 33.0, used as the starting point.
Our rule for combining the values is to multiply the new number at each step and add 42.
let fetchAssetAmount acc aCid = do
a <- fetch aCid
return (acc * a.amount + 42.0)
totAssetAmount <- foldlA fetchAssetAmount 33.0 cids
@Stephen, thank you very much for the explanation. I now see the difference in the output type between foldlA and foldl1A, which wasn’t clear to me previously. @cocreature, thank you for correcting my errors and for providing an idiomatic example. Much appreciated.