Reference: Interfaces¶
Warning
This feature is under active development and not officially supported in production environments.
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 corresponding interface 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¶
In order to test this experimental feature, your Daml project needs to target
Daml-LF version 1.15
or higher, which is specificed by adding the following
line to the project’s daml.yaml
file:
build-options: [--target=1.15]
If using Canton, it needs to run in dev mode, see Canton: How do I enable unsupported features for instructions.
Interface Declaration¶
An interface declaration is somewhat similar to a template declaration.
Interface Name¶
interface MyInterface where
- This is the name of the interface.
- It’s preceded by the keyword
interface
and 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
toInterface
to a template value. - Values of this type cannot be inspected directly via case analysis.
Instead, use functions such as
fromInterface
. - See Interface Functions for 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
b
of typeI
, whereI
is 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 ifb
the result of a fetch from the ledger within the same transaction.
Interface Methods¶
method1 : Party
method2 : Int
method3 : Bool -> Int -> Int -> Int
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
:func1 : MyInterface -> Party func1 = method1 func2 : MyInterface -> Int func2 = method2 func3 : MyInterface -> Bool -> Int -> Int -> Int func3 = method3
The first argument’s type
I
means that the function can only be applied to values of the interface typeI
itself. Methods cannot be applied to template values, even if there exists aninterface instance
ofI
for that template. To use an interface method on a template value, first convert it using thetoInterface
function.Applying the function to such argument results in a value of type
M
, corresponding to the implementation ofm
in the interface instance ofI
for 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. (see Interface View Type below).
Interface View Type¶
data MyInterfaceViewType =
MyInterfaceViewType { name : Text, value : Int }
viewtype MyInterfaceViewType
- All interface instances must implement a special
view
method 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¶
choice MyChoice : (ContractId MyInterface, Int)
with
argument1 : Bool
argument2 : Int
controller method1 this
do
let n0 = method2 this
let n1 = method3 this argument1 argument2 n0
pure (self, n1)
nonconsuming choice MyNonConsumingChoice : Int
controller method1 this
do
pure $ method2 this
- 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
toInterface
function. - 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.method2
andmethod3
). - As for template choices, the
choice
keyword can be optionally prefixed with thenonconsuming
keyword to specify that the contract will not be consumed when the choice is exercised. If not specified, the choice will beconsuming
. Note that thepreconsuming
andpostconsuming
qualifiers are not supported on interface choices. - See Reference: Choices for full reference information, but note that controller-first syntax is not supported for interface choices.
Empty Interfaces¶
data EmptyInterfaceView = EmptyInterfaceView {}
interface YourInterface where
viewtype EmptyInterfaceView
- 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:
template MyTemplate
with
field1 : Party
field2 : Int
where
signatory field1
interface instance
clause¶
interface instance MyInterface for MyTemplate where
view = MyInterfaceViewType "MyTemplate" 100
method1 = field1
method2 = field2
method3 False _ _ = 0
method3 True x y
| x > 0 = x + y
| otherwise = y
- To make a template an instance of an existing interface, an
interface instance
clause must be defined in the template declaration. - The template of the clause must match the enclosing declaration. In other
words, a template
T
declaration can only containinterface instance
clauses where the template isT
. - The clause must start with the keywords
interface instance
, followed by the name of the interface, then the keywordfor
and 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
this
referring 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
view
method must return the type specified as theviewtype
in the interface declaration.
interface instance
clause in the interface¶
interface MyNewInterface where
viewtype EmptyInterfaceView
myNewMethod1 : Party
myNewMethod2 : Int -> Int
choice MyNewChoice : Int
controller myNewMethod1 this
do pure $ myNewMethod2 this 0
interface instance MyNewInterface for MyTemplate where
view = EmptyInterfaceView
myNewMethod1 = field1
myNewMethod2 x = field2 `min` x
- To make an existing template an instance of a new interface, the
interface instance
clause must be defined in the interface declaration. - In this case, the interface of the clause must match the enclosing
declaration. In other words, an interface
I
declaration can only containinterface instance
clauses where the interface isI
. - All other rules for
interface instance
clauses are the same whether the enclosing declaration is a template or an interface. In particular, the implicit local bindingthis
always has the type of the template’s record.
Empty interface instance
clause¶
- If the interface has no methods, the interface instance only needs to
implement the
view
method:
interface instance YourInterface for MyTemplate where
view = EmptyInterfaceView
Interface Functions¶
interfaceTypeRep
¶
Type | HasInterfaceTypeRep i => i -> TemplateTypeRep |
Instantiated Type | MyInterface -> TemplateTypeRep |
Notes | The value of the resulting TemplateTypeRep indicates what template
was used to construct the interface value. |
toInterface
¶
Type | forall i t. HasToInterface t i => t -> i |
Instantiated Type | MyTemplate -> MyInterface |
Notes | Converts a template value into an interface value. |
fromInterface
¶
Type | HasFromInterface t i => i -> Optional t |
Instantiated Type | MyInterface -> Optional MyTemplate |
Notes | Attempts to convert an interface value back into a template value.
The result is None if the expected template type doesn’t match the
underlying template type used to construct the contract. |
toInterfaceContractId
¶
Type | forall i t. HasToInterface t i => ContractId t -> ContractId i |
Instantiated Type | ContractId MyTemplate -> ContractId MyInterface |
Notes | Converts a template Contract ID into an Interface Contract ID. |
fromInterfaceContractId
¶
Type | forall t i. HasFromInterface t i => ContractId i -> ContractId t |
Instantiated Type | ContractId MyInterface -> ContractId MyTemplate |
Notes | Converts an interface contract id into a template contract id.
This function does not verify that the given contract id actually points
to a contract of the resulting type; if that is not the case, a
subsequent fetch , exercise or archive will fail.
Therefore, this should only be used when the underlying contract is known
to be of the resulting type, or when the result is immediately used by a
fetch , exercise or archive action and a transaction failure
is the desired behavior in case of mismatch.
In all other cases, consider using fetchFromInterface instead. |
fetchFromInterface
¶
Type | forall t i. (HasFromInterface t i, HasFetch i) => ContractId i -> Update (Optional (ContractId t, t)) |
Instantiated Type | ContractId MyInterface -> Update (Optional (ContractId MyTemplate, MyTemplate)) |
Notes | Attempts to fetch and convert an interface contract id into a template,
returning both the converted contract and its contract id if the
conversion is successful, or None otherwise. |
Required Interfaces¶
Warning
This feature is under active development and not officially supported in production environments. Required interfaces are only available when targeting Daml-LF 1.dev.
interface OurInterface requires MyInterface, YourInterface where
viewtype EmptyInterfaceView
An interface can depend on other interfaces. These are specified with the
requires
keyword after the interface name but before thewhere
keyword, separated by commas.For an interface declaration to be valid, its list of required interfaces must be transitively closed. In other words, an interface
I
cannot require an interfaceJ
without also explicitly requiring all the interfaces required byJ
. The order, however, is irrelevant.For example, given
interface Shape where viewtype EmptyInterfaceView interface Rectangle requires Shape where viewtype EmptyInterfaceView
This declaration for interface
Square
would cause a compiler error-- Compiler error! "Interface Square is missing requirement [Shape]" interface Square requires Rectangle where viewtype EmptyInterfaceView
Explicitly adding
Shape
to the required interfaces fixes the errorinterface Square requires Rectangle, Shape where viewtype EmptyInterfaceView
For a template
T
to be a validinterface instance
of an interfaceI
,T
must also be aninterface instance
of each of the interfaces required byI
.
Interface Functions¶
Function | Notes |
---|---|
toInterface |
Can also be used to convert an interface value to one of its required interfaces. |
fromInterface |
Can also be used to convert a value of an interface type to one of its requiring interfaces. |
toInterfaceContractId |
Can also be used to convert an interface contract id into a contract id of one of its required interfaces. |
fromInterfaceContractId |
Can also be used to convert an interface contract id into a contract id of one of its requiring interfaces. |
fetchFromInterface |
Can also be used to fetch and convert an interface contract id into a contract and contract id of one of its requiring interfaces. |