Skip to content


Below several examples of migrations are offered.

Introduce a new protocol property

Consider the following protocol.

protocol[a] Before(var field1: Text) {};

Property field2 of type Number must be added. First, the original definition is replaced by the following definition.

protocol[a] After(var field1: Text, var field2: Number) {};

This new field is populated with the value in the following migration.

migration("replace").transformProtocol(beforeTypeId, afterTypeId) {
    put("field2") {

Introduce a new protocol party

Consider the following protocol.

protocol[p1] Foo0() { };

Party p2 must be added. First, the original definition is replaced by the following definition.

protocol[p1, p2] Foo1() { };

This new party is added in the following migration.

    currentTypeId = MigrationTester.typeId("Foo0"),
    targetTypeId = MigrationTester.typeId("Foo1"),
) { parties(parties.single(), createParty(p2)) },

Replace an existing protocol property value

Consider the following protocol.

protocol[a] Foo(var n: Text) {};

Property n must be changed to some other value. This is accomplished in the following migration:

    currentTypeId = MigrationTester.typeId("Foo"),
    targetTypeId = MigrationTester.typeId("Foo"),
) {
    replace<TextValue>("n") {

Replace an existing protocol symbol value

Consider the following symbol and protocol definition.

symbol pas;
protocol[p] Foo(var u: pas) {};

If the current value of u is 0, then the value of u must be changed to 20. That is accomplished in the following migration:

migration("modify symbol value")
    ) {
        if (get<SymbolValue>("u") ==
            SymbolValue(NumberValue(0), NameType("pas"))
        ) {
            replace<SymbolValue>("u") {
                createSymbolValue(20, "pas")

Introduce an identifier property

Consider the following struct and protocol definition.

struct FavoriteStructId { id: Number };

protocol[owner] Before(firstItem: Text) {
    var favorites: Map<FavoriteStructId, Text> = mapOf<FavoriteStructId, Text>()
        .with(FavoriteStructId(1), firstItem);

    permission[owner] addTitle(title: Text) { /* ... */ }

You would like to replace the user-defined struct definition with an identifier.

identifier FavoriteId;

protocol[owner] After() {
    var favorites: Map<FavoriteId, Text> = mapOf<FavoriteId, Text>();

    permission[owner] addTitle(title: Text) { /* ... */  }

This is accomplished in the following migration:

migration("transform user-defined value to a identifier value")
    ) {
        replace<MapValue>("favorites") { oldMap -> // map of Pair<Number, Text>
            val mapWithTransformedKeys = oldMap.value.mapKeys {

Delete an existing protocol property

Consider the following protocol.

protocol[a] Before(var x: Number) {};

Property x must be removed. First, the existing definition is replaced by the following definition.

protocol[a] After() {};

This field is removed in the following migration.

    .transformProtocol(beforeTypeId, afterTypeId) {

Collecting data using read

Reading values

Consider the following code.

protocol[a] Tiny(var n: Number) {};

We can collect the values of n from all instances of the protocol in the following migration.

val collectedNumbers = mutableListOf<NumberValue>()
    .read(MigrationTester.typeId("Tiny")) {
        collectedNumbers += get<NumberValue>("n")

collectedNumbers can then be used in transformations within the same migration.

Reading unions

Consider the following code.

union U { Text, Number };
protocol[p] Foo(var u: U) {};

The values u need to be collected in a step prior to an actual migration. The following migration accomplishes this.

val texts = mutableListOf<TextValue>()
val numbers = mutableListOf<NumberValue>()
    .read(MigrationTester.typeId("Foo")) {
        when (val v = get<Value>("u")) {
            is NumberValue -> numbers.add(v)
            is TextValue -> texts.add(v)
            else -> throw RuntimeException("Expected union value ${v::class}")

Note that if these do not need to be organized by type, they could also be stored in a List<Value> without the use of a match-statement.

Creating new protocol instances using existing protocol data

Consider the following protocol.

protocol[p] Baz(var x: Text) { };

A new protocol Qux must be created that uses a value from Baz.

protocol[p] Qux(var x: Text) { };

The following migration emits a new protocol Qux, while Baz continues to exist.

    ) {
        val x = get<TextValue>("x")
        createProtocol(MigrationTester.typeId("Qux"), parties, listOf(x))

Replacing protocol instances using existing protocol data

Consider the following protocol.

protocol[p] Bar(var field1: Text) { };

It is to be replaced by a new protocol Foo that has the same value(s) as Bar.

protocol[p] Foo(var field1: Text) { };

The following migration emits a new protocol Foo, while Bar ceases to exist. Foo therefore gets Bar's identifier.

        currentTypeId = MigrationTester.typeId("Bar"),
        targetTypeId = MigrationTester.typeId("Foo"),

The operation of transformProtocol and its various modes of operation are discussed in more detail here.

Split a protocol

Consider the following protocol.

protocol[p] Foo(var field1: Text, var field2: Number) { };

Two new protocols need to replace this protocol: Bar, which gets Foo's identity, and Baz, which is a new protocol altogether.

protocol[p] Bar(var field1: Text) { };

protocol[p] Baz(var field2: Number) { };

The following migration replaces Foo with Bar, and also creates a new protocol Baz.

    currentTypeId = MigrationTester.typeId("Foo"),
    targetTypeId = MigrationTester.typeId("Bar"),
) {
    val field2 = get<NumberValue>("field2")
    createProtocol(MigrationTester.typeId("Baz"), parties, listOf(field2))

A migration with multiple changes

Consider the following protocol.

identifier KeyId;

protocol[p] Foo1(var id: Number, var beta: Number) {
    initial state red;
    state green;

    var gamma: Text = "gamma";
    var keyId: KeyId = KeyId();

The quite different protocol below is intended to replace it.

identifier Id;

protocol[p] Foo2(var beta: Text) {
    initial state wind;
    state fire;

    var zeta: Number = -1;
    var id: Id = Id();

Note the use of replace, delete, put, get and state within one migration.

    ) // rename identifier type
    .transformProtocol(MigrationTester.typeId("Foo1"), MigrationTester.typeId("Foo2")) {
        // `beta` is changed into a string.
        replace<NumberValue>("beta") { oldBeta ->

        // `gamma` is deleted and `zeta` is added (keep the old `id` Number value)
        put("zeta") { get<NumberValue>("id") }

        // rename identifier field from `keyId` to `id` (keep the old `keyId` identifier value)
        put("id") {
            withTransformations(get<IdentifierValue>("keyId")) { it }

        // map the states
        state("red" to "wind", "green" to "fire")

Using withTransformations

Consider the following definitions.

struct Bar1 { n: Number };

protocol[p] Foo1() {
    contents: List<Bar1> = listOf<Bar1>(Bar1(n = 2));

The following definitions need to replace the existing definitions. Note how not only does Bar1 change to Bar2, but also turns from a List into a Set.

struct Bar2 { n: Number };

protocol[p] Foo2() {
    // This is now a Set, rather than a List
    contents: Set<Bar2> = setOf<Bar2>();

    permission[p] c() returns Number {
        return 42;

The following migration accomplishes this:

    .transformStruct(MigrationTester.typeId("Bar1"), MigrationTester.typeId("Bar2"))
    .transformProtocol(MigrationTester.typeId("Foo1"), MigrationTester.typeId("Foo2")) {
        replace<ListValue>("contents") { old ->
            // Change List to Set, and invoke the
            // registered transformation for the struct
            withTransformations(old) {
                // Due to withTransformations, 'old' is now a List of Bar2.
                // It does not know how to change the List to a Set.
                createSet(it.value, type(MigrationTester.typeId("Bar2")))

Note how type("Bar2") is used to retrieve the type reference of the Bar2 struct, which is a user-defined type.

Using type

Consider the following protocol:

protocol[p] Before(var fooBar: Option<FooBar>) {};

The protocol makes use of a user-defined type FooBar. FooBar is defined as a struct containing a Boolean and a Number. There are also a couple of other user-defined types.

struct FooBar { foo: Boolean, bar: Number };

union Version { Number, Text };
struct VersionedFooBar { fooBar: FooBar, version: Version };

We now want our protocol to use versioned FooBars (Version being either a version Number or Text in this example). We also want it to take another parameter, which is a list of Versions:

protocol[p] After(var fooBar: Option<VersionedFooBar>, var baz: List<Version>) {};

The following migration transforms Before into After so that it uses VersionedFooBar rather than FooBar. It also adds our new parameter (and populates the new field with a couple of bonus Version values).

Notice especially how we use the type function here to resolve the user-defined types. In the case of baz, this is used to explicitly state that the elements of the list have a union type. Notice also how we do not need to provide createOption with a type -- if we leave it out, it can be inferred. As a rule of thumb this can be done whenever we're not dealing with elements that have union types -- but if we are, it is better to specify the type in order to avoid unforeseen consequences.

migration("type example")
    .transformProtocol(beforeTypeId, afterTypeId) {
        replace<OptionValue>("fooBar") { oldFooBar ->
            when (oldFooBar.value) {
                null -> createEmptyOption(type("/testpkg/VersionedFooBar"))
                else -> createOption(
                            "fooBar" to oldFooBar.value!!,
                            "version" to createText("NONE"),
        put("baz") {
                listOf(createText("RELEASE"), createNumber(1)),