How to use the new standalone DAML REPL for experimenting with advanced functions

With the new DAML SDK v 1.4.0, the DAML REPL can be used as an interactive IDE, without connecting to any ledger. I know that @cocreature and @Gary_Verhaegen were working on it, thanks for them and any other contributor for this great feature!

This new feature means that you don’t have to resort to GHCi, the Haskell interactive IDE, if you want to experiment with a more complex DAML function in order to deeper understanding it.

One example where I formerly had to use GHCi to understand what’s going on, but now I can demonstrate it with the standalone REPL, is the following.

Recently, I spent some time with reverse-engineering some advanced DAML refapps, in order to gain inspiration for my own projects.

In the DAML Finance Library (FinLib), within that in the DA.Finance.Trade.SettlementInstruction module, the ensure statement of the SettlementInstruction template looks like this:

ensure length steps > 0 && asset.quantity > 0.0
      && all (\(s1,s2) -> s1.receiverAccount.owner == s2.senderAccount.owner) (zip steps $ tail steps)

I’m sure many of you know immediately what the zip steps $ tail steps part does, but I had to think and try it out in Haskell. Now I can show it to you in the REPL. If you want to follow along, all you need to do is to install the new DAML RC, start the REPL without specifying a ledger hostname and port number, and install the DA.List module in order to be able to use the tail function, which cuts of the first element (the head) of a list.

The ensure clause is there to make sure that the chain of steps specified for a settlement chain is gapless, meaning that the sender in one step is the same party as the receiver in the previous step.

The zip steps $ tail steps part converts a list of steps to a list of tuples, where the elements of the tuples are the adjacent steps so that we can perform the check we need, like this:

[step1,step2,step3, …] --> [(step1, step2), (step2,step3), (step3, step4)…]

This is how it works, simplified, representing parties with numbers, and steps with tuples of parties:

bgy@BGY ~ % daml repl
daml> import DA.List
daml> let parties = [1..5]
daml> tail parties
[2,3,4,5]
daml> let steps = zip parties $ tail parties
daml> steps
[(1,2),(2,3),(3,4),(4,5)]
daml> let chain = zip steps $ tail steps    
daml> chain
[((1,2),(2,3)),((2,3),(3,4)),((3,4),(4,5))]

Now we have a list of tuples, where the first and second element of a tuple are adjacent steps, and we can check, with a simplified version of the above ensure clause function if the parties to the steps fulfill the gapless “relay run” condition needed for a settlement chain (which is, of course, true because that’s how we defined the steps in the first place):

daml> all (\(s1,s2) -> snd s1 == fst s2) $ zip steps $ tail steps
True

There are other interesting functions in the module, like this one:

nextSender : SettlementInstruction -> Optional Party
nextSender SettlementInstruction{..} = (.senderAccount.owner) <$> find (\step -> isNone step.depositCid) steps

I leave this to you to reverse engineer, using the REPL.

,

4 Likes

Great guide, thanks for writing this up!

1 Like