Copying all fields from one contract to another

For upgrading DAR versions, I copy contracts created using the old version into contracts created using the new version. For the situation that the fields of the contracts didn’t change, I can do this without enumerating all the fields. For example, for a template C that exists unchanged in both versions 1 and 2, this works:

upgrade : C1 -> Update C2
upgrade c1@(C1 with ..) = do
  let _ = c1
  c2cid <- create C2 with
    ..
  fetch c2cid

This I can use so:

(c1cid, c1) <- fetchByKey @C1 c1key
_ <- archive c1cid
c2 <- upgrade c1

This compiles alright and also executes.

However I would like to do this polymorphically. But this doesn’t compile:

class Upgrader c0 c1 where
  upgrade : c0 -> Update c1

instance (Template c0, Template c1) => Upgrader c0 c1 where
  upgrade a1@(c0 with ..) = do
    let _ = a1
    a2cid <- create c1 with
      ..
    fetch a2cid

This results in the compiler error message

Source:   parser
Severity: DsError
Message:  error: You cannot use `..' in a record update

The error message is for the line upgrade a1@(c0 with ..) = do.

Can anyone explain what that means, and how to do it right?

Thanks a lot,
mesch.

Unfortunately, you cannot do that polymorphically. Record wildcards (that’s what the .. match is called) always have to match on a specific record.

What you could do is introduce a new typeclass that defines the conversion, implement that for all types you care about (for that you can use record wildcards) and then use that to do generic operations:

class Convertible a b where
  convert : a -> b

instance Convertible C1 C2 where
  convert (C1 with ..) = C2 with ..


instance (Template c0, Template c1, Convertible c0 c1) => Upgrader c0 c1 where
  upgrade c0 = do
    a2cid <- create @C1 (convert c0) 
    fetch a2cid

Thanks a lot, that confirms the suspicion I had. I just create all the instances then.

Thanks also for the proposed structure of the type classes; for the record, I had to make a few adjustments:

a2cid <- create @C1 (convert c0) with ..

caused the error

You cannot use `..' in a record update

Solved by dropping the with .. part.

instance (Template c0, Template c1, Convertible c0 c1) => Upgrader c0 c1 where

caused an error

The constraint ‘Converter c1 c2’
is no smaller than the instance head ‘Upgrader c1 c2’
(Use UndecidableInstances to permit this)

solved by adding

{-# LANGUAGE UndecidableInstances #-}

at the top.

Now,

instance (Template c0, Template c1, Convertible c0 c1) => Upgrader c0 c1 where

caused another error

Redundant constraint: Template c1
In the instance declaration for ‘Upgrader c1 c2’

Solved by dropping Template c1, .

With these adjustments, the code builds and runs.

Thanks again!

Cheers,
mesch.