daml/ghc-lib/template-desugaring.md
Moritz Kiefer 75c9b1bf91
Simplify template desugaring (#3670)
* 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
2019-11-29 16:13:15 +01:00

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"