I know currently template DAML does not have support for generics. Still, I was wondering why is that and what are the options if someone wants to achieve something similar?
Assume I want something like the following:
template DataHolder
with
owner : Party
value: ???
where
signatory owner
The options I can think of for the ??? part:
expose the type Any which is now internal. That way I think one could achieve some sort of generic behaviour (maybe with some typeclasses involved)
use Text and store arbitrary values, e.g. JSON. Maybe create a newtype out of it and develop the facilities to deal with that type. (E.g.: easy, encapsulated way to work with the JSON structure)
use sum types. This approach is limited compared to the others in the sense that it does not allow arbitrary runtime types. But it does give more type safety
DAML used to support generic templates as an experimental feature, but they conflicted badly with other critical features DAML needed to support, so they were eventually dropped. In the project I’m working on, we made extensive use of generic templates when they were available.
We have been able to replace their use with typeclass constraints and functional dependencies, the result does involve more boilerplate, as we have to replace some of the machinery that was being generated by the DAML complier
module GenericTemplates where
import DA.Record
import DA.Functor
class Template t => Perform t a | t -> a where
perform : ContractId t -> a -> Update a
generic : Perform t a => Party -> ContractId t -> a -> Scenario a
generic p fid a = do
submit p $ perform fid a
genericFetch : (Template t, HasField "field" t a) => ContractId t -> Update a
genericFetch fid = (.field) <$> fetch fid
template Foo
with
party : Party
field : Int
where
signatory party
controller party can
PerformFoo : Int
with
param : Int
do
pure field
instance Perform Foo Int where
perform fid param = exercise fid PerformFoo with param
template Bar
with
pty : Party
field : Text
where
signatory pty
controller pty can
PerformBar : Text
with
param : Text
do
pure field
instance Perform Bar Text where
perform fid param = exercise fid PerformBar with param
test : Scenario Int
test = scenario do
p <- getParty "party"
fid <- submit p $ create Bar with pty = p; field = "Text"
b <- submit p $ genericFetch fid
generic p fid b
fid <- submit p $ create Foo with party = p; field = 10
f <- submit p $ genericFetch fid
generic p fid f
Probably best to avoid something so primitive as the typeclass above, just wrapping a single field access or exercise is generally going to be a code smell. However, you can represent business contract protocols this way — generic business workflows that can be parametized by the typeclass constraints representing various “roles” different templates can play within them. Each template then declares an instance of that role typeclass to specify exactly how it participates. So for instance you might have:
class Template t => Fungible t where
quantity : t -> Int
split : Int -> ContractId t -> Update (t, t)
merge : ContractId t -> ContractId t -> Update t
Different asset classes would provide their own instance of this typeclass, and therefore be able to participate in a business workflow (assuming the existence of a Transferable typeclass as well:
transferQuantity : (Transferable t, Fungible t) => Int -> ContractId t -> Party -> Update (ContractId t, ContractId t)
You can do quite a lot of magic with type classes and that was indeed how Generic Templates were implemented in the background when they were available. DAML-LF’s type system is much simpler though so all that magic gets compiled away and is thus not available when including dependencies as data-dependencies, which is the only way they can be included across SDK. So what you’ll have available is the fully applied templates, and instances defined, but not the type classes, or the relationship between instances of a type class. So it is not possible to write a library of generic templates like Perform above, that is usable across SDK versions, and that is exactly why we put the Generic Template feature on ice again.
To do it properly, we need to extend DAML-LF’s type system, but that bears its own risks. If we make it too flexible, we lose safety. So there is a design challenge there for next gen Generic Templates.
Please correct me if I’m wrong, but I think that there doesn’t exist a safe language that has this capability, and if a language does have this capability it isn’t really safe.
I interpret @Andrae’s example to demonstrate that the parametericity you need probably depends on what you intend to do with your templates. So you could have generic choices. But if you’re looking for a mechanism to avoid repeating share state can you have a generic data type that has a specific instance on each template?
Barring a good example, I don’t think that exposing the Any type is a good solution. Who would want to sign such a contract? I’m not certain that it provides the mechanism that you’re looking for, (something akin to Data.Dynamic?) Even if you were to have that you’ll still have to compile the lookup.
I think what @Tamas_Kalczai is after is an existential type. We don’t support those. If we did, you’d be able to express wildcard agreements like “I agree to exercise any choice” so there’s definitely a tradeoff with security.