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.
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.