How to Instantiate template with optional parameters

Hi, how can I instantiate a template & pass in the optional parameters?

eg in following ShipmentReceived template has customsInfo and fdaInfo as optional, but I am getting an error while calling the create ShipmentReceived as I do not have them to pass as arguments (Will set those later in workflow etc)

  controller order.purchaseOrderTerms.buyerAgent can 
      ReceiveShipment : ContractId ShipmentReceived 
        with 
          receiptText : Text 
          dateReceived : Date 
        do 
          create ShipmentReceived with receiptText, dateReceived, shippingInfo, order

-- ShipmentReceived
template ShipmentReceived
  with 
    order : Order
    shippingInfo : ShippingInfo
    receiptText : Text 
    dateReceived : Date 
    customsInfo : Optional CustomsInfo
    fdaInfo : Optional FDAInfo
 where 
    signatory order.purchaseOrderTerms.buyerAgent 
    controller order.purchaseOrderTerms.buyerAgent can 
      AddCustomsInfo : ContractId ShipmentReceived 
        with 
          customsInfo1 : CustomsInfo
        do 
          create this with customsInfo = customsInfo1
      AddFDAInfo : ContractId ShipmentReceived
2 Likes

Nevermind, Found solution is to use None

create ShipmentReceived with receiptText, dateReceived, shippingInfo, order, customsInfo = None, fdaInfo = None
4 Likes

Followup on this. Doesnt not look like the code is working. (ie. Optional field is NOT getting set)

Eg in the Scenario/init-script if I do

  let 
    customsInfo1 = CustomsInfo  with 
      reference  = "CustomsInfoRef"
      date  = DA.Date.date 2021 Jan 01
      name  = "CustomsInfo"

  shipmentReceived1 <- submit buyerAgent1 do exerciseCmd shipmentReceived1 AddCustomsInfo with customsInfo1=customsInfo1

shipmentReceived1 still shows customsInfo (None)

thoughts?

1 Like

Can you show the full code sample you are using now?

1 Like

Hope this helps…

----------------------------------------------------------
-- Shipment
----------------------------------------------------------
template ShipmentInitiated
  with 
    order : Order
    shippingInfo : ShippingInfo
  where 
    signatory order.quote.quoteInfo.seller 
    controller order.purchaseOrderTerms.buyerAgent can 
      ReceiveShipment : ContractId ShipmentReceived 
        with 
          receiptText : Text 
          dateReceived : Date 
        do 
          create ShipmentReceived with receiptText, dateReceived, shippingInfo, order, customsInfo = None, fdaInfo = None

----------------------------------------------------------
-- ShipmentReceived
----------------------------------------------------------
template ShipmentReceived
  with 
    order : Order
    shippingInfo : ShippingInfo
    receiptText : Text 
    dateReceived : Date 
    customsInfo : Optional CustomsInfo
    fdaInfo : Optional FDAInfo
 where 
    signatory order.purchaseOrderTerms.buyerAgent 
    controller order.purchaseOrderTerms.buyerAgent can 

      AddCustomsInfo : ContractId ShipmentReceived 
        with 
          customsInfoNew : CustomsInfo
        do 
          let
            this.customsInfo = customsInfoNew
          create ShipmentReceived with ..

      AddFDAInfo : ContractId ShipmentReceived 
        with 
          fdaInfoNew :FDAInfo
        do 
          let
            this.fdaInfo = fdaInfoNew
          create ShipmentReceived with ..

      ClearShipment : ContractId ShipmentCleared 
        with 
          reference : Text 
          dateCleared : Date 
        do 
          create ShipmentCleared with shipmentReceived=this, dateCleared, buyer = order.quote.quoteInfo.buyer, buyerAgent = order.purchaseOrderTerms.buyerAgent  

setup : Script ()

  buyerAgent1 <- allocatePartyWithHint "BuyerAgent1" $ PartyIdHint with partyIdHint = "BuyerAgent1"
  buyerAgentInvite1 <-submit buyer1 do exerciseCmd buyerRole1 InviteBuyerAgent with buyerAgent = buyerAgent1
  buyerAgentRole1 <- submit buyerAgent1 do exerciseCmd buyerAgentInvite1 AcceptBuyerAgentInvitation
  
