daml/ghc-lib/template-desugaring.md

222 lines
6.4 KiB
Markdown
Raw Normal View History

2019-04-04 11:33:38 +03:00
# DAML template syntax desugaring
Copyright 2020 Digital Asset (Switzerland) GmbH and/or its affiliates. All Rights Reserved.
2019-04-04 11:33:38 +03:00
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:
```haskell
template Iou
with
issuer : Party
owner : Party
amount : Decimal
2019-04-04 11:33:38 +03:00
regulators : [Party]
where
ensure amount > 0.0
signatory issuer, owner
2019-04-04 11:33:38 +03:00
observer regulators
agreement show issuer <> " will pay " <> show owner <> " " <> show amount
2019-04-04 11:33:38 +03:00
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:
```haskell
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
2019-04-04 11:33:38 +03:00
```
In this example, `t` is identified with `Iou`. The rest of this section shows you how desugaring proceeds.
2019-04-04 11:33:38 +03:00
First we have data type definitions for the `Iou` template and the `Transfer` choice.
2019-04-04 11:33:38 +03:00
```haskell
data Iou = Iou with
2019-04-04 11:33:38 +03:00
issuer : Party
owner : Party
amount : Decimal
regulators : [Party]
deriving (Eq, Show)
2019-04-04 11:33:38 +03:00
data Transfer = Transfer with
newOwner : Party
deriving (Eq, Show)
2019-04-04 11:33:38 +03:00
```
Next we have the instance of the `Template` typeclass:
2019-04-04 11:33:38 +03:00
```haskell
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"
2019-04-04 11:33:38 +03:00
```
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)`:
2019-04-04 11:33:38 +03:00
```haskell
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
2019-04-04 11:33:38 +03:00
```
In this example, `c` is identified with `Transfer` and `r` with `ContractId Iou`.
2019-04-04 11:33:38 +03:00
The `instance` declaration establishes the triple `(Iou, Transfer, ContractId Iou)` as satisfying the `Choice` relation:
2019-04-04 11:33:38 +03:00
```haskell
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)
2019-04-04 11:33:38 +03:00
```
### Example (2)
2019-04-04 11:33:38 +03:00
The next contract exercises the "contract keys" feature of DAML.
Contract key syntax desugars to `instance` declarations of the following typeclass.
2019-04-04 11:33:38 +03:00
```haskell
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
2019-04-04 11:33:38 +03:00
```
In the following `Enrollment` contract, there are no choices but there are declarations of `key` and `maintainer`.
```haskell
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.
```haskell
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"
```