daml/ghc-lib/template-desugaring.md

15 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 : Float
    regulators : [Party]
  where
    ensure amount > 0
    signatory issuer, owner
    observer regulators
    agreement issuer <> " will pay " <> 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 a class IouInstance with the bulk of the definitions we will need.

class IouInstance where
  _signatoryIou : Iou -> [Party]
  _signatoryIou this@Iou{..} = [issuer, owner]
  _observerIou : Iou -> [Party]
  _observerIou this@Iou{..} = regulators
  _ensureIou : Iou -> Bool
  _ensureIou this@Iou{..} = amount > 0.0
  _agreementIou : Iou -> Text
  _agreementIou this@Iou{..} = show issuer <> " will pay " <> show owner <> " " <> show amount
  _createIou : Iou -> Update (ContractId Iou)
  _createIou = magic @"create"
  _fetchIou : ContractId Iou -> Update Iou
  _fetchIou = magic @"fetch"
  _archiveIou : ContractId Iou -> Update ()
  _archiveIou cid = exerciseIouArchive cid Archive
  _toAnyTemplateIou : Iou -> AnyTemplate
  _toAnyTemplateIou = magic @"toAnyTemplate"
  _fromAnyTemplateIou : AnyTemplate -> Optional Iou
  _fromAnyTemplateIou = magic @"fromAnyTemplate"

  _consumptionIouArchive : PreConsuming Iou
  _consumptionIouArchive = PreConsuming
  _controllerIouArchive : Iou -> Archive -> [Party]
  _controllerIouArchive this@Iou{..} arg@Archive = signatoryIou this
  _actionIouArchive : ContractId Iou -> Iou -> Archive -> Update ()
  _actionIouArchive self this@Iou{..} arg@Archive = pure ()
  _exerciseIouArchive : ContractId Iou -> Archive -> Update ()
  _exerciseIouArchive = magic @"archive"
  _toAnyChoiceIouArchive : proxy Iou -> Archive -> AnyChoice
  _toAnyChoiceIouArchive = magic @"toAnyChoice"
  _fromAnyChoiceIouArchive : proxy Iou -> AnyChoice -> Optional Archive
  _fromAnyChoiceIouArchive = magic @"fromAnyChoice"

  _consumptionIouTransfer : PreConsuming Iou
  _consumptionIouTransfer = PreConsuming
  _controllerIouTransfer : Iou -> Transfer -> [Party]
  _controllerIouTransfer this@Iou{..} arg@Transfer{..} = [owner]
  _actionIouTransfer : ContractId Iou -> Iou -> Transfer -> Update (ContractId Iou)
  _actionIouTransfer self this@Iou{..} arg@Transfer{..} = create this with owner = newOwner
  _exerciseIouTransfer : ContractId Iou -> Transfer -> Update (ContractId Iou)
  _exerciseIouTransfer = magic @"exercise"
  _toAnyChoiceIouTransfer : proxy Iou -> Transfer -> AnyChoice
  _toAnyChoiceIouTransfer = magic @"toAnyChoice"
  _fromAnyChoiceIouTransfer : proxy Iou -> AnyChoice -> Optional Transfer
  _fromAnyChoiceIouTransfer = magic @"fromAnyChoice"

With that class defined, we can define an instance declaration for Iou to declare its membership in Template:

instance IouInstance => Template Iou where
  signatory = _signatoryIou
  observer = _observerIou
  ensure = _ensureIou
  agreement = _agreementIou
  create = _createIou
  fetch = _fetchIou
  archive = _archiveIou
  toAnyTemplate = _toAnyTemplate
  fromAnyTemplate = _fromAnyTemplate

instance IouInstance

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 -> AnyChoice
  _fromAnyChoice : proxy t -> AnyChoice -> 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 = _exerciseIouTransfer
  _toAnyChoice = _toAnyChoiceIouTransfer
  _fromAnyChoice = _fromAnyChoiceIouTransfer

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))

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)