let 
    purchaseOrderTerms1 = PurchaseOrderTerms with
      buyerAgent = buyerAgent1

-- Buyer accepts Quote from Seller 2
  purchaseOrder1 <- submit buyer1 do exerciseCmd quote2 AcceptQuote with purchaseOrderTerms = purchaseOrderTerms1

--Seller accepts PO and Order is created
  order1 <- submit seller2 do exerciseCmd purchaseOrder1 AcceptPurchaseOrder with acceptText = "Accepted"

-- Seller can InitiateShipment
  let
    shippingInfo1 =  ShippingInfo with
      shippingDate  =  DA.Date.date 2021 Jan 01
      trackingNumber  = "Text"  
  
  shipmentInitiated1 <- submit seller2 do exerciseCmd order1 InitiateShipment with shippingInfo = shippingInfo1
  
-- BuyersAgent can ReceiveShipment
  shipmentReceived1 <- submit buyerAgent1 do exerciseCmd shipmentInitiated1 ReceiveShipment with receiptText = "Received", dateReceived = DA.Date.date 2021 Jan 01

-- Add CustomsInfo
  let 
    customsInfo1 = CustomsInfo  with 
      reference  = "CustomsInfoRef"
      date  = DA.Date.date 2021 Jan 01
      name  = "CustomsInfo"

  shipmentReceived1 <- submit buyerAgent1 do exerciseCmd shipmentReceived1 AddCustomsInfo with customsInfoNew=customsInfo1

-- Add FDA Info
  let 
    fdaInfo1 = FDAInfo  with 
      reference  = "FDAInfoRef"
      date  = DA.Date.date 2021 Jan 01
      name  = "FDAInfo"

  shipmentReceived1 <- submit buyerAgent1 do exerciseCmd shipmentReceived1 AddFDAInfo with fdaInfoNew=fdaInfo1
1 Like

Let’s start with a very simplified version of your example to illustrate the problem:

module Main where

import Daml.Script

data CustomsInfo = CustomsInfo
  deriving (Eq, Show)

template ShipmentReceived
  with
    p : Party
    customsInfo : Optional CustomsInfo
 where
    signatory p
    controller p can

      AddCustomsInfo : ContractId ShipmentReceived
        with
          customsInfoNew : CustomsInfo
        do
          let
            this.customsInfo = customsInfoNew
          create ShipmentReceived with ..

setup : Script ()
setup = do
  p <- allocatePartyWithHint "P" $ PartyIdHint with partyIdHint = "P"
  cid <- submit p (createCmd ShipmentReceived with p = p, customsInfo = None)
  shipments <- query @ShipmentReceived p
  debug shipments
  cid <- submit p (exerciseCmd cid (AddCustomsInfo CustomsInfo))
  shipments <- query @ShipmentReceived p
  debug shipments
  pure ()

As you said, this doesn’t work. We get the following output when running daml script:

[DA.Internal.Prelude:540]: [(<contract-id>,ShipmentReceived {p = 'P', customsInfo = None})]
[DA.Internal.Prelude:540]: [(<contract-id>,ShipmentReceived {p = 'P', customsInfo = None})]

Now what is going wrong here?
The crucial point is the following piece of code

          let
            this.customsInfo = customsInfoNew
          create ShipmentReceived with ..

If you remove a bit of syntactic sugar this is equivalent to

          let
            this.customsInfo = customsInfoNew
          create ShipmentReceived with p = p, customsInfo = customsInfo

Now the crucial questions is what does the customsInfo at the very end refer to.
To understand that we have to understand what the following line does.

let this.customsInfo = customsInfoNew

Remember that DAML is an immutable language. You cannot simply mutate a field in a template. This line isn’t mutating the customsInfo field so that customsInfo changes its meaning. customsInfo at the end still refers to the old value which is why you see the unchanged value.

To fix your code you have to actually change the value, e.g.,

createCmd ShipmentReceived with p = p, customsInfo = Some customsInfoNew

or if you want to use a let and .. you can use the following

-- Note that this does not mutate customsInfo. It defines a new variable of this name that hides the one from your template.
let customsInfo = Some customsInfoNew
createCmd ShipmentReceived with ..

