Generic Templates¶
Sometimes different DAML templates have a common structure. Typically this occurs when there is some logic (usually in choices) that can be applied to many different underlying contracts. Generic templates allow you to capture this logic in a single place, instead of having to duplicate it for every template. Let’s see a simple example to understand what this means.
Example: Generic Proposal¶
Suppose we want to model a propose and accept workflow. This means that a party can propose a contract to a specific party, who may accept it with the terms of that contract. We see this pattern occur frequently for different types of contracts. Of course, we could implement one proposal template for every underlying template. However this is tedious and error prone. Alternatively, we can write it once and for all using a generic template.
This is how the generic Proposal
template looks in DAML.
template Template t => Proposal t
with
asset : t
receiver : Party
where
signatory (signatory asset \\ [receiver])
controller receiver can
Accept : ContractId t
do create asset
There are several things to notice in this short template.
Firstly, where we usually see a template name, there is now a more general template header Template t => Proposal t
. This can include multiple type parameters and constraints on those types. In this case Proposal
takes a single type parameter t
representing the type of the underlying asset. The Template
constraint says that t
is not just any type but a contract template with signatories, choices and more.
Secondly, the asset
parameter to the template has the abstract type t
. We don’t know anything about asset
other than that it is an instance of Template
. However this is all we need to implement the proposal template.
Notice that the signatories of the proposal are obtained from the signatories of the underlying contract. This is done by calling the overloaded signatory
method. In this case we exclude the receiving party, as this is the one whose authorization we want to gain from accepting the proposal.
Finally let’s look at the Accept
choice which characterizes the propose and accept workflow. The receiver can Accept
which results in a contract of the underlying asset type being created. We are able to call create
on the asset since we know it satisfies the Template
constraint.
Template Instances¶
The above template soundly represents the proposal workflow, but we have not yet used it on a concrete (non-generic) template. We call the concrete instantiation a template instance.
Let’s introduce a very simple Coin
contract that we can use in the proposal.
template Coin
with
issuer : Party
owner : Party
where
signatory issuer, owner
We would like to model an issuing party (e.g. a bank) proposing a coin contract for an individual to accept. To do this, we need to explicitly state our intention to use Coin
in a Proposal
. We do this using the template instance
syntax.
template instance CoinProposal = Proposal Coin
Note that we must choose a name, here CoinProposal
, for the template instance. It serves two purposes. First, it provides a type synonym for the underlying type, here Proposal Coin
. Second, it must be used to create contracts of this template in some client languages (for example when using the Java ledger bindings).
With a template instance in place, we can create and exercise choices on contracts of this type.
coinIssuance = scenario do
alice <- getParty "alice"
bank <- getParty "bank"
let coin = Coin with issuer = bank; owner = alice
let proposal = Proposal with asset = coin; receiver = alice
propId <- bank `submit` create proposal
alice `submit` exercise propId Accept
We construct the underlying asset and the proposal data using the Coin
and Proposal
data constructors respectively. The bank is able to create the coin proposal which Alice can then accept. This scenario results in two contract creations: first the proposal contract, which is consumed to give rise to the coin contract.