daml/ghc-lib/template-desugaring.md

222 lines
6.4 KiB
Markdown
Raw Normal View History

# Daml template syntax desugaring
2019-04-04 11:33:38 +03:00
Copyright (c) 2021 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.
2019-04-04 11:33:38 +03:00
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.
2019-04-04 11:33:38 +03:00
## How Daml syntax desugars
2019-04-04 11:33:38 +03:00
### 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:
2019-04-04 11:33:38 +03:00
```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"
```