If you now rebuild and rerun your script you will see the expected output:

[DA.Internal.Prelude:540]: [(<contract-id>,ShipmentReceived {p = 'P', customsInfo = None})]
[DA.Internal.Prelude:540]: [(<contract-id>,ShipmentReceived {p = 'P', customsInfo = Some CustomsInfo})]

Now there is one remaining piece of the puzzle. What does let this.customsInfo = customsInfoNew actually do? This is rather confusing: It defines an infix operator called . which accepts two arguments this and customsInfo and will return customsInfoNew. So you could call "hello" . "world" afterwards and it will give you back customsInfoNew.

1 Like

Thank you for the detailed explanation. (Code works as expected now :slight_smile: )

I have a variation of this problem

template SurgicalEvent
  with
   operator : Party  
   hospital : Party
   emr: Text
   patientname: Text   
   patientdob : Date
   eventdate : Date
   eventdescription : Text
   surgeonname : Text
   surgicaleventproductdata : Optional [SurgicalEventProductdata]

  where
   signatory operator
   observer hospital 

   controller operator can
     nonconsuming AddSurgicalEventData : ContractId SurgicalEvent
      with
       newproductdata : SurgicalEventProductdata

      do
        archive self
        create this with surgicaleventproductdata = newproductdata :: surgicaleventproductdata

I tried to introduce “Some”

 newproductdata : Some SurgicalEventProductdata

But errors occur

1 Like

Some is not a type constructor. The type you are looking for is Optional SurgicalEventProductData which has the values None and Some productData for productData : SurgicalEventProductData.

1 Like

If I have this

template SurgicalEvent
  with
   operator : Party  
   hospital : Party
   emr: Text
   patientname: Text   
   patientdob : Date
   eventdate : Date
   eventdescription : Text
   surgeonname : Text
   surgicaleventproductdata : Optional [SurgicalEventProductdata]
  where
   signatory operator
   observer hospital 
   controller operator can
     nonconsuming AddSurgicalEventData : ContractId SurgicalEvent
      with
       newproductdata: Optional SurgicalEventProductdata
      do
        archive self
        create this with surgicaleventproductdata = newproductdata :: surgicaleventproductdata

get this error code in last line

C:\Users\bartc\Documents\Github\Loci\daml\SurgicalEvent.daml:30:9: error:
    • Couldn't match type ‘Optional [SurgicalEventProductdata]’
                     with ‘[Optional SurgicalEventProductdata]’
        arising from a functional dependency between:
          constraint ‘DA.Internal.Record.HasField
                        "surgicaleventproductdata"
                        SurgicalEvent
                        [Optional SurgicalEventProductdata]’
            arising from a use of ‘DA.Internal.Record.setField’
          instance ‘DA.Internal.Record.HasField
                      "surgicaleventproductdata"
                      SurgicalEvent
                      (Optional [SurgicalEventProductdata])’
            at <no location info>
    • In the first argument of ‘create’, namely
        ‘(DA.Internal.Record.setField
            @"surgicaleventproductdata"
            newproductdata :: surgicaleventproductdata
            this)’
      In a stmt of a 'do' block:
        create
          (DA.Internal.Record.setField
             @"surgicaleventproductdata"
             newproductdata :: surgicaleventproductdata
             this)
      In the expression:
        do archive self
           create
             (DA.Internal.Record.setField
                @"surgicaleventproductdata"
                newproductdata :: surgicaleventproductdata
                this)typecheck
1 Like

The issue is that you have an Optional [a] and you’re trying to add a Optional a to the front which doesn’t typecheck since Optional [a] is not a list. There are various options depending on your requirements. One solution would be to just change the type to [Optional a] which should make your example typecheck. Another would be to change it to [a] and do nothing if a None gets passed into AddSurgicalEventData. If you don’t want to change the types you have to define what you want to happen in the case where either the new or the old value is None.

Here is a solution for the second option:

