How Daml types are translated to Daml-LF¶
This page shows how types in Daml are translated into Daml-LF. It should help you understand and predict the generated client interfaces, which is useful when you’re building a Daml-based application that uses the Ledger API or client bindings in other languages.
For an introduction to Daml-LF, see Daml-LF.
Primitive types¶
Built-in data types in Daml have straightforward mappings to Daml-LF.
This section only covers the serializable types, as these are what client applications can interact with via the generated Daml-LF. (Serializable types are ones whose values can be written in a text or binary format. So not function types, Update
and Scenario
types, as well as any types built up from those.)
Most built-in types have the same name in Daml-LF as in Daml. These are the exact mappings:
Daml primitive type | Daml-LF primitive type |
---|---|
Int |
Int64 |
Time |
Timestamp |
() |
Unit |
[] |
List |
Decimal |
Decimal |
Text |
Text |
Date |
Date |
Party |
Party |
Optional |
Optional |
ContractId |
ContractId |
Be aware that only the Daml primitive types exported by the Prelude module map to the Daml-LF primitive types above. That means that, if you define your own type named Party
, it will not translate to the Daml-LF primitive Party
.
Tuple types¶
Daml tuple type constructors take types T1, T2, …, TN
to the type (T1, T2, …, TN)
. These are exposed in the Daml surface language through the Prelude module.
The equivalent Daml-LF type constructors are daml-prim:DA.Types:TupleN
, for each particular N (where 2 <= N <= 20). This qualified name refers to the package name (ghc-prim
) and the module name (GHC.Tuple
).
For example: the Daml pair type (Int, Text)
is translated to daml-prim:DA.Types:Tuple2 Int64 Text
.
Data types¶
Daml-LF has three kinds of data declarations:
- Record types, which define a collection of data
- Variant or sum types, which define a number of alternatives
- Enum, which defines simplified sum types without type parameters nor argument.
Data type declarations in Daml (starting with the data
keyword) are translated to record, variant or enum types. It’s sometimes not obvious what they will be translated to, so this section lists many examples of data types in Daml and their translations in Daml-LF.
Record declarations¶
This section uses the syntax for Daml records with curly braces.
Daml declaration | Daml-LF translation |
---|---|
data Foo = Foo { foo1: Int; foo2: Text } |
record Foo ↦ { foo1: Int64; foo2: Text } |
data Foo = Bar { bar1: Int; bar2: Text } |
record Foo ↦ { bar1: Int64; bar2: Text } |
data Foo = Foo { foo: Int } |
record Foo ↦ { foo: Int64 } |
data Foo = Bar { foo: Int } |
record Foo ↦ { foo: Int64 } |
data Foo = Foo {} |
record Foo ↦ {} |
data Foo = Bar {} |
record Foo ↦ {} |
Variant declarations¶
Daml declaration | Daml-LF translation |
---|---|
data Foo = Bar Int | Baz Text |
variant Foo ↦ Bar Int64 | Baz Text |
data Foo a = Bar a | Baz Text |
variant Foo a ↦ Bar a | Baz Text |
data Foo = Bar Unit | Baz Text |
variant Foo ↦ Bar Unit | Baz Text |
data Foo = Bar Unit | Baz |
variant Foo ↦ Bar Unit | Baz Unit |
data Foo a = Bar | Baz |
variant Foo a ↦ Bar Unit | Baz Unit |
data Foo = Foo Int |
variant Foo ↦ Foo Int64 |
data Foo = Bar Int |
variant Foo ↦ Bar Int64 |
data Foo = Foo () |
variant Foo ↦ Foo Unit |
data Foo = Bar () |
variant Foo ↦ Bar Unit |
data Foo = Bar { bar: Int } | Baz Text |
variant Foo ↦ Bar Foo.Bar | Baz Text , record Foo.Bar ↦ { bar: Int64 } |
data Foo = Foo { foo: Int } | Baz Text |
variant Foo ↦ Foo Foo.Foo | Baz Text , record Foo.Foo ↦ { foo: Int64 } |
data Foo = Bar { bar1: Int; bar2: Decimal } | Baz Text |
variant Foo ↦ Bar Foo.Bar | Baz Text , record Foo.Bar ↦ { bar1: Int64; bar2: Decimal } |
data Foo = Bar { bar1: Int; bar2: Decimal } | Baz { baz1: Text; baz2: Date } |
data Foo ↦ Bar Foo.Bar | Baz Foo.Baz , record Foo.Bar ↦ { bar1: Int64; bar2: Decimal } , record Foo.Baz ↦ { baz1: Text; baz2: Date } |
Enum declarations¶
Daml declaration | Daml-LF declaration |
---|---|
data Foo = Bar | Baz |
enum Foo ↦ Bar | Baz |
data Color = Red | Green | Blue |
enum Color ↦ Red | Green | Blue |
Banned declarations¶
There are two gotchas to be aware of: things you might expect to be able to do in Daml that you can’t because of Daml-LF.
The first: a single constructor data type must be made unambiguous as to whether it is a record or a variant type. Concretely, the data type declaration data Foo = Foo
causes a compile-time error, because it is unclear whether it is declaring a record or a variant type.
To fix this, you must make the distinction explicitly. Write data Foo = Foo {}
to declare a record type with no fields, or data Foo = Foo ()
for a variant with a single constructor taking unit argument.
The second gotcha is that a constructor in a data type declaration can have at most one unlabelled argument type. This restriction is so that we can provide a straight-forward encoding of Daml-LF types in a variety of client languages.
Banned declaration | Workaround |
---|---|
data Foo = Foo |
data Foo = Foo {} to produce record Foo ↦ {} OR data Foo = Foo () to produce variant Foo ↦ Foo Unit |
data Foo = Bar |
data Foo = Bar {} to produce record Foo ↦ {} OR data Foo = Bar () to produce variant Foo ↦ Bar Unit |
data Foo = Foo Int Text |
Name constructor arguments using a record declaration, for example data Foo = Foo { x: Int; y: Text } |
data Foo = Bar Int Text |
Name constructor arguments using a record declaration, for example data Foo = Bar { x: Int; y: Text } |
data Foo = Bar | Baz Int Text |
Name arguments to the Baz constructor, for example data Foo = Bar | Baz { x: Int; y: Text } |
Type synonyms¶
Type synonyms (starting with the type
keyword) are eliminated during conversion to Daml-LF. The body of the type synonym is inlined for all occurrences of the type synonym name.
For example, consider the following Daml type declarations.
type Username = Text
data User = User { name: Username }
The Username
type is eliminated in the Daml-LF translation, as follows:
record User ↦ { name: Text }
Template types¶
A template declaration in Daml results in one or more data type declarations behind the scenes. These data types, detailed in this section, are not written explicitly in the Daml program but are created by the compiler.
They are translated to Daml-LF using the same rules as for record declarations above.
These declarations are all at the top level of the module in which the template is defined.
Template data types¶
Every contract template defines a record type for the parameters of the contract. For example, the template declaration:
template Iou
with
issuer: Party
owner: Party
currency: Text
amount: Decimal
where
results in this record declaration:
data Iou = Iou { issuer: Party; owner: Party; currency: Text; amount: Decimal }
This translates to the Daml-LF record declaration:
record Iou ↦ { issuer: Party; owner: Party; currency: Text; amount: Decimal }
Choice data types¶
Every choice within a contract template results in a record type for the parameters of that choice. For example, let’s suppose the earlier Iou
template has the following choices:
controller owner can
nonconsuming DoNothing: ()
do
return ()
Transfer: ContractId Iou
with newOwner: Party
do
updateOwner newOwner
This results in these two record types:
data DoNothing = DoNothing {}
data Transfer = Transfer { newOwner: Party }
Whether the choice is consuming or nonconsuming is irrelevant to the data type declaration. The data type is a record even if there are no fields.
These translate to the Daml-LF record declarations:
record DoNothing ↦ {}
record Transfer ↦ { newOwner: Party }
Names with special characters¶
All names in Daml—of types, templates, choices, fields, and variant data constructors—are translated to the more restrictive rules of Daml-LF. ASCII letters, digits, and _
underscore are unchanged in Daml-LF; all other characters must be mangled in some way, as follows:
$
changes to$$
,- Unicode codepoints less than 65536 translate to
$uABCD
, whereABCD
are exactly four (zero-padded) hexadecimal digits of the codepoint in question, using only lowercasea-f
, and - Unicode codepoints greater translate to
$UABCD1234
, whereABCD1234
are exactly eight (zero-padded) hexadecimal digits of the codepoint in question, with the samea-f
rule.
Daml name | Daml-LF identifier |
---|---|
Foo_bar |
Foo_bar |
baz' |
baz$u0027 |
:+: |
$u003a$u002b$u003a |
naïveté |
na$u00efvet$u00e9 |
:🙂: |
$u003a$U0001f642$u003a |