What is the viewtype for interfaces mean?

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

1 Like

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.

1 Like

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.

1 Like

@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?

1 Like

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.

1 Like