Can I avoid boilerplate code for transfrerring data of a data type to an identical data type in a different package?

I want to write code for migrating contracts from one package to another.

As a starting point, I want to write a function which transfers a contract payload in one package, to an identical contract payload in the other package.

If the contract payload only contains primitive data types, this is simple, using double dot syntax.

If I have the following template in both the old and the new package:

template Asset
  with
    issuer : Party
    owner  : Party
    name : Text
etc.

I create an upgrade package using the old and the new package as data dependency, and label the packages like this:

data-dependencies:
  - ../asset-1/.daml/dist/asset1-0.0.1.dar
  - ../asset-2/.daml/dist/asset2-0.0.1.dar
module-prefixes:
  asset1-0.0.1: Asset1
  asset2-0.0.1: Asset2

Given this, I can use double dot syntax for the migration function:

module Main where
import qualified Asset1.Main as Main1
import qualified Asset2.Main as Main2

migrate: Main1.Asset -> Main2.Asset
migrate Main1.Asset{..} = Main2.Asset{..}

But as soon as I have custom data types in the Asset contract, the double dot syntax doesn’t work.

data AssetCode = AssetCode with 
    name: Text
    version: Int
  deriving (Eq, Show)

template Asset
  with
    issuer : Party
    owner  : Party
    assetCode   : AssetCode
etc.

With the template above, the double dot syntax will result in an error, and I can do e.g. the following:

module Main where

import qualified Asset1.Main as Main1
import qualified Asset2.Main as Main2

transform: Main1.AssetCode -> Main2.AssetCode
transform Main1.AssetCode{..} = Main2.AssetCode{..}

-- This doesn't work
-- migrate: Main1.Asset -> Main2.Asset
-- migrate Main1.Asset{..} = Main2.Asset{..}

-- This works
migrate: Main1.Asset -> Main2.Asset
migrate Main1.Asset{..} = Main2.Asset with 
                            assetCode = transform assetCode
                            ..

For a complex template, which has even more custom data in several levels, this requires a lot of boilerplate code.

Is there a trick to avoid this possibly huge amount of boilerplate code?

Not really, as the two AssetCode types are different at this point.

The reason things work with primitives is they’re not different, because we make sure, when compiling the daml stdlib, that they don’t change.

Remember that a type name includes the package ID of the package in which the name is defined. Therefore, if you want two templates to use the same type in their definition, that type has to be the same up to package ID, and then the double-dot syntax will work.

I can see two ways of achieveing that in the context you describe: either the new package asset2 depends on asset1 and uses the AssetCode type from asset1 directly (which means you’ll need asset1 as long as you use asset2, possibly over time creating a long list of unwanted packages), or you create a separate package with just your “expected-to-be-stable” data definitions and both asset1 and asset2 depend on that.

Or you could go further and have one package per data type, and then you never need to change a type because another one changed. The logistics of that may get in the way a little bit, so you’ll have to find some middleground.

1 Like

Yes, thank you, this helps, we will explore the options you recommend.