* Introduce a simpler template desugaring without support for generic templates This adapts the LF conversion to the new template desugaring introduced in our GHC fork. The guiding principle is that we use the typeclasses directly to avoid generating, typechecking and converting redundant code caused by indirections. I updated the template desugaring documentation so that is probably a good starting point for reviewing this. * Address review comments * Fix daml doc tests * Fix data dependency tests * Switch to new ghc-lib release
6.4 KiB
DAML template syntax desugaring
Copyright 2019 Digital Asset (Switzerland) GmbH and/or its affiliates. All Rights Reserved. SPDX-License-Identifier: (Apache-2.0 OR BSD-3-Clause)
Introduction
DAML syntax describes contracts and the choices that operate on them. When DAML syntax is interpreted, the first step is to parse it to Haskell abstract syntax trees.
This note shows how DAML examples are desugared by showing their Haskell source equivalents, and should help you understand the connection between DAML and Haskell.
How DAML syntax desugars
Example (1)
Here is a contract with one choice:
template Iou
with
issuer : Party
owner : Party
amount : Decimal
regulators : [Party]
where
ensure amount > 0.0
signatory issuer, owner
observer regulators
agreement show issuer <> " will pay " <> show owner <> " " <> show amount
choice Transfer : ContractId Iou
with
newOwner : Party
controller owner
do
create this with owner = newOwner
The class Template
(defined by the DAML standard library) represents the set of all contract types:
class Template t where
signatory : t -> [Party]
observer : t -> [Party]
ensure : t -> Bool
agreement : t -> Text
create : t -> Update (ContractId t)
fetch : ContractId t -> Update t
archive : ContractId t -> Update ()
toAnyTemplate : t -> AnyTemplate
fromAnyTemplate : AnyTemplate -> Optional t
In this example, t
is identified with Iou
. The rest of this section shows you how desugaring proceeds.
First we have data type definitions for the Iou
template and the Transfer
choice.
data Iou = Iou with
issuer : Party
owner : Party
amount : Decimal
regulators : [Party]
deriving (Eq, Show)
data Transfer = Transfer with
newOwner : Party
deriving (Eq, Show)
Next we have the instance of the Template
typeclass:
instance Template Iou where
ensure this@Iou {..} = amount > 0.0
agreement this@Iou {..}
= show issuer <> " will pay " <> show owner <> " " <> show amount
signatory this@Iou {..}
= concat
[concat
[toParties (owner),
toParties (issuer)]]
observer this@Iou {..}
= concat
[concat
[concat
[toParties (regulators)]]]
archive cid
= exercise cid Archive
create = magic @"create"
fetch = magic @"fetch"
toAnyTemplate = magic @"toAnyTemplate"
fromAnyTemplate = magic @"fromAnyTemplate"
_templateTypeRep = magic @"_templateTypeRep"
When a type t
is a Template
instance, class Choice
(defined by the DAML standard library) defines a (multi-parameter type class) relation on types t
, c
and r
such that r
is uniquely determined by the pair (t, c)
:
class Template t => Choice t c r | t c -> r where
exercise : ContractId t -> c -> Update r
_toAnyChoice : proxy t -> c -> Any
_fromAnyChoice : proxy t -> Any -> Optional c
In this example, c
is identified with Transfer
and r
with ContractId Iou
.
The instance
declaration establishes the triple (Iou, Transfer, ContractId Iou)
as satisfying the Choice
relation:
instance Choice Iou Transfer (ContractId Iou) where
exercise = magic @"exerciseIouTransfer"
_toAnyChoice = magic @"toAnyChoiceIouTransfer"
_fromAnyChoice
= magic @"fromAnyChoiceIouTransfer"
Information about a choice that is not part of the Choice
typeclass is recorded in a
separate top-level identifier. Specifically, this is a tuple containing the controller,
the choice body and information on whether the choice is pre-, post- or nonconsuming:
_choice_IouTransfer :
(Iou -> Transfer -> [Party],
ContractId Iou
-> Iou -> Transfer -> Update (ContractId Iou),
PreConsuming Iou)
_choice_IouTransfer
= (\ this@Iou {..} arg@Transfer {..}
-> let
in
concat
[toParties (owner)],
\ self this@Iou {..} arg@Transfer {..}
-> let
in do create (DA.Internal.Record.setField @"owner" newOwner this),
PreConsuming)
Example (2)
The next contract exercises the "contract keys" feature of DAML.
Contract key syntax desugars to instance
declarations of the following typeclass.
class Template t => TemplateKey t k | t -> k where
key : t -> k
fetchByKey : k -> Update (ContractId t, t)
lookupByKey : k -> Update (Optional (ContractId t))
_maintainer : proxy t -> k -> [Party]
_toAnyContractKey : proxy t -> k -> Any
_fromAnyContractKey : proxy t -> Any -> Optional ks
In the following Enrollment
contract, there are no choices but there are declarations of key
and maintainer
.
data Course =
Course with
institution : Party
title : Text
deriving (Show, Eq)
data Registration =
Registration with
student : Party
course : Course
year : Int
deriving (Show, Eq)
template Enrollment
with
reg : Registration
where
signatory reg.student, reg.course.institution
key reg : Registration
maintainer key.course.institution
The Course
and Registration
data types remain as they are, but the Enrollment
template results in several pieces after desugaring.
data Enrollment =
Enrollment with
reg : Registration
deriving (Show, Eq)
instance Template Enrollment where
signatory this@Enrollment {..}
= concat
[concat
[toParties
((DA.Internal.Record.getField
@"institution" (DA.Internal.Record.getField @"course" reg))),
toParties
((DA.Internal.Record.getField @"student" reg))]]
observer this@Enrollment {..} = concat []
ensure this@Enrollment {..} = True
agreement this@Enrollment {..} = ""
archive cid
= exercise cid Archive
create = magic @"create"
fetch = magic @"fetch"
toAnyTemplate = magic @"toAnyTemplate"
fromAnyTemplate = magic @"fromAnyTemplate"
_templateTypeRep = magic @"_templateTypeRep"
instance TemplateKey Enrollment Registration where
key this@Enrollment {..} = reg
_maintainer _ key
= concat
[concat
[toParties
((DA.Internal.Record.getField
@"institution" (DA.Internal.Record.getField @"course" key)))]]
fetchByKey = magic @"fetchByKey"
lookupByKey = magic @"lookupByKey"
_toAnyContractKey = magic @"_toAnyContractKey"
_fromAnyContractKey
= magic @"_fromAnyContractKey"