The current migration pattern has the following signature
// A is the old state & fields (if declared in B is kept)
// B is the new state & fields
func<A, B>(old_state : A) : B // B
I’m curious why the old state wasn’t declared as a record, like this
func<A, B>({ state : A }) : B
Then it would be easier to extend with additional optional fields that one can pick out, similar to how the caller is parseable in a canister API
func<A, B, ...>({
state : A;
caller : Principal;
initArgs : ...
}) : B
Now that we have migrations, it might be helpful to have a primitive allowing a canister to infer and assign the type of all stable variables in a canister, and hold the canister to implement that collection of stable variables type.
For example, if I have the following
persistent actor Test {
let a = ...;
let b = ...;
...
let z = ...;
}
and then I want to do a migration on it, is there a way for me to pass type T, which is essentially the full typed record of stable variables a-z? Otherwise, I need to explicitly pass in every input to the migration, and all inputs must be included in the output of the migration or they are considered dropped.
Then you’d get something like this, but where you’d like to omit old parts of the state from the new record when copying things over.
type A = { a : Int; counter : Nat };
type B = { b : Float; counter : Nat };
func<A, B>({ state : A }) : B {
{ state with b = Float.fromInt(state.a) }
}
Exploring this was how I started running into the edge case errors mentioned here Pre-release `moc` 0.14.0 available in Playground - #18 by icme