Skip to content

Commit

Permalink
daml2ts : Explain DAML/TypeScript mappings (digital-asset#4196)
Browse files Browse the repository at this point in the history
changelog_begin
changelog_end

Co-authored-by: Shayne Fletcher <shayne.fletcher@digitalasset.com>
  • Loading branch information
shayne-fletcher and Shayne Fletcher authored Jan 27, 2020
1 parent 60b0c4a commit a56caa0
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/configs/pdf/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Experimental features
triggers/index
daml-script/index
tools/visual
daml2ts/index

Support and updates
-------------------
Expand Down
290 changes: 290 additions & 0 deletions docs/source/daml2ts/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
.. Copyright (c) 2020 The DAML Authors. All rights reserved.
.. SPDX-License-Identifier: Apache-2.0
TypeScript!
###########

daml2ts : TypeScript of DAML
============================

Introduction
------------

``daml2ts`` generates TypeScript for interacting with DAML via the `json-api <../json-api/index.html>`_ .

``daml2ts`` inputs are compiled DAML modules. ``daml2ts`` outputs are TypeScript source files containing definitions modeling the DAML entities found.

The code ``daml2ts`` generates is in terms of the package `@daml/types <https://github.com/digital-asset/daml/tree/master/language-support/ts/daml-types>`_.

Primitives: @daml/types
-----------------------

To understand the TypeScript code generated by ``daml2ts``, it is helpful to keep in mind this quick review of the primitives provided by @daml/types.

**Interfaces**:

- ``Serializable<T>``
- ``Template<T extends object, K = unknown>``
- ``Choice<T extends object, C, R, K = unknown>``

..
An object that extends the ``Serializable<T>`` interface has a field ``decoder: () => jtv.Decoder<T>``.
An object that extends the ``Template<T, K>`` interface has fields ``templateId: string``, ``keyDecoder: ()=> jtv.Decoder<K>``, ``Archive: Choice<T, {}, {}>`` and extends ``Serializable<T>``.
An object that extends the ``Choice<T, C, R, K>`` interface has fields ``template: () => Template<T, K>``, ``argumentDecoder: () => jtv.Decoder<C>``, ``resultDecoder: () => jtv.Decoder<R>`` and ``choiceName: string``.
**Types**:

+-------------------+------------------+----------------------------------+
| TypeScript | DAML equiv. | TypeScript definition |
+===================+==================+==================================+
| ``Unit`` | ``()`` | ``{}`` |
+-------------------+------------------+----------------------------------+
| ``Bool`` | ``Bool`` | ``boolean`` |
+-------------------+------------------+----------------------------------+
| ``Int`` | ``Int`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Decimal`` | ``Decimal`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Numeric`` | ``Numeric n`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Text`` | ``Text`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Time`` | ``Time`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Party`` | ``Party`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``List<T>`` | ``[t]`` | ``T[]`` |
+-------------------+------------------+----------------------------------+
| ``Date`` | ``Date`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``ContractId<T>`` | ``ContractId t`` | ``string`` |
+-------------------+------------------+----------------------------------+
| ``Optional<T>`` | ``Optional t`` | ``null | (null extends T ?`` |
| | | ``[] | [Exclude<T, null>] : T)`` |
+-------------------+------------------+----------------------------------+
| ``TextMap<T>`` | ``TextMap t`` | ``{ [key: string]: T }`` |
+-------------------+------------------+----------------------------------+

.. note::
The TypeScript types ``Time``, ``Decimal``, ``Numeric`` and ``Int`` all alias to ``string``. These choices relate to the avoidance of precision loss under serialization over the `json-api <../json-api/index.html>`_.

.. note::
The Typescript definition of type ``Optional<T>`` in the above table might look complicated. It accounts for differences in the encoding of optional values when nested versus when they are not (i.e. "top-level"). For example, ``null`` and ``"foo"`` are two possible values of ``Optional<Text>`` whereas, ``[]`` and ``["foo"]`` are two possible values of type ``Optional<Optional<Text>>`` (``null`` is another possible value, ``[null]`` is **not**).

DAML to TypeScript mappings
---------------------------

The mappings from DAML to TypeScript are best explained by example.

Records
~~~~~~~

In DAML, we might model a person like this.

.. code-block:: daml
:linenos:
data Person =
Person with
name: Text
party: Party
age: Int
Given the above definition, the generated TypeScript code will be as follows.

.. code-block:: typescript
:linenos:
type Person = {
name: string;
party: daml.Party;
age: daml.Int;
}
Tuples
~~~~~~

Suppose the following DAML definition.

.. code-block:: daml
:linenos:
data Pair a b = Pair (a, b)
In TypeScript, the above definition renders as below [1]_.

.. code-block:: typescript
:linenos:
type Pair<a, b> =
| { tag: 'Pair'; value: {_1: a, _2: b} }
.. [1] This is a simplification but it is not wrong to think of it this way.
.. note::
The general rule is that DAML tuples are represented in TypeScript as simple objects with fields labeled ``_1``, ``_2`` and so on.

The DAML type ``Pair a b`` has been modeled as a TypeScript discriminated union (with a single case). In general, all DAML variants are translated so as we will see in the next section.

Variants
~~~~~~~~

This is a DAML type for a language of additive expressions.

.. code-block:: daml
:linenos:
data Expr a =
Lit a
| Var Text
| Add (Expr a, Expr a)
In TypeScript, it is represented as a discriminated union.

.. code-block:: typescript
:linenos:
type Expr<a> =
| { tag: 'Lit'; value: a }
| { tag: 'Var'; value: string }
| { tag: 'Add'; value: {_1: Expr<a>, _2: Expr<a>} }
Sum-of-products
~~~~~~~~~~~~~~~~

Let's slightly modify the ``Expr a`` type of the last section into the following.

.. code-block:: daml
:linenos:
data Expr a =
Lit a
| Var Text
| Add {lhs: Expr a, rhs: Expr a}
Compared to the earlier definition, the ``Add`` case is now in terms of a record with fields ``lhs`` and ``rhs``. This renders in TypeScript like so.

.. code-block:: typescript
:linenos:
type Expr<a> =
| { tag: 'Lit2'; value: a }
| { tag: 'Var2'; value: string }
| { tag: 'Add'; value: Expr.Add<a> }
namespace Expr {
type Add<a> = {
lhs: Expr<a>;
rhs: Expr<a>;
}
}
The thing to note is how the definition of the ``Add`` case has given rise to a record type definition ``Expr.Add``.

Enums
~~~~~

DAML enumerations map naturally to TypeScript.

.. code-block:: daml
:linenos:
data Color = Red | Blue | Yellow
The companion TypeScript type is the following.

.. code-block:: typescript
:linenos:
enum Color {
Red = 'Red',
Blue = 'Blue',
Yellow = 'Yellow',
}
Templates and choices
~~~~~~~~~~~~~~~~~~~~~

Here is a DAML template of a basic 'IOU' contract.

.. code-block:: daml
:linenos:
template Iou
with
issuer: Party
owner: Party
currency: Text
amount: Decimal
where
signatory issuer
choice Transfer: ContractId Iou
with
newOwner: Party
controller owner
do
create this with owner = newOwner
``daml2ts`` generates types for each of the choices defined on the template as well as the template itself.

.. code-block:: typescript
:linenos:
type Transfer = {
newOwner: daml.Party;
}
type Iou = {
issuer: daml.Party;
owner: daml.Party;
currency: string;
amount: daml.Numeric;
}
Each template and each template choice also results in the generation of a companion object corresponding to the choice or template type. Here, is a schematic of the ones generated from the ``Iou`` template.

.. code-block:: typescript
:linenos:
const Transfer: daml.Serializable<Transfer> = ({
/* ... */
}),
})
const Iou: daml.Template<Iou, undefined> & {
Archive: daml.Choice<Iou, DA_Internal_Template.Archive, {}, undefined>;
Transfer: daml.Choice<Iou, Transfer, daml.ContractId<Iou>, undefined>;
} = {
/* ... */
}
The exact details of these companion objects are not important - think of them as representing "metadata".

What **is** important is the use of the companion objects when creating contracts and exercising choices using the `@daml/ledger <https://github.com/digital-asset/daml/tree/master/language-support/ts/daml-ledger>`_ package. The following code snippet demonstrates their usage.

.. code-block:: typescript
:linenos:
import Ledger from '@daml/ledger';
const ledger = new Ledger(/* ... */);
// Contract creation; Bank issues Alice a USD $1MM IOU.
const iouDetails: Iou = {
issuer: 'Chase',
owner: 'Alice',
currency: 'USD',
amount: 1000000.0,
};
const aliceIouCreateEvent = await ledger.create(Iou, iouDetails);
const aliceIouContractId = aliceIouCreateEvent.contractId;
// Choice execution; Alice transfers ownership of the IOU to Bob.
const transferDetails: Transfer = {
newOwner: 'Bob',
}
const [bobIouContractId, _] = await ledger.exercise(Transfer, aliceIouContractId, transferDetails);
Observe on line *12*, the first argument to ``create`` is the ``Iou`` companion object and on line *20*, the first argument to ``exercise`` is the ``Transfer`` companion object.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ DAML SDK documentation
DAML Triggers <triggers/index>
DAML Script <daml-script/index>
tools/visual
daml2ts/index

.. toctree::
:titlesonly:
Expand Down

0 comments on commit a56caa0

Please sign in to comment.