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:
- Is there a way to implement a homogeneous linked list with interfaces as they stand today?
- 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)?