Overview: Template Structure
This page covers what a template looks like: what parts of a template there are, and where they go. For the structure of a Daml file outside a template, seefile-structure.
Template Outline Structure
Here’s the structure of a Daml template:template keyword
parameters
with followed by the names of parameters and their types
template body
where keyword
Can include:
template-local definitions (deprecated)
let keyword
Lets you make definitions that have access to the contract arguments and are available in the rest of the template definition.
signatories
signatory keyword
Required. The parties (see the Party type) who must consent to the creation of this contract. You won’t be able to create this contract until all of these parties have authorized it.
observers
observer keyword
Optional. Parties that aren’t signatories but who you still want to be able to see this contract.
a precondition
ensure keyword
Only create the contract if the conditions after ensure evaluate to true.
a contract key
key keyword
Optional. Lets you specify a combination of a party and other data that uniquely identifies a contract of this template. See Contract Keys and Maintainers.
maintainers
maintainer keyword
Required if you have specified a key. Keys are only unique to a maintainer. See Contract Keys and Maintainers.
choices
choice NameOfChoice : ReturnType controller nameOfParty do
Defines choices that can be exercised. See Choice structure for what can go in a choice.
Choice Structure
Here is the structure of a choice inside a template:preconsuming, postconsuming, nonconsuming, which changes the behavior of the choice with respect to privacy and if and when the contract is archived. See contract consumption in choices for more details.
a name
Must begin with a capital letter. Must be unique - choices in different templates can’t have the same name.
a return type
after a :, the return type of the choice
choice arguments
with keyword
If you include a Party as a choice argument, you can make that Party the controller of the choice. This means that the controller can be specified when the choice is exercised, rather than when the contract is created. For the exercise to work, the party needs to be able to see the contract, i.e. it must be an observer or a signatory.
a controller (or controllers)
controller keyword
Who can exercise the choice.
choice observers
observer keyword
Optional. Additional parties that are guaranteed to be informed of an exercise of the choice.
To specify choice observers, you must start you choice with the choice keyword.
The optional observer keyword must precede the mandatory controller keyword.
a choice body
After do keyword
What happens when someone exercises the choice. A choice body can contain update statements: see Choice body structure below.
Choice Body Structure
A choice body containsUpdate expressions, wrapped in a do block.
The update expressions are:
create
Create a new contract of this template.
create NameOfContract with contractArgument1 = value1; contractArgument2 = value2; ...
exercise
Exercise a choice on a particular contract.
exercise idOfContract NameOfChoiceOnContract with choiceArgument1 = value1; choiceArgument2 = value 2; ...
fetch
Fetch a contract using its ID. Often used with assert to check conditions on the contract’s content.
fetchedContract <- fetch IdOfContract
fetchByKey
Like fetch, but uses a contract key rather than an ID.
fetchedContract <- fetchByKey @ContractType contractKey
lookupByKey
Confirm that a contract with the given contract key exists.
fetchedContractId <- lookupByKey @ContractType contractKey
abort
Stop execution of the choice, fail the update.
if False then abort
assert
Fail the update unless the condition is true. Usually used to limit the arguments that can be supplied to a contract choice.
assert (amount > 0)
getTime
Gets the ledger time. Usually used to restrict when a choice can be exercised.
currentTime <- getTime
return
Explicitly return a value. By default, a choice returns the result of its last update expression. This means you only need to use return if you want to return something else.
return ContractID ExampleTemplate
The choice body can also contain:
let keyword
Used to assign values or functions.
assign a value to the result of an update statement
For example: contractFetched <- fetch someContractId
Reference: Templates
This page gives reference information on templates: For the structure of a template, seestructure.
Template Name
- This is the name of the template. It’s preceded by
templatekeyword. Must begin with a capital letter. - This is the highest level of nesting.
- The name is used when creating a contract of this template (usually, from within a choice).
Template Parameters
withkeyword. The parameters are in the form of a record type.- Passed in when creating a contract from this template. These are then in scope inside the template body.
- A template parameter can’t have the same name as any choice arguments inside the template.
- For all parties involved in the contract (whether they’re a
signatory,observer, orcontroller) you must pass them in as parameters to the contract, whether individually or as a list ([Party]).
Implicit Record
Whenever a template is defined, a record is implicitly defined with the same name and fields as that template. This record structure is used in Daml code to represent the data of a contract based on that template. Note that in the general case, the existence of a local bindingb of type T, where T is a template (and thus also a record), does not necessarily imply the existence of a contract with the same data as b on the ledger. You can only assume the existence of such a contract if b is the result of a fetch from the ledger within the same transaction.
You can create a new instance of a record of type T without any interaction with the ledger; in fact, this is how you construct a create command.
this and self
Within the body of a template we implicitly define a local binding this to represent the data of the current contract. For a template T, this binding is of type T, i.e. the implicit record defined by the template.
Within choices, you can additionally use the binding self to refer to the contract ID of the current contract (the one on which the choice is being executed). For a contract of template T, the self binding is of type ContractId T.
Template-local Definitions (Deprecated)
Fix or remove this literal include
letkeyword. Starts a block and is followed by any number of definitions, just like any otherletblock.- Template parameters as well as
thisare in scope, butselfis not. - Definitions from the
letblock can be used anywhere else in the template’swhereblock.
Migration
Users are strongly encouraged to adapt their code to avoid this feature. This involves replacing each template-local definition with a regular top-level definition. If the old definition made use of contract fields or the contract body (“this”), the new definition should take them as parameters. Correspondingly, the use sites of these definitions should supply the appropriate values as arguments. For example, consider the templatePerson below. It defines and uses a template-local binding fullName, which now triggers the deprecation warning.
fullName should be defined as a top-level function, and its use site now passes this explicitly.
Turning off the warning
This warning is controlled by the warning flagtemplate-let, which means that it can be toggled independently of other warnings. This is especially useful for gradually migrating code that used this syntax.
To turn off the warning within a Daml file, add the following line at the top of the file:
build-options field of the project’s daml.yaml file
daml.yaml file, it can be turned back on for individual Daml files by adding the following line at the top of each file:
Signatory Parties
-
signatorykeyword. Afterwhere. Followed by at least oneParty. -
Signatories are the parties (see the
Partytype) who must consent to the creation of this contract. They are the parties who would be put into an obligable position when this contract is created. Daml won’t let you put someone into an obligable position without their consent. So if the contract will cause obligations for a party, they must be a signatory. If they haven’t authorized it, you won’t be able to create the contract. In this situation, you may see errors like:NameOfTemplate requires authorizers Party1,Party2,Party, but only Party1 were given. - When a signatory consents to the contract creation, this means they also authorize the consequences of choices that can be exercised on this contract.
- The contract is visible to all signatories (as well as the other stakeholders of the contract). That is, the compiler automatically adds signatories as observers.
-
Each template must have at least one signatory. A signatory declaration consists of the
signatorykeyword followed by a comma-separated list of one or more expressions, each expression denoting aPartyor collection thereof.
Observers
observerkeyword. Afterwhere. Followed by at least oneParty.- Observers are additional stakeholders, so the contract is visible to these parties (see the
Partytype). - Optional. You can have many, either as a comma-separated list or reusing the keyword. You could pass in a list (of type
[Party]). - Use when a party needs visibility on a contract, or be informed or contract events, but is not a signatory or controller.
- If you start your choice with
choicerather thancontroller(seedaml-ref-choicesbelow), you must make sure to add any potential controller as an observer. Otherwise, they will not be able to exercise the choice, because they won’t be able to see the contract.
Choices
- A right that the contract gives the controlling party. Can be exercised.
- This is essentially where all the logic of the template goes.
- By default, choices are consuming: that is, exercising the choice archives the contract, so no further choices can be exercised on it. You can make a choice non-consuming using the
nonconsumingkeyword. - See
choicesfor full reference information.
Serializable Types
Every parameter to a template, choice argument, and choice result must have a serializable type. This does not merely mean “convertible to bytes”; it has a specific meaning in Daml. The serializability rule serves three purposes:- Offer a stable means to store ledger values permanently.
- Provide a sensible encoding of them over the
ledger-api. - Provide sensible types that directly match their Daml counterparts in languages like Java for language codegen.
- Function types.
- Record types with any non-serializable field.
- Variant types with any non-serializable value case.
- Variant and enum types with no constructors.
- References to a parameterized data type with any non-serializable type argument. This applies whether or not the data type definition uses the type parameter.
- Defined data types with any type parameter of kind
Nat, or any kind other than*. This means higher-kinded types, and types that take a parameter just to pass toNumeric, are not serializable.
Migration
Users should remove any agreement declarations from their code, as this feature has been fully removed from the language.Preconditions
ensurekeyword, followed by a boolean condition.- Used on contract creation.
ensurelimits the values on parameters that can be passed to the contract: the contract can only be created if the boolean condition is true.
Contract Keys and Maintainers
-
keyandmaintainerkeywords. - This feature lets you specify a “key” that you can use to uniquely identify this contract as an instance of this template.
-
If you specify a
key, you must also specify amaintainer. This is aPartythat will ensure the uniqueness of all the keys it is aware of. Because of this, thekeymust include themaintainerPartyor parties (for example, as part of a tuple or record), and themaintainermust be a signatory. -
For a full explanation, see
contractkeys.
Interface Instances
- Used to make a template an instance of an existing interface.
- The clause must start with the keywords
interface instance, followed by the name of the interface, then the keywordforand the name of the template (which must match the enclosing declaration), and finally the keywordwhere, which introduces a block where all the methods of the interface must be implemented. - See
interfacesfor full reference information on interfaces, or sectioninterface-instancesfor interface instances specifically.
Reference: Choices
This page gives reference information on choices. For information on the high-level structure of a choice, seestructure.
Choice Name
choicekeyword- The name of the choice. Must begin with a capital letter.
- Must be unique in the module. Different templates defined in the same module cannot share a choice name.
Controllers
-
controllerkeyword - The controller is a comma-separated list of values, where each value is either a party or a collection of parties. The conjunction of all the parties are required to authorize when this choice is exercised.
Choice Observers
Choice observers can be attached to a choice using theobserver keyword. The choice observers are a list of parties who are not stakeholders but who see all the consequences of the action.
Contract Consumption
If no qualifier is present, choices are consuming: the contract is archived before the evaluation of the choice body and both the controllers and all contract stakeholders see all consequences of the action.Preconsuming Choices
preconsumingkeyword. Optional.- Makes a choice pre-consuming: the contract is archived before the body of the exercise is executed.
- The create arguments of the contract can still be used in the body of the exercise, but cannot be fetched by its contract id.
- The archival behavior is analogous to the consuming default behavior.
- Only the controllers and signatories of the contract see all consequences of the action. Other stakeholders merely see an archive action.
- Can be thought as a non-consuming choice that implicitly archives the contract before anything else happens
Postconsuming Choices
postconsumingkeyword. Optional.- Makes a choice post-consuming: the contract is archived after the body of the exercise is executed.
- The create arguments of the contract can still be used in the body of the exercise as well as the contract id for fetching it.
- Only the controllers and signatories of the contract see all consequences of the action. Other stakeholders merely see an archive action.
- Can be thought as a non-consuming choice that implicitly archives the contract after the choice has been exercised
Non-consuming Choices
nonconsumingkeyword. Optional.- Makes a choice non-consuming: that is, exercising the choice does not archive the contract.
- Only the controllers and signatories of the contract see all consequences of the action.
- Useful in the many situations when you want to be able to exercise a choice more than once.
Return Type
- Return type is written immediately after choice name.
- All choices have a return type. A contract returning nothing should be marked as returning a “unit”, ie
(). - If a contract is/contracts are created in the choice body, usually you would return the contract ID(s) (which have the type
ContractId <name of template>). This is returned when the choice is exercised, and can be used in a variety of ways.
Choice Arguments
withkeyword.- Choice arguments are similar in structure to
daml-ref-template-parameters: a record type. - A choice argument can’t have the same name as any parameter to the template the choice is in.
- Optional - only if you need extra information passed in to exercise the choice.
Choice Body
- Introduced with
do - The logic in this section is what is executed when the choice gets exercised.
- The choice body contains
Updateexpressions. For detail on this, seeupdates. - By default, the last expression in the choice is returned. You can return multiple updates in tuple form or in a custom data type. To return something that isn’t of type
Update, use thereturnkeyword.
Reference: Updates
This page gives reference information on Updates. For the structure around them, seestructure.
Background
- An
Updateis ledger update. There are many different kinds of these, and they’re listed below. - They are what can go in a choice body.
Binding Variables
- One of the things you can do in a choice body is bind (assign) an Update expression to a variable. This works for any of the Updates below.
do
-
docan be used to groupUpdateexpressions. You can only have one update expression in a choice, so any choice beyond the very simple will use adoblock. -
Anything you can put into a choice body, you can put into a
doblock. -
By default,
doreturns whatever is returned by the last expression in the block. So if you want to return something else, you’ll need to usereturnexplicitly - seedaml-ref-returnfor an example.
archive
archivefunction.- Archives a contract already created and residing on the ledger. The contract is fetched by its unique contract identifier
ContractId <name of template>and then exercises theArchivechoice on it. - Returns unit.
- Requires authorization from the contract controllers/signatories. Without the required authorization, the transaction fails. For more detail on authorization, see
daml-ref-signatories. - All templates implicitly have an
Archivechoice that cannot be removed, which is equivalent to:
create
-
createfunction. -
Creates a contract on the ledger. When a contract is committed to the ledger, it is given a unique contract identifier of type
ContractId <name of template>. -
Creating the contract returns that
ContractId. -
Use
withto specify the template parameters. -
Requires authorization from the signatories of the contract being created. This is given by being signatories of the contract from which the other contract is created, being the controller, or explicitly creating the contract itself.
If the required authorization is not given, the transaction fails. For more detail on authorization, see
daml-ref-signatories.
exercise
exercisefunction.- Exercises the specified choice on the specified contract.
- Use
withto specify the choice parameters. - Requires authorization from the controller(s) of the choice. If the authorization is not given, the transaction fails.
exerciseByKey
exerciseByKeyfunction.- Like
exercise, but the contract is specified by contract key, instead of contract ID. - For details see Reference: Contract Keys: exerciseByKey
fetch
fetchfunction.- Fetches the contract with that ID. Usually used with a bound variable, as in the example above.
- Often used to check the details of a contract before exercising a choice on that contract. Also used when referring to some reference data.
fetch cidfails ifcidis not the contract id of an active contract, and thus causes the entire transaction to abort.- The submitting party must be an observer or signatory on the contract, otherwise
fetchfails, and similarly causes the entire transaction to abort.
fetchByKey
fetchByKeyfunction.- Like
fetch, but fetches the contract with that contract key, instead of the contract ID. - For details see Contract Keys.
visibleByKey
visibleByKeyfunction.- Use this to check whether a contract with the given contract key exists.
- For details see Reference: Contract Keys: visibleByKey
lookupByKey
lookupByKeyfunction.- Use this to confirm that a contract with the given contract key exists.
- For details see Reference: Contract Keys: lookupByKey
abort
abortfunction.- Fails the transaction - nothing in it will be committed to the ledger.
errorMessageis of typeText. Use the error message to provide more context to an external system (e.g., it gets displayed in Daml Studio script results).- You could use
assert Falseas an alternative.
assert
assertkeyword.- Fails the transaction if the condition is false. So the choice can only be exercised if the boolean expression evaluates to
True. - Often used to restrict the arguments that can be supplied to a contract choice.
assert to prevent a choice being exercised if the Party passed as a parameter is on a blacklist:
getTime
getTimekeyword.- Gets the ledger time. (You will usually want to immediately bind it to a variable in order to be able to access the value.)
- Used to restrict when a choice can be made. For example, with an
assertthat the time is later than a certain time.
return
returnkeyword.- Used to return a value from
doblock that is not of typeUpdate.
let
See the documentation ondaml-ref-let.
Let looks similar to binding variables, but it’s very different! This code example shows how:
this
this lets you refer to the current contract from within the choice body. This refers to the contract, not the contract ID.
It’s useful, for example, if you want to pass the current contract to a helper function outside the template.
Reference: Data Types
This page gives reference information on Daml’s data types.Built-in Types
Table of built-in primitive types
Escaping Characters
Text literals support backslash escapes to include their delimiter (\") and a backslash itself (\\).
Time
Definition of time on the ledger is a property of the execution environment. Daml assumes there is a shared understanding of what time is among the stakeholders of contracts.Lists
[a] is the built-in data type for a list of elements of type a. The empty list is denoted by [] and [1, 3, 2] is an example of a list of type [Int].
You can also construct lists using [] (the empty list) and :: (which is an operator that appends an element to the front of a list). For example:
Sum a List
To sum a list, use a fold (because there are no loops in Daml). Seedaml-ref-folding for details.
Records and Record Types
You declare a new record type using thedata and with keyword:
label1,label2, …,labelNare labels, which must be unique in the record typetype1,type2, …,typeNare the types of the fields
with and the format using { } are exactly the same syntactically. The main difference is that when you use with, you can use newlines and proper indentation to avoid the delimiting semicolons.
The deriving (Eq, Show) ensures the data type can be compared (using ==) and displayed (using show). The line starting deriving is required for data types used in fields of a template.
In general, add the deriving unless the data type contains function types (e.g. Int -> Int), which cannot be compared or shown.
For example:
Data Constructors
You can usedata keyword to define a new data type, for example data Floor a = Floor a for some type a.
The first Floor in the expression is the type constructor. The second Floor is a data constructor that can be used to specify values of the Floor Int type: for example, Floor 0, Floor 1.
In Daml, data constructors may take at most one argument.
An example of a data constructor with zero arguments is data Empty = Empty {}. The only value of the Empty type is Empty.
In
data Confusing = Int, the Int is a data constructor with no arguments. It has nothing to do with the built-in Int type.Access Record Fields
To access the fields of a record type, use dot notation. For example:Update Record Fields
You can also use thewith keyword to create a new record on the basis of an existing replacing select fields.
For example:
MyRecord with first = 1; second = 5.
If you have a variable with the same name as the label, Daml lets you use this without assigning it to make things look nicer:
The
with keyword binds more strongly than function application. So for a function, say return, either write return IntegerCoordinate with first = 1; second = 5 or return (IntegerCoordinate {first = 1; second = 5}), where the latter expression is enclosed in parentheses.Parameterized Data Types
Daml supports parameterized data types. For example, to express a more general type for 2D coordinates:Coordinate is Coordinate Int Int.
Type Synonyms
To declare a synonym for a type, use thetype keyword.
For example:
IntegerTuple and (Int, Int) synonyms: they have the same type and can be used interchangeably.
You can use the type keyword for any type, including daml-ref-built-in-types.
Function Types
A function’s type includes its parameter and result types. A functionfoo with two parameters has type ParamType1 -> ParamType2 -> ReturnType.
Note that this can be treated as any other type. You could for instance give it a synonym using type FooType = ParamType1 -> ParamType2 -> ReturnType.
Algebraic Data Types
An algebraic data type is a composite type: a type formed by a combination of other types. The enumeration data type is an example. This section introduces more powerful algebraic data types.Product Types
The following data constructor is not valid in Daml:data AlternativeCoordinate a b = AlternativeCoordinate a b. This is because data constructors can only have one argument.
To get around this, wrap the values in a record: data Coordinate a b = Coordinate {first: a; second: b}.
These kinds of types are called product types.
A way of thinking about this is that the Coordinate Int Int type has a first and second dimension (that is, a 2D product space). By adding an extra type to the record, you get a third dimension, and so on.
Sum Types
Sum types capture the notion of being of one kind or another. An example is the built-in data typeBool. This is defined by data Bool = True | False deriving (Eq,Show), where True and False are data constructors with zero arguments . This means that a Bool value is either True or False and cannot be instantiated with any other value.
Please note that all types which you intend to use as template or choice arguments need to derive at least from (Eq, Show).
A very useful sum type is data Optional a = None | Some a deriving (Eq,Show). It is part of the Daml standard library.
Optional captures the concept of a box, which can be empty or contain a value of type a.
Optional is a sum type constructor taking a type a as parameter. It produces the sum type defined by the data constructors None and Some.
The Some data constructor takes one argument, and it expects a value of type a as a parameter.
Pattern Matching
You can match a value to a specific pattern using thecase keyword.
The pattern is expressed with data constructors. For example, the Optional Int sum type:
optionalIntegerToText function, the case construct first tries to match the x argument against the None data constructor, and in case of a match, the "Box is empty" text is returned. In case of no match, a match is attempted for x against the next pattern in the list, i.e., with the Some data constructor. In case of a match, the content of the value attached to the Some label is bound to the val variable, which is then used in the corresponding output text string.
Note that all patterns in the case construct need to be complete, i.e., for each x there must be at least one pattern that matches. The patterns are tested from top to bottom, and the expression for the first pattern that matches will be executed. Note that _ can be used as a catch-all pattern.
You could also case distinguish a Bool variable using the True and False data constructors and achieve the same behavior as an if-then-else expression.
As an example, the following is an expression for a Text:
An underscore was used in place of a variable name. The reason for this is that Daml Studio produces a warning for all variables that are not being used. This is useful in detecting unused variables. You can suppress the warning by naming the variable with an initial underscore.
Reference: Expressions
This page gives reference information for Daml expressions that are not updates.Definitions
Use assignment to bind values or functions at the top level of a Daml file or in a contract template body.Values
For example:pi has type Decimal is inferred from the value. To explicitly annotate the type, mention it after a colon following the variable name:
Functions
You can define functions. Here’s an example: a function for computing the surface area of a tube:- the name of the function
-
the function’s type signature
Decimal -> Decimal -> DecimalThis means it takes two Decimals and returns another Decimal. -
the definition
= 2.0 * pi * r * h(which uses the previously definedpi)
Arithmetic Operators
The result of the modulo operation has the same sign as the dividend:
7 / 3and(-7) / (-3)evaluate to2(-7) / 3and7 / (-3)evaluate to-27 % 3and7 % (-3)evaluate to1(-7) % 3and(-7) % (-3)evaluate to-1
(+) 1 2 is another way of writing 1 + 2.
Comparison Operators
Logical Operators
The logical operators in Daml are:notfor negation, e.g.,not True == False&&for conjunction, wherea && b == and a b||for disjunction, wherea || b == or a b
Bool variables a and b.
If-then-else
You can use conditional if-then-else expressions, for example:Let
To bind values or functions to be in scope beneath the expression, use the block keywordlet:
let inside do blocks:
Reference: Functions
This page gives reference information on functions in Daml. Daml is a functional language. It lets you apply functions partially and also have functions that take other functions as arguments. This page discusses these higher-order functions.Defining Functions
Inexpressions, the tubeSurfaceArea function was defined as:
\, a sequence of parameters, and an arrow -> as:
Partial Application
The type of thetubeSurfaceArea function described previously, is Decimal -> Decimal -> Decimal. An equivalent, but more instructive, way to read its type is: Decimal -> (Decimal -> Decimal): saying that tubeSurfaceArea is a function that takes one argument and returns another function.
So tubeSurfaceArea expects one argument of type Decimal and returns a function of type Decimal -> Decimal. In other words, this function returns another function. Only the last application of an argument yields a non-function.
This is called currying: currying is the process of converting a function of multiple arguments to a function that takes just a single argument and returns another function. In Daml, all functions are curried.
This doesn’t affect things that much. If you use functions in the classical way (by applying them to all parameters) then there is no difference.
If you only apply a few arguments to the function, this is called partial application. The result is a function with partially defined arguments. For example:
Functions are Values
The function type can be explicitly added to thetubeSurfaceArea function (when it is written with the lambda notation):
tubeSurfaceArea : BinaryDecimalFunction = ... follows the same pattern as when binding values, e.g., pi : Decimal = 3.14159265359.
Functions have types, just like values. Which means they can be used just like normal variables. In fact, in Daml, functions are values.
This means a function can take another function as an argument. For example, define a function applyFilter: (Int -> Int -> Bool) -> Int -> Int -> Bool which applies the first argument, a higher-order function, to the second and the third arguments to yield the result.
daml-ref-folding section looks into two useful built-in functions, foldl and foldr, that also take a function as an argument.
Daml does not allow functions as parameters of contract templates and contract choices. However, a follow up of a choice can use built-in functions, defined at the top level or in the contract template body.
Generic Functions
A function is parametrically polymorphic if it behaves uniformly for all types, in at least one of its type parameters. For example, you can define function composition as follows:a, b, and c are any data types. Both compose ((+) 4) ((*) 2) 3 == 10 and compose not ((&&) True) False evaluate to True. Note that ((+) 4) has type Int -> Int, whereas not has type Bool -> Bool.
You can find many other generic functions including this one in the Daml standard library.
Daml currently does not support generic functions for a specific set of types, such as
Int and Decimal numbers. For example, sum (x: a) (y: a) = x + y is undefined when a equals the type Party. Bounded polymorphism might be added to Daml in a later version.Reference: Daml File Structure
This page gives reference information on the structure of Daml files outside of templates.File Structure
-
This file’s module name (
module NameOfThisFile where). Part of a hierarchical module system to facilitate code reuse. Must be the same as the Daml file name, without the file extension. For a file with path./Scenarios/Demo.daml, usemodule Scenarios.Demo where.
Imports
- You can import other modules (
import OtherModuleName), including qualified imports (import qualified AndYetOtherModuleName,import qualified AndYetOtherModuleName as Signifier). Can’t have circular import references. - To import the
Preludemodule of./Prelude.daml, useimport Prelude. - To import a module of
./Scenarios/Demo.daml, useimport Scenarios.Demo. - If you leave out
qualified, and a module alias is specified, top-level declarations of the imported module are imported into the module’s namespace as well as the namespace specified by the given alias.
Libraries
A Daml library is a collection of related Daml modules. Define a Daml library using aLibraryModules.daml file: a normal Daml file that imports the root modules of the library. The library consists of the LibraryModules.daml file and all its dependencies, found by recursively following the imports of each module.
Errors are reported in Daml Studio on a per-library basis. This means that breaking changes on shared Daml modules are displayed even when the files are not explicitly open.
Comments
Use-- for a single line comment. Use {- and -} for a comment extending over multiple lines.
Contract Identifiers
When an instance of a template (that is, a contract) is added to the ledger, it’s assigned a unique identifier, of typeContractId <name of template>.
The runtime representation of these identifiers depends on the execution environment: a contract identifier from the Sandbox may look different to ones on other Daml Ledgers.
You can use == and /= on contract identifiers of the same type.
Reference: Daml Packages
This page gives reference information on Daml package dependencies.Building Daml Archives
When a Daml project is compiled, the compiler produces aDaml archive. These are platform-independent packages of compiled Daml code that can be uploaded to a Daml ledger or imported in other Daml projects.
Daml archives have a .dar file ending. By default, when you run dpm build, it will generate the .dar file in the .daml/dist folder in the project root folder. For example, running dpm build in project foo with project version 0.0.1 will result in a Daml archive .daml/dist/foo-0.0.1.dar.
You can specify a different path for the Daml archive by using the -o flag:
Inspecting DARs
Refer to the section on decoding DARs and DALF files.Import Daml Packages
There are two ways to import a Daml package in a project: viadependencies, and via data-dependencies. They each have certain advantages and disadvantages. To summarize:
dependenciesallow you to import a Daml archive as a library. The definitions in the dependency will all be made available to the importing project. However, the dependency must be compiled with the same SDK version, so this method is only suitable for breaking up large projects into smaller projects that depend on each other, or to reuse existing libraries.data-dependenciesallow you to import a Daml archive (.dar) or a Daml-LF package (.dalf), including packages that have already been deployed to a ledger. These packages can be compiled with any previous SDK version. On the other hand, not all definitions can be carried over perfectly, since the Daml interface needs to be reconstructed from the binary.
Import a Daml package via Dependencies
A Daml project can declare a Daml archive as a dependency in thedependencies field of daml.yaml. This lets you import modules and reuse definitions from another Daml project. The main limitation of this method is that the dependency must be built for the same SDK version as the importing project.
Let’s go through an example. Suppose you have an existing Daml project foo, located at /home/user/foo, and you want to use it as a dependency in a project bar, located at /home/user/bar.
To do so, you first need to generate the Daml archive of foo. Go into /home/user/foo and run dpm build -o foo.dar. This will create the Daml archive, /home/user/foo/foo.dar.
Next, we will update the project config for bar to use the generated Daml archive as a dependency. Go into /home/user/bar and change the dependencies field in daml.yaml to point to the created `Daml archive`:
dpm build in the bar project, the compiler will make the definitions in foo.dar available for importing. For example, if foo exports the module Foo, you can import it in the usual way:
foo are made available when importing foo as a dependency. To limit which modules of foo get exported, you may add an exposed-modules field in the daml.yaml file for foo:
Import a Daml Archive via data-dependencies
You can import a Daml archive (.dar) or Daml-LF package (.dalf) using data-dependencies. Unlike dependencies, this can be used when the SDK versions do not match.
For example, you can import foo.dar as follows:
data-dependencies to work across SDK versions, the compiler has to abstract over some details which are not compatible across SDK versions. This means that there are some Daml features that cannot be recovered when using data-dependencies. In particular:
- Export lists cannot be recovered, so imports via
data-dependenciescan access definitions that were originally hidden. This means it is up to the importing module to respect the data abstraction of the original module. Note that this is the same for all code that runs on the ledger, since the ledger does not provide special support for data abstraction. - If you have a
dependencythat limits the modules that can be accessed viaexposed-modules, you can get an error if you also have adata-dependencythat references something from the hidden modules (even if it is only reexported). Sinceexposed-modulesare not available on the ledger in general, we recommend to not make use of them and instead rely on naming conventions (e.g., suffix module names with.Internal) to make it clear which modules are part of the public API. - Prior to Daml-LF version 1.8, typeclasses could not be reconstructed. This means if you have a package that is compiled with an older version of Daml-LF, typeclasses and typeclass instances will not be carried over via data-dependencies, and you won’t be able to call functions that rely on typeclass instances. This includes the template functions, such as
create,signatory, andexercise, as these rely on typeclass instances. - Starting from Daml-LF version 1.8, when possible, typeclass instances will be reconstructed by re-using the typeclass definitions from dependencies, such as the typeclasses exported in
daml-stdlib. However, if the typeclass signature has changed, you will get an instance for a reconstructed typeclass instead, which will not interoperate with code from dependencies.
Transitive dependency management
The Daml compiler identifies each DAR dependency in the project by itspackageId and fully qualified name (Daml project package name and version number).
If you have a Daml project which contains multiple common transitive DAR dependencies, those common transitive dependencies must either:
- Have identical contents if they have the same name and version specified in their Daml project’s
daml.yamlfile, or - Have a different value for the
versionentry in their respectivedaml.yamlfiles.
- Daml project X (top-level) has dependencies
DarAandDarB. DarAandDarBboth contain DAR dependencyDarC
DarC dependency referenced by both DarA and DarB either has identical Daml contents or has a different version number if the contents differ. The version number is defined in the daml.yaml file of the Daml project producing DarC, under the version key.
Reference Daml Packages Already On the Ledger
Daml packages that have been uploaded to a ledger can be imported as data dependencies, given you have the necessary permissions to download these packages. To import such a package, add the package name and version separated by a colon to the data-dependencies stanza as follows:localhost:6865), the ledger stanza can be omitted. This will fetch and install the package foo-1.0.0. A daml.lock file is created at the root of your project directory, pinning the resolved packages to their exact package ID:
daml.lock file needs to be checked into version control of your project. This assures that package name/version tuples specified in your data dependencies are always resolved to the same package ID. To recreate or update your daml.lock file, delete it and run dpm build again.
Handling Module Name Collisions
Sometimes you will have multiple packages with the same module name. In that case, a simple import will fail, since the compiler doesn’t know which version of the module to load. Fortunately, there are a few tools you can use to approach this problem. The first is to use package qualified imports. Supposing you have packages with different names,foo and bar, which both expose a module X, you can select which one you want with a package qualified import.
To get X from foo:
X from bar:
--package build option.
Suppose you are importing packages foo-1.0.0 and foo-2.0.0. Notice they have the same name foo but different versions. To get modules that are exposed in both packages, you will need to provide module aliases. You can do this by passing the --package build option. Open daml.yaml and add the following build-options:
X in foo-1.0.0 as Foo1.X, and alias the X in foo-2.0.0 as Foo2.X. Now you will be able to import both X by using the new names:
module-prefixes field in your daml.yaml. This is particularly useful for upgrades where you can map all modules of version v of your package under V$v. For the example above you can use the following:
X from package foo-1.0.0 as Foo1.X and X from package foo-2.0.0 as Foo2.
You can also use more complex module prefixes, e.g., foo-1.0.0: Foo1.Bar which will make module X available under Foo1.Bar.X.
Reference: Contract Keys
Contract keys are an optional addition to templates that let you identify contracts using their template parameters, similar to a key in a database. See Contract Keys for a full walkthrough, including multi-contract-key lookups (lookupNByKey, lookupAllByKey) and the Daml Script key functions. For the stdlib API surface, see DA.ContractKeys.
Reference: Interfaces
In Daml, an interface defines an abstract type together with a behavior specified by its view type, method signatures, and choices. For a template to conform to this interface, there must be a correspondinginterface instance definition where all the methods of the interface (including the special view method) are implemented. This allows decoupling such behavior from its implementation, so other developers can write applications in terms of the interface instead of the concrete template.
Configuration
To use this feature your Daml project must target Daml-LF version1.15 or higher, which is the current default.
If using Canton, the protocol version of the sync domain should be 4 or higher, see Canton protocol version for more details.
Interface Declaration
An interface declaration is somewhat similar to a template declaration.Interface Name
- This is the name of the interface.
- It’s preceded by the keyword
interfaceand followed by the keywordwhere. - It must begin with a capital letter, like any other type name.
Implicit abstract type
- Whenever an interface is defined, an abstract type is defined with the same name. “Abstract” here means:
- Values of this type cannot be created using a data constructor. Instead, they are constructed by applying the function
toInterfaceto a template value. - Values of this type cannot be inspected directly via case analysis. Instead, use functions such as
fromInterface. - See
daml-ref-interface-functionsfor more information on these and other functions for interacting with interface values.
- Values of this type cannot be created using a data constructor. Instead, they are constructed by applying the function
- An interface value carries inside it the type and parameters of the template value from which it was constructed.
- As for templates, the existence of a local binding
bof typeI, whereIis an interface does not necessarily imply the existence on the ledger of a contract with the template type and parameters used to constructb. This can only be assumed ifbthe result of a fetch from the ledger within the same transaction.
Interface Methods
- An interface may define any number of methods.
- A method definition consists of the method name and the method type, separated by a single colon
:. The name of the method must be a valid identifier beginning with a lowercase letter or an underscore. - A method definition introduces a top level function of the same name:
- If the interface is called
I, the method is calledm, and the method type isM(which might be a function type), this introduces the functionm : I -> M:
- If the interface is called
-
The first argument’s type
Imeans that the function can only be applied to values of the interface typeIitself. Methods cannot be applied to template values, even if there exists aninterface instanceofIfor that template. To use an interface method on a template value, first convert it using thetoInterfacefunction. -
Applying the function to such argument results in a value of type
M, corresponding to the implementation ofmin the interface instance ofIfor the underlying template typet(the type of the template value from which the interface value was constructed). -
One special method,
view, must be defined for the viewtype. (seeinterface-viewtypebelow).
Interface View Type
- All interface instances must implement a special
viewmethod which returns a value of the type declared byviewtype. - The type must be a record.
- This type is returned by subscriptions on interfaces.
Interface Choices
- Interface choices work in a very similar way to template choices. Any contract of a template type for which an interface instance exists will grant the choice to the controlling party.
- Interface choices can only be exercised on values of the corresponding interface type. To exercise an interface choice on a template value, first convert it using the
toInterfacefunction. - Interface methods can be used to define the controller of a choice (e.g.
method1) as well as the actions that run when the choice is exercised (e.g.method2andmethod3). - As for template choices, the
choicekeyword can be optionally prefixed with thenonconsumingkeyword to specify that the contract will not be consumed when the choice is exercised. If not specified, the choice will beconsuming. Note that thepreconsumingandpostconsumingqualifiers are not supported on interface choices. - See
choicesfor full reference information, but note that controller-first syntax is not supported for interface choices.
Empty Interfaces
- It is possible (though not necessarily useful) to define an interface without methods, precondition or choices. However, a view type must always be defined, though it can be set to unit.
Interface Instances
For context, a simple template definition:interface instance clause
- To make a template an instance of an existing interface, an
interface instanceclause must be defined in the template declaration. - The template of the clause must match the enclosing declaration. In other words, a template
Tdeclaration can only containinterface instanceclauses where the template isT. - The clause must start with the keywords
interface instance, followed by the name of the interface, then the keywordforand the name of the template, and finally the keywordwhere, which introduces a block where all the methods of the interface must be implemented. - Within the clause, there’s an implicit local binding
thisreferring to the contract on which the method is applied, which has the type of the template’s data record. The template parameters of this contract are also in scope. - Method implementations can be defined using the same syntax as for top level functions, including pattern matches and guards (e.g.
method3). - Each method implementation must return the same type as specified for that method in the interface declaration.
- The implementation of the special
viewmethod must return the type specified as theviewtypein the interface declaration.
Empty interface instance clause
- If the interface has no methods, the interface instance only needs to implement the
viewmethod:
Interface Functions
interfaceTypeRep
toInterface
fromInterface
toInterfaceContractId
fromInterfaceContractId
coerceInterfaceContractId
fetchFromInterface
Required Interfaces
-
An interface can depend on other interfaces. These are specified with the
requireskeyword after the interface name but before thewherekeyword, separated by commas. -
For an interface declaration to be valid, its list of required interfaces must be transitively closed. In other words, an interface
Icannot require an interfaceJwithout also explicitly requiring all the interfaces required byJ. The order, however, is irrelevant. For example, givenThis declaration for interfaceSquarewould cause a compiler errorExplicitly addingShapeto the required interfaces fixes the error -
For a template
Tto be a validinterface instanceof an interfaceI,Tmust also be aninterface instanceof each of the interfaces required byI.
Interface Functions
Reference: Exceptions (Deprecated)
Exceptions are a deprecated Daml feature which provides a way to handle certain errors that arise during interpretation instead of aborting the transaction, and to roll back the state changes that lead to the error. There are two types of errors:Builtin Errors
Note that other errors cannot be handled via exceptions, e.g., an exercise on an inactive contract will still result in a transaction abort.
User-defined Exceptions
Users can define their own exception types which can be thrown and caught. The definition looks similar to templates, and just like with templates, the definition produces a record type of the given name as well as instances to make that type throwable and catchable. In addition to the record fields, exceptions also need to define amessage function.
Throw Exceptions
There are two ways to throw exceptions:- Inside of an
ActionlikeUpdateorScriptyou can usethrowfromDA.Exception. This works for anyActionthat is an instance ofActionThrow. - Outside of
ActionThrowyou can throw exceptions usingthrowPure.
throw since it is easier to reason about when exactly the exception will get thrown.
Catch Exceptions
Exceptions are caught in try-catch blocks similar to those found in languages like Java. Thetry block defines the scope within which errors should be handled while the catch clauses defines which types of errors are handled and how the program should continue. If an exception gets caught, the subtransaction between the try and the the point where the exception is thrown is rolled back. The actions under rollback nodes are still validated, so, e.g., you can never fetch a contract that is inactive at that point or have two contracts with the same key active at the same time. However, all ledger state changes (creates, consuming exercises) are rolled back to the state before the rollback node.
Each try-catch block can have multiple catch clauses with the first one that applies taking precedence.
In the example below the create of T will be rolled back and the first catch clause applies which will create an Error contract.
Reference: Built-in Functions
This page gives reference information on built-in functions for working with a variety of common concepts.Work with Time
Daml has these built-in functions for working with time:datetime: creates aTimegiven year, month, day, hours, minutes, and seconds as argument.subTime: subtracts one time from another. Returns theRelTimedifference betweentime1andtime2.addRelTime: add times. Takes aTimeandRelTimeand adds theRelTimeto theTime.days,hours,minutes,seconds: constructs aRelTimeof the specified length.pass: (in Daml Script tests only) usepass : RelTime -> Script Timeto advance the ledger time by the argument amount. Returns the new time.
Work with Numbers
Daml has these built-in functions for working with numbers:-
round: rounds aDecimalnumber toInt.round dis the nearestInttod. Tie-breaks are resolved by rounding away from zero, for example: -
truncate: converts aDecimalnumber toInt, truncating the value towards zero, for example: -
intToDecimal: converts anInttoDecimal.
Decimal is not closed under division as the result may require more than 10 decimal places to represent. For example, 1.0 / 3.0 == 0.3333... is a rational number, but not a Decimal.
Work with Text
Daml has these built-in functions for working with text:<>operator: concatenates twoTextvalues.showconverts a value of the primitive types (Bool,Int,Decimal,Party,Time,RelTime) to aText.
\:
Work with Lists
Daml has these built-in functions for working with lists:foldlandfoldr: seedaml-ref-foldingbelow.
Fold
A fold takes:- a binary operator
- a first accumulator value
- a list of values
foldl, or from the right in a foldr).
We’d usually recommend using
foldl, as foldr is usually slower. This is because it needs to traverse the whole list before starting to discharge its elements.- The binary operator is applied to the first accumulator value and the first element in the list. This produces a second accumulator value.
- The binary operator is applied to the second accumulator value and the second element in the list. This produces a third accumulator value.
- This continues until there are no more elements in the list. Then, the last accumulator value is returned.
Fixity, Associativity and Precedence
With normal, prefix operators (e.g. functions), the semantics off g h is clear: f is a function that takes g and h as parameters. If we want f to take the result of applying g to h we write f (g h).
In the case of infix operators (e.g. symbol operators such as + and *, or functions surrounded by backticks, for example `elem ```), it is less clear. What doesx - y - zmean? Subtractingxfromyfirst (i.e.(x - y) - z) generally yields different results than subtracting zfromyfirst (i.e.x - (y - z)). In Daml, the subtraction operator -is defined as a \*left-assocative\* operator. That is, when we writex - y - z - …the parser associates \*to the left\*, meaning the parser interprets this as((x - y) - z) - …\.
Some operators are right-associative. We have already encountered one: function application! A function signature of a -> b -> c -> ... is parsed as (a -> (b -> (c -> ...))).
Finally, some operators are non-associative. A good example are comparison operators such as == and >. This means any ambiguous usage of these operators (e.g. a == b == c or a > b > c) results in a parse error.
Non-associative operators are not to be confused with operators that are both left- and right-associative, such as
+ (since (x + y) + z = x + (y + z))). To obtain a deterministic parser, such operators must be declared as one of either left-associative or right-associative. In Daml the + operator has been declared as left-associativex + y * z is parsed as x + (y * z). Operator precedence is expressed as a number, where a higher number indicates a higher precedence. Operators of same precedence are associated to the left (e.g. x + y - z is parsed as (x + y) - z.
The fixity and precedence of an operator are declared using the infixl, infix, and infixr keywords (denoting left-, non-, and right-associativity, respectfully) that take an integer between 0 and 9 inclusive and an operator the fixity applies to. For example, infixl 6 + declares that + is a left-associative operator with precedence 6. These keywords can be used for user-defined operators as well. The following table shows the fixity and precedence for operators that are built-in to the Daml language, such as + and -: