DAML and container image manifests
Last week I wrote about belatedly learning how container image manifests work. My motivation was, in part, in order to try to prototype this mechanism.
First, in DAML:
https://github.com/DazWilkin/manifests/blob/master/daml/Manifests.daml
With thanks to Daniel and Neil for helping educate me on DAML.
If you read no further, Neil taught me two most-excellent things:
1. Hoogle
This may be an obvious solution to Haskellians (sp?) but, when I struggled to identify functions, Neil provided me with this DAML package explorer:
Hoogle is a Haskell search engine, geddit?
2. GitHub Linguist for “DAML”
Create .gitattributes
and set:
*.daml linguist-language=Haskell
And benefit from GitHub language-detection of your DAMLs:
Summary
I’m a DAML neophyte and I’ve (not|never) used Haskell. I find DAML to be “unusual” but I’m able to achieve my economical goals using it. I wrote the sample referenced in this post in a couple of hours this week and spent the majority of my time, scratching around docs.daml.com
to recall syntax and to find functions (e.g. lists).
In a spirit of learning by teaching, and teaching by helping others with my mistakes, I’m going to summarize my challenges here in building this sample.
Visual Studio Code
The DAML SDK works excellent with Visual Studio Code. I’m generally in need of software help particularly for code completion, signature help etc. As I “build a relationship” with DAML, the Code integration is wonderful.
Types
I know what I want (aliases, structs|records etc.) but I find the Haskell syntax a little ‘wonky’:
data SHA256 = SHA256 Text
deriving (Eq,Show)
I don’t grok why I must reproduce the ‘type’. Runtime errors guided me to adding the deriving (Eq,Show)
and I suspect (!) that e.g. Eq
works because it’s the same as equality on Text
but… magic.
I learned that this is more simply written as:
type SHA256 = Text
And that there will be a foundational type of “SHA-256” in the SDK.
User-defined Functions
Having learned ML and Prolog back in the day, after seeing somewhere (?) in the documentation an example constructing lists a :: [b,c,d]
, and (pre-Hoogle) being unaware of map
, I needed a way to map a list of Blob
to a list of Layer
. First working attempt:
computeLayers: [Blob] -> [Layer]
computeLayers [] = []
computeLayers (b::l) = (Layer with
mediaType = ""
size = computeSize b
digest = computeSHA256 b
)::computeLayers l
Which I was proud of ;-) computeLayers
takes a list of Blob
and returns a list of Layer
. It’s defined as, not much on an empty list ([]
) and recursively applies the implicit function mapping a Blob
to a Layer
for any other list.
Neil pointed me to Hoogle and map
yielding the more elegant:
computeLayer: Blob -> Layer
computeLayer b = Layer with
mediaType = ""
size = computeSize b
digest = computeSHA256 bcomputeLayers: [Blob] -> [Layer]
computeLayers = map computeLayer
With an explicit definition of computeLayer
and its list variant as being simply map computeLayer
.
Templates
I’m still slightly ‘fragile’ about when to use template
versus data
. Intuitively, I seem to pick the correct one but why data manifest
but template image
is difficult to articulate.
A fundamental differentiator is that, things that have Party
properties are Templates, but (!) the following works:
data Dog = Dog
with
guardian: Party
So, back to square one.
Perhaps it’s that Templates are primarily ledger-resident entities because they can be create
’d and other Verb’d?
Templates map closely (?) to O-Objects or Golang types and the verb actions mapping to O-Methods or Go’s function receivers. All good there. I struggle slightly with:
Accept: ContractId Account
Here, ContractId
is the ID of the Account
template but it must always be ContractId
and the syntax highlighting (both ContractId
and Account
in this example are colored blue) suggests these are the same type of thing. In this example, the Accept
method returns the ID of an Account
template. Its parameter (name
) is provided using the with
statement:
Accept: ContractId Account
with
name: Text
That continues to be quirky to my eyes.
There’s Pythonesque indentation requirements too that invariably cause me to shift blocks of statements in|out until the syntax checker stops complaining.
Scenarios
The DAML SDK not only provides excellent editing with Visual Studio Code but it also includes an outstanding testing framework:
The editor detects scenario do
in your source and provides clickable Scenario results
that takes you this view wherein you can determine the Contracts (Templates) that are created and archived using both a (more useful) transaction view
and a table view
.
The syntax for scenarios has changed between DAML versions and it took me a little while to get my head around the new syntax but it’s relatively straightforward. The ambition here is to emulate instantiating Contract (Templates) and applying methods to them. The tooling is able to identify errors in your code (you can’t send money to dead accounts, for example) and you can’t perform actions unless you’re a signatory.
It’s likely good practice to start a prototype by writing (or having someone write for you) the tests that you wish fulfilled and *then* writing the implementation. In this example, short of creating their own Registry
, Alice
must submit an AccountRequest
to a Registry
owner and only the Registry
owner can approve the request thereby creating an Account
for Alice
to use.
Commonly, scenarios would include assertions that, you often can’t spend more money than you have or sell things that you don’t possess.
Conclusion
If you’re familiar with Haskell, you probably didn’t read this far as this is obvious to you.
If you’re unfamiliar with Haskell and DAML, I encourage you to explore the language using the excellent SDK if you’re interested in developing formal solutions to workflow scenarios particularly smart contracts.
For me, DAML isn’t an intuitively obvious language but, then again, I’ve not used anything else that provides the same rapid development and controls for solutions in this domain.
A possible next step for me is to rewrite this solution using a more familiar language (e.g. Golang) to see which is more productive and which is more accurate.
The DAML SDK is excellent and the documentation (including Hoogle) are comprehensive.