class EnrollmentInstance where
  _signatoryEnrollment : Enrollment -> [Party]
  _signatoryEnrollment this@Enrollment{..} = [reg.student, reg.course.institution]
  _observerEnrollment : Enrollment -> [Party]
  _observerEnrollment this@Enrollment{..} = []
  _ensureEnrollment : Enrollment -> Bool
  _ensureEnrollment this@Enrollment{..} = True
  _agreementEnrollment : Enrollment -> Text
  _agreementEnrollment this@Enrollment{..} = ""
  _createEnrollment : Enrollment -> Update (ContractId Enrollment)
  _createEnrollment = magic @"create"
  _fetchEnrollment : ContractId Enrollment -> Update Enrollment
  _fetchEnrollment = magic @"fetch"
  _archiveEnrollment : ContractId Enrollment -> Update ()
  _archiveEnrollment cid = exerciseEnrollmentArchive cid Archive
  _toAnyTemplateEnrollment : Enrollment -> AnyTemplate
  _toAnyTemplateEnrollment = magic @"toAnyTemplate"
  _fromAnyTemplateEnrollment : AnyTemplate -> Optional Enrollment
  _fromAnyTemplateEnrollment = magic @"fromAnyTemplate"

  _hasKeyEnrollment : HasKey Enrollment
  _hasKeyEnrollment = HasKey
  _keyEnrollment : Enrollment -> Registration
  _keyEnrollment this@Enrollment{..} = reg
  _maintainerEnrollment : HasKey Enrollment -> Registration -> [Party]
  _maintainerEnrollment HasKey key = [key.course.institution]
  _fetchByKeyEnrollment : Registration -> Update (ContractId Enrollment, Enrollment)
  _fetchByKeyEnrollment = magic @"fetchByKey"
  _lookupByKeyEnrollment : Registration -> Update (Optional (ContractId Enrollment))
  _lookupByKeyEnrollment = magic @"lookupByKey"

  _consumptionEnrollmentArchive : PreConsuming Enrollment
  _consumptionEnrollmentArchive = PreConsuming
  _controllerEnrollmentArchive : Enrollment -> Archive -> [Party]
  _controllerEnrollmentArchive this@Enrollment{..} arg@Archive = signatoryEnrollment this
  _actionEnrollmentArchive : ContractId Enrollment -> Enrollment -> Archive -> Update ()
  _actionEnrollmentArchive self this@Enrollment{..} arg@Archive = pure ()
  _exerciseEnrollmentArchive : ContractId Enrollment -> Archive -> Update ()
  _exerciseEnrollmentArchive = magic @"archive"
  _toAnyChoiceEnrollmentArchive : proxy Enrollment -> Archive -> AnyChoice
  _toAnyChoiceEnrollmentArchive = magic @"toAnyChoice"
  _fromAnyChoiceEnrollmentArchive : proxy Enrollment -> AnyChoice -> Optional Archive
  _fromAnyChoiceEnrollmentArchive = magic @"fromAnyChoice"

instance EnrollmentInstance

instance EnrollmentInstance => Template Enrollment where
  signatory = _signatoryEnrollment
  observer = _observerEnrollment
  ensure = _ensureEnrollment
  agreement = _agreementEnrollment
  create = _createEnrollment
  fetch = _fetchEnrollment
  archive = _archiveEnrollment
  toAnyTemplate = _toAnyTemplateEnrollment
  fromAnyTemplate = _fromAnyTemplateEnrollment

instance TemplateKey Enrollment Registration where
  key = _keyEnrollment
  fetchByKey = _fetchByKeyEnrollment
  lookupByKey = _lookupByKeyEnrollment
  maintainer = _maintainerEnrollment (_hasKeyEnrollment : HasKey Enrollment)

Example (3)

The final example shows a generic proposal template.

template Template t => Proposal t with
    asset : t
    receivers : [Party]
    name : Text
  where
    signatory (signatory asset \\ receivers)
    observer receivers
    key (signatory this, name)
    maintainer (fst key)
    choice Accept : ContractId t
      controller receivers
      do
        create asset

Notice that the Proposal template has a type argument t with a Template constraint preceding it. We also specify a primary key for the Proposal template by combining data from the underlying template as well as the proposal. This desugars to the following declarations.

data Proposal t = Proposal with
    asset : t
    receivers : [Party]
    name : Party
  deriving (Eq, Show)

data Accept = Accept with
  deriving (Eq, Show)