template SurgicalEvent
  with
   operator : Party
   hospital : Party
   surgicaleventproductdata : [SurgicalEventProductdata]
  where
   signatory operator
   observer hospital
   controller operator can
     nonconsuming AddSurgicalEventData : ContractId SurgicalEvent
      with
       optnewproductdata: Optional SurgicalEventProductdata
      do
        archive self
        case optnewproductdata of
          None -> pure self
          Some productdata -> create this with surgicaleventproductdata = productdata :: surgicaleventproductdata

Side note: Please use code blocks surrounded by triple backticks ``` instead of quote blocks where each line starts with >. The latter strips all indentation which makes code examples very hard to read.

Such as:

```
my code here
```

Here is some updated template code where I keep the 2 data elements the same type

template SurgicalEvent
  with
   operator : Party  
   hospital : Party
   emr: Text
   patientname: Text   
   patientdob : Date
   eventdate : Date
   eventdescription : Text
   surgeonname : Text
   surgicaleventproductdata : Optional [SurgicalEventProductdata]

  where
   signatory operator
   observer hospital 

   controller operator can
     nonconsuming AddSurgicalEventData : ContractId SurgicalEvent
      with
       newproductdata : Optional [SurgicalEventProductdata]

      do
        archive self
        case surgicaleventproductdata of
          None -> create this with surgicaleventproductdata = newproductdata
          Some surgicaleventproductdata -> 
           case newproductdata of
              None -> create this
              Some newproductdata ->  create this with surgicaleventproductdata = newproductdata :: surgicaleventproductdata
          

This results in the following error on the last line…

C:\Users\bartc\Documents\Github\Loci\daml\SurgicalEvent.daml:39:39: error:
    • Couldn't match type ‘Optional [SurgicalEventProductdata]’
                     with ‘[[SurgicalEventProductdata]]’
        arising from a functional dependency between:
          constraint ‘DA.Internal.Record.HasField
                        "surgicaleventproductdata"
                        SurgicalEvent
                        [[SurgicalEventProductdata]]’
            arising from a use of ‘DA.Internal.Record.setField’
          instance ‘DA.Internal.Record.HasField
                      "surgicaleventproductdata"
                      SurgicalEvent
                      (Optional [SurgicalEventProductdata])’
            at <no location info>
    • In the first argument of ‘create’, namely
        ‘(DA.Internal.Record.setField
            @"surgicaleventproductdata"
            newproductdata :: surgicaleventproductdata
            this)’
      In the expression:
        create
          (DA.Internal.Record.setField
             @"surgicaleventproductdata"
             newproductdata :: surgicaleventproductdata
             this)
      In a case alternative:
          Some newproductdata
            -> create
                 (DA.Internal.Record.setField
                    @"surgicaleventproductdata"
                    newproductdata :: surgicaleventproductdata
                    this)typecheck
Peek Problem (Alt+F8)
No quick fixes available

:: prepends a single element to a list. If you want to concatenate 2 lists, you need to use ++. However, in your example, you don’t have 2 lists. You have 2 Optional [a]. That gives you 4 cases to considner:

surgicaleventproductdata newproductdata result
Some(xs) Some(ys) Some(xs ++ ys)
None Some(ys) What do you want here?
Some(xs) None What do you want here?
None None What do you want here?

A common solution is to choose None as the answer for the last 3. You can get that as follows:

liftA2 (++) surgicaleventproductdata newproductdata

However, looking at your example I expect you might have something else in mind?

It is also useful to check if there is really a difference between None and Some []. If not, there is no need to use Optional at all here.

1 Like

Elaborating on that last sentence, i.e. not using Optional at all and representing “no surgical data” as an empty list, you would write it as:

module Main where

data SurgicalEventProductData = SurgicalEventProductData
  deriving (Eq, Show)

template SurgicalEvent
  with
   operator : Party  
   hospital : Party
   emr: Text
   patientname: Text   
   patientdob : Date
   eventdate : Date
   eventdescription : Text
   surgeonname : Text
   surgicaleventproductdata : [SurgicalEventProductData]
  where
   signatory operator
   observer hospital 
   controller operator can
     nonconsuming AddSurgicalEventData : ContractId SurgicalEvent
      with
       newproductdata: SurgicalEventProductData
      do
        archive self
        create this with surgicaleventproductdata = newproductdata :: surgicaleventproductdata

This typechecks, and could be what you want depending on the broader context.

1 Like