Hello, I am new to Haskell / DAML, and have a basic question.
Below is some psuedo code which illustrates what I want to achieve… i.e. I want some kind of overloading which allow a single template to support different product catelogue. Would you please shred me some light on how can I achieve it in DAML?
Many thanks in advance!
data ComAProductCode = A1 | A2 | A3 deriving (Eq,Show)
data ComBProductCode = B1 | B2 | B3 deriving (Eq,Show)
data PurchaseOrderForComAorComB a = PurchaseOrderForComAorComB with
code : a
quantity : Int
deriving (Eq,Show)
template PurchaseOrderRequest
with
buyer: Party
seller: Party
PurchaseOrderForComAorComB : PurchaseOrderForComAorComB a
where
signatory buyer
observer seller
controller buyer can
nonconsuming Create: ContractId PurchaseOrderRequest
with
purchaseOrder: PurchaseOrderFromComAorComB
do
Hi @a14843, welcome to the forum! DAML currently doesn’t support type parameters at the template level so you have a few options:
Declare a ProductCode variant type that covers both A and B: data ProductCode = PCComA ComAProductCode | PCComB ComBProductCode.
You can then replace your parameter a with ProductCode. The downside of this approach is that ProductCode is a static list. Ie if a company C joins, you can’t easily extend this.
Define a template per company: template PurchaseOrderRequestComA and template PurchaseOrderRequestComB. This has the obvious downside that consumers of PurchanseOrderRequest also need to be replicated per company.
Type your product code weakly. Ie store it as a Text, not an enum.
It is also worth noting that this restriction only applies to Templates. The rest of your DAML code can use PurchaseOrderForComAorComB just fine. It’s only when writing data to the ledger that DAML currently needs to know the precise concrete type for its data.
So generally we use option 1 from @bernhard’s answer above in the template itself, and then either use that sum type for all the code, or define a function to convert it to the generic form in any choice that needs to use it.
data ComAProductCode = A1 | A2 | A3
deriving(Eq, Show)
data ComBProductCode = B1 | B2 | B3
deriving(Eq, Show)
data AllProductCode = PCComA ComAProductCode | PCComB ComBProductCode
deriving(Eq, Show)
…
Somewhere inside a template, I refer it:
controller receipent can
PurchaseOrderRequest_Accept: ContractId Invoice
do
now <- getTime
let
costForInsurance : Optional Decimal = if
purchaseOrder.shippingAgreement == CIF &&
purchaseOrder.productCode == A1
then Some 3.0
else None
The line “purchaseOrder.productCode == A1” gives me an error:
• Couldn't match type ‘AllProductCode’ with ‘ComAProductCode’
arising from a functional dependency between:
constraint ‘DA.Internal.Record.HasField
"productCode" PurchaseOrder ComAProductCode’
arising from a use of ‘DA.Internal.Record.getField’
instance ‘DA.Internal.Record.HasField
"productCode" PurchaseOrder AllProductCode’
at <no location info>
• In the first argument of ‘(==)’, namely
‘(DA.Internal.Record.getField @"productCode" purchaseOrder)’
In the second argument of ‘(&&)’, namely
‘(DA.Internal.Record.getField @"productCode" purchaseOrder) == A1’
In the expression:
(DA.Internal.Record.getField @"shippingAgreement" purchaseOrder)
== CIF
&& (DA.Internal.Record.getField @"productCode" purchaseOrder) == A1 typecheck