class Template t => ProposalInstance t where
    _signatoryProposal : Proposal t -> [Party]
    _signatoryProposal this@Proposal{..} = signatory asset \\ receivers
    _observerProposal : Proposal t -> [Party]
    _observerProposal this@Proposal{..} = receivers
    _ensureProposal : Proposal t -> Bool
    _ensureProposal this@Proposal{..} = True
    _agreementProposal : Proposal t -> Text
    _agreementProposal this@Proposal{..} = ""
    _createProposal : Proposal t -> Update (ContractId (Proposal t))
    _createProposal = magic @"create"
    _fetchProposal : ContractId (Proposal t) -> Update (Proposal t)
    _fetchProposal = magic @"fetch"
    _archiveProposal : ContractId (Proposal t) -> Update ()
    _archiveProposal cid = exerciseProposalArchive cid Archive
    _toAnyTemplateProposal : Proposal t -> AnyTemplate
    _toAnyTemplateProposal = magic @"toAnyTemplate"
    _fromAnyTemplateProposal : AnyTemplate -> Optional (Proposal t)
    _fromAnyTemplateProposal = magic @"fromAnyTemplate"

    _hasKeyProposal : HasKey (Proposal t)
    _hasKeyProposal = HasKey
    _keyProposal : Proposal t -> ([Party], Text)
    _keyProposal this@Proposal{..} = (signatory this, name)
    _maintainerProposal : HasKey (Proposal t) -> ([Party], Text) -> [Party]
    _maintainerProposal HasKey key = fst key
    _fetchByKeyProposal : ([Party], Text) -> Update (ContractId (Proposal t), Proposal t)
    _fetchByKeyProposal = magic @"fetchByKey"
    _lookupByKeyProposal : ([Party], Text) -> Update (Optional (ContractId (Proposal t)))
    _lookupByKeyProposal = magic @"lookupByKey"

    _consumptionProposalArchive : PreConsuming (Proposal t)
    _consumptionProposalArchive = PreConsuming
    _controllerProposalArchive : Proposal t -> Archive -> [Party]
    _controllerProposalArchive this@Proposal{..} arg@Archive = signatoryProposal this
    _actionProposalArchive : ContractId (Proposal t) -> Proposal t -> Archive -> Update ()
    _actionProposalArchive self this@Proposal{..} arg@Archive = pure ()
    _exerciseProposalArchive : ContractId (Proposal t) -> Archive -> Update ()
    _exerciseProposalArchive = magic @"archive"
    _toAnyChoiceProposalArchive : proxy (Proposal t) -> Archive -> AnyChoice
    _toAnyChoiceProposalArchive = magic @"toAnyChoice"
    _fromAnyChoiceProposalArchive : proxy (Proposal t) -> AnyChoice -> Optional Archive
    _fromAnyChoiceProposalArchive = magic @"fromAnyChoice"

    _consumptionProposalAccept : PreConsuming (Proposal t)
    _consumptionProposalAccept = PreConsuming
    _controllerProposalAccept : Proposal t -> Accept -> [Party]
    _controllerProposalAccept this@Proposal{..} arg@Accept = receivers
    _actionProposalAccept : ContractId (Proposal t) -> Proposal t -> Accept -> Update (ContractId t)
    _actionProposalAccept self this@Proposal{..} arg@Accept = do
        create asset
    _exerciseProposalAccept : ContractId (Proposal t) -> Accept -> Update (ContractId t)
    _exerciseProposalAccept = magic @"exercise"
    _toAnyChoiceProposalAccept : proxy (Proposal t) -> Accept -> AnyChoice
    _toAnyChoiceProposalAccept = magic @"toAnyChoice"
    _fromAnyChoiceProposalAccept : proxy (Proposal t) -> AnyChoice -> Optional Accept
    _fromAnyChoiceProposalAccept = magic @"fromAnyChoice"

instance ProposalInstance t => Template (Proposal t) where
    signatory = _signatoryProposal
    observer = _observerProposal
    ensure = _ensureProposal
    agreement = _agreementProposal
    create = _createProposal
    fetch = _fetchProposal
    archive = _archiveProposal
    toAnyTemplate = _toAnyTemplate
    fromAnyTemplate = _fromAnyTemplate

instance ProposalInstance t => TemplateKey (Proposal t) ([Party], Text) where
    key = _keyProposal
    fetchByKey = _fetchByKeyProposal
    lookupByKey = _lookupByKeyProposal

instance ProposalInstance t => Choice (Proposal t) Accept (ContractId t) where
    exercise = _exerciseProposalAccept
    _toAnyChoice = _toAnyChoiceProposalAccept
    _fromAnyChoice = _fromAnyChoiceProposalAccept

instance ProposalInstance t => Choice (Proposal t) Archive () where
    exercise = exerciseProposalArchive
    _toAnyChoice = _toAnyChoiceProposalArchive
    _fromAnyChoice = _fromAnyChoiceProposalArchive

Example (3)(cont)

We showed the generic proposal template above, but have not showed what an instance looks like. Let's instantiate the Proposal template with the Iou (concrete) template from Example 1. This is done using the syntax below.

template instance ProposalIou = Proposal Iou

This allows us to create and exercise choices on a proposal contract instantiated to an Iou contract. The name ProposalIou is not needed in DAML code but is required when creating contracts via the Ledger API (as client languages may not be able to express generic template and type instantiation). The template instance desugars to the following declarations.

type ProposalIou = Proposal Iou
instance ProposalInstance Iou

The instance here simply leverages the implementation of the ProposalInstance class.