How to access functions belonging to an implemented interface from the template's choices?

(pseudocode)
If I have an interface

interface MyInterface
  myMethod: Update()

If I have a template that implements that interface

template MyTemplate
  interface instance MyInterface for MyTemplate
    myMethod = myLengthyMethodDefinition
  choice MyChoice
    controller ctrl
      do
        myMethod

I want to declare an abstract method on the interface, and then in the template I want to implement that method and access that implementation from various places in the template.

I realise that I could simply leave myMethod off of the interface and simply declare it on the template.
What I want to do here is to force all implementations of MyInterface to not only implement methods essential for the interface’s choices, but also to implement a utility method for use in the template’s choices, simply as a way to communicate to other developers that implementations should have a utility method used in the template’s (non-interface) choices too. So the point is just to make my intentions clearer.
However, I cannot see how to access my implementations from elsewhere in the template.

1 Like

This code compiles and the test passes; however, this isn’t really what interface methods are for. You should use separate typeclasses and instances instead.

data MIV = MIV {x: Int}

interface MyInterface where
  viewtype MIV
  myMethod: Update (Text, Party)

template MyTemplate with
    p: Party
  where
    signatory p
    interface instance MyInterface for MyTemplate where
      view = MIV 42
      myMethod = pure ("here", p)
    choice MyChoice: (Text, Party)
      controller p
      do
        myMethod $ toInterface this

testIt = script do
  alice <- allocateParty "alice"
  result <- submit alice $ createAndExerciseCmd (MyTemplate alice) MyChoice
  result === ("here", alice)

Why is this the wrong abstraction? Because interface methods are really meant for turning concrete interface choices into partially-abstract implementations that have bits and pieces controlled by the concrete template implementation. That is, they are not accessible from the ledger API, whereas the meaningful use cases for interfaces are all about accessibility from the ledger API. To put it another way, if a method is not used in a concrete interface choice, it is not truly “essential for the interface’s choices”.

The proper tool for Daml-code-internal abstractions is the traditional typeclass. Here is a rewrite of the above using typeclasses:

class EssentialMethods t where
  myMethod: t -> Update (Text, Party)

interface MyInterface where
  viewtype MIV

instance EssentialMethods MyTemplate where
  myMethod mt = pure ("here", mt.p)

template MyTemplate with
    p: Party
  where
    signatory p
    interface instance MyInterface for MyTemplate where
      view = MIV 42
    choice MyChoice: (Text, Party)
      controller p
      do
        myMethod this

Note that now myMethod can be invoked directly on this, illustrating one way in which typeclasses give you better Daml-specific APIs than can be accomplished with interfaces alone.

Typeclasses, defined with the class and instance keywords, are far more flexible; templates and interfaces themselves are built on much more sophisticated usage of typeclasses. Even create and exercise are based on typeclass interaction with templates and interfaces, rather than directly defined as bespoke magic for those features. Here’s some more about understanding their role in the standard library.

3 Likes