Hello, I am new to Daml and learning it slowly but surely. I got to interfaces and am not sure about its goal. I have lots of experience with OOP and know what interfaces mean in OOP. I read the docs about the interfaces several times but I still don’t get it.
Can someone please write a simple application using interfaces, so I will understand its purpose and what each part of the interface does, for example, while in the docs it mentions the viewtype, it does not say why we need it for. and etc…
Hi @doron_levi,
I drafted as tiny example of why interfaces are useful in a previous forum post.
Please, have a look and let me know if that provides you with some clarity. Further above in the same post, I explain why interfaces have a view.
Matteo
Hi @Matteo_Limberto I appreciate your answer, but still I don’t understand the use of viewtype. If interfaces in Daml are like interfaces in OOP, why does interface has to keep a serialize data? Isn’t suppose to be part of the template instead?
While the concept is generally similar to OO interfaces, it’s still a concept that has to live in the broader Daml context.
Specifically, you have to keep in mind that a Daml program is distributed between a ledger and a number of clients. If you want to be able to write client code that deals with interfaces, that client code has to be able to receive an object of type Interface I rather than Template T. Therefore, the interface types, just like templates, have to be serializable.
This is basically my entire question… why do I need a serializable part in interfaces? What was missing, so it was needed to be a part of the interface?
If your client code deals with interfaces, you will reach a point where it wants to get the data for a ContractId for which it does not know the Template type, but does know the (an) Interface type.
In that case, your client code will ask the Ledger API for the data associated with that ContractId “under that interface”, which will be the “view” of the interface, i.e. a data record with fields defined by the interface.
Because the server needs to be able to send that data over the wire to your clent, the server needs to serialize that data (turn it into bits that can travel on the network), and the client then needs to be able to deserialize it. And because the server and client are not necessarily written in the same language, we had to define a cross-language subset of data types that can be send that way, and we call that “serializable”.
In practice, for the most part, serializable data is “everything but functions”, though there may be corner cases.
Does this help?
It helped a lot. Thank you.
If I may, I have another question in regards to your explanation. How can it be possible that I will reach a point where I want to get the data of a ContractId for which I do not know the Template type? If I am not aware of the template, its details are kind of worthless.
Let’s say I have 3 different types of templates that are implementing a “Rentable” interface (RentApartment, RentCar, RentMusicInstrument). The view of that interface has the amount for the rent and the rent expiry date. So, when I am pulling these details I don’t know if it is the amount for renting a car , an apartment or maybe even a music instrument. Also, I won’t be able to exercise any choice of it
How does it help me?
The only situation I can think of this as helpful, is when I rebuilt my project file and the version of the template is newer, so I won’t be able to fetch the “old” contracts using the new DAR file.
What if I just want to know the total sum of how much I owe each period across all things I’ve rented?
With interfaces I can write that code in such a way that, if you later add a 4th type of Rentable things, my client code doesn’t have to change.
@Gary_Verhaegen,
Can you give me such an example with a code, so I will understand how you do it?
This is a bit abstract, but let’s look at the following code:
module Main where
import Daml.Script
import DA.Assert ((===))
data LoanView = LoanView with capitalOwed: Int
deriving Show
interface Loan where
viewtype LoanView
template LoanType1 with
principal: Int
interest: Int
owner: Party
where
signatory owner
interface instance Loan for LoanType1 where
view = LoanView with capitalOwed = principal
template LoanType2 with
capital: Int
owner: Party
where
signatory owner
interface instance Loan for LoanType2 where
view = LoanView with capitalOwed = capital
totalLoans : Party -> Script Int
totalLoans p = do
loans <- queryInterface @Loan p
return $ sum (map (\(cid, Some loan) -> loan.capitalOwed) loans)
setup : Script ()
setup = script do
alice <- allocatePartyWithHint "Alice" (PartyIdHint "Alice")
submit alice do createCmd LoanType1 with principal = 10, interest = 1, owner = alice
submit alice do createCmd LoanType2 with capital = 10, owner = alice
totalOwed <- totalLoans alice
20 === totalOwed
In this case, if you add new templates that have instances for the Loan
interface, you don’t need to change the totalLoans
function at all, it will still give you the total amount you owe across all loans you have.
Obviously in a real model your loans would be more complex and you would need to filter on the ones you owe v. the ones you own etc. But the gist of it is your client code (here, just the totalLoans
function, but the same reasoning would apply to a full-fledged Java application serving a user-facing GUI) does not need to change when you add new loan types if it is built on top of the interface as an abstraction barrier instead of touching templates directly.
Does that help?
I don’t find anything regarding queryInterface
.
Variable not in scope: queryInterfacetypecheck
• Cannot apply expression of type ‘t1’
to a visible type argument ‘Loan’
• In a stmt of a 'do' block: loans <- queryInterface @Loan p
In the expression:
do loans <- queryInterface @Loan p
return
$ sum
(map (\ (cid, Some loan) -> (getField @"capitalOwed" loan)) loans)
In an equation for ‘totalLoans’:
totalLoans p
= do loans <- queryInterface @Loan p
return
$ sum
(map (\ (cid, Some loan) -> (getField @"capitalOwed" loan)) loans)typecheck
I searched in the docs.daml.com site as well with no luck.
I don’t think it’s part of Daml 2.4.0 yet; if you want to play along, you’ll have to use a snapshot (or wait for 2.5.0). See the GitHub releases for the latest snapshot, though bear in mind snapshots are not stable and therefore not supported in any way.
Also, to enable interface support you’ll have to add
build-options:
- --target=1.15
to your daml.yaml
in current snapshots.
@Gary_Verhaegen when 2.5 is about to get released?
Current target is mid-December I believe but I’m not in charge of the release schedule so I’m not making any promise.