Implementing a linked list of templates via interfaces

I’m trying to capture the notion of a generic linked list of templates which supports the standard operations of head, tail, and cons. With type-classes I can get pretty close:

module Class where

import DA.Record (HasField)

findElem : (LinkedList l elem meta, Eq elem) => elem -> ContractId l -> Update (Optional (ContractId l, l))
findElem elem lId = do
  l <- fetch lId
  if l.head == elem
  then pure $ Some (lId, l)
  else
    case l.tail of
      Some t -> findElem elem t
      None   -> pure None

class (HasField "head" l elem, HasField "tail" l (Optional (ContractId l)), Template l)
  => LinkedList l elem meta | l -> elem, l -> meta where
     -- meta is any additional data needed by l's constructor
    cons : meta -> elem -> Optional (ContractId l) -> l

template IntLinkedList with
    owner : Party
    head : Int
    tail : Optional (ContractId IntLinkedList)
  where
    signatory owner
    nonconsuming choice Find : (Optional (ContractId IntLinkedList, IntLinkedList)) with
        elem : Int
      controller owner
        do
          findElem elem self

instance LinkedList IntLinkedList Int Party where
  cons = IntLinkedList

LinkedList above requires that head and tail be record fields, and the user must implement cons. I can write a generic findElem function using head and tail just fine, and the only boilerplate remaining is the choice Find wrapping findElem.

Unfortunately, when trying to implement this via an interface, I ran into the immediate problem of not being able to specify the element type. So instead I omitted head from the interface, and replaced Find with IsElem, since I can’t write a useful find function without an element type (searching for a contract id in the list is pointless because I can just fetch the contract). So the interface version looks like this:

interface ILinkedList where
  viewtype LinkedListView

  owner : Party

  setTail : Optional (ContractId ILinkedList) -> ILinkedList

  choice IsElem : Bool with
      tgtId : ContractId ILinkedList
    controller owner this
    do
      if self == tgtId
      then pure True
      else
        case (view this).tail of
          Some tailId -> exercise tailId $ IsElem tgtId
          None        -> pure False

  choice Prepend : ContractId ILinkedList with
      lId : ContractId ILinkedList
    controller owner this
      do
        l <- fetch lId
        case (view l).tail of
          Some tId -> do
            tail' <- exercise self $ Prepend tId
            create $ setTail l (Some tail')
          None -> create $ setTail l (Some self)

  nonconsuming choice GetTail : Optional (ContractId ILinkedList)
    controller owner this
    do
      pure (view this).tail

  choice SetTail : ContractId ILinkedList with
      tail : Optional (ContractId ILinkedList)
    controller owner this
    do
      create $ setTail this tail

data LinkedListView = LinkedListView with tail : Optional (ContractId ILinkedList)
  deriving (Eq, Show)

This implementation also admits heterogeneous lists, but that isn’t what I want. So, here are my questions:

  1. Is there a way to implement a homogeneous linked list with interfaces as they stand today?
  2. If the answer to (1) is “no”, what are the problems with allowing abstract type declarations in the interface definition ala ML modules (type Elem) and/or naming the underlying type (so one can implement a homogeneous list)?
1 Like