How to update code and resolve resulting conflicts

Sometimes you'll need to make a change in the codebase that can't automatically be propagated to dependent functions, for example: if you alter a data constructor for a type or change the arguments to a function. Unison can programmatically guide you through the edits that you need to make in these cases.

Note, your code which calls the old versions of a function will not bebroken(it'll still run as originally implemented) but it might refer to the old version by hash reference, which is how you end up with cryptic terms that look like this:

exclaim : Text
exclaim =
    use Text ++
    #7i0ceb20mm 4 ++ "!!"

This document walks through a workflow for making changes to your codebase and resolving the resulting conflicts in a way that helps to preserve the human readable names for your functions. 🤖

Suggested workflow summary:

  1. forkthe namespace you'd like to edit
  2. make your changes in the forked namespace
  3. updatethe codebase
  4. runtodoto see dependencies to update
  5. editandupdatethe functions that the UCM suggests
  6. mergethe forked namespace back

Walk through an example

Let's say we have a simple type and a function which makes use of that type:

unique type Box = Box Nat

Box.toText : Box -> Text
Box.toText box = match box with
  Box.Box nat -> Nat.toText nat

Weaddit to the codebase, and then later, we may realize its data constructor should be changed. The first thing we should do isforkthe namespace to create a new namespace where we'll be making our changes.

.> cd myWork
.> add
myWork.> fork .myWork .newFeature

In our new namespace, let'seditthe type:

myWork.> cd .newFeature
.newFeature> edit Box

We've changed the data constructor from taking a value of typeNattoInt.

unique type Box = Box Int

After making the change to the type we'llupdatethe codebase, and see if we have any functions or types to adjust given our change with thetodocommand. Remember, we're making these updates in our forked copy of the original namespace.

.newFeature> update
.newFeature> todo

🚧

The namespace has 1 transitive dependent(s) left to upgrade.
Your edit frontier is the dependents of these definitions:

  unique type .myWork.Box

I recommend working on them in the following order:

1. toText : myWork.Box -> Text

We only have one todo item here, but in a more complicated codebase the suggested edit order from the UCM can be extremely helpful in prompting you to work from the "edges" of your codebase, working up the function call chain.

💡

You can tackle your "todo's" one by one, but did you know, you can also update all of your dependencies at once with theedit 1-nsyntax.

Maybe you want to edit a grouping of connected functions, but they aren't contiguous in the numbered list? You can combine theedit 1-nsyntax with a space separated list of terms you'd like to edit, for exampleedit 2-4 6 8oredit 1 4 7-9.

In this case, it looks like thetoTextfunction will need to be changed to point to theupdatedtype in this namespace and a few names and functions should be changed to handle theInt.Because we've kept around a copy of the old data type the old namespace, the UCM renders it with its name instead of a cryptic hash.

-- old code
Box.toText : myWork.Box -> Text
Box.toText = cases myWork.Box.Box nat -> Nat.toText nat

-- new version ✨
Box.toText : newFeature.Box -> Text
Box.toText = cases newFeature.Box.Box int -> Int.toText int

When you're satisfied with the changes in your forked namespace and there are no moretodoitems, you canmergethe new namespace into the old namespace to fully replace the old implementation

.> merge newFeature myWork

There you have it! You've updated your codebase with the help of the UCM! 🌻