How to update code and resolve resulting conflicts in a branch

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. branchoff of the project branch you'd like to edit before making the change.
  2. make your changes in the branch
  3. updatethe codebase
  4. runtodoto see dependencies to update
  5. editandupdatethe functions that the UCM suggests
  6. mergethe forked branch back

Walk through an example

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

unique type Box = Box Nat

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

Later, we may realize its data constructor should be changed. The first thing we should do isbranchfrom our working branch so we have a nice environment to make our changes in.

myProject/main> branch updateMyType

In our new branch, let'seditthe type:

myProject/updateMyType>
myProject/updateMyType> 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 a branch.

myProject/updateMyType> update
myProject/updateMyType> todo

🚧

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

  unique type Box

I recommend working on them in the following order:

1. toText : 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 branch 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 branch, the UCM renders it with the hash of its project prefixed before the name of the type (this is a temporary stop-gap) instead of an entirely cryptic hash.

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

-- new version ✨
Box.toText : Box -> Text
Box.toText = cases 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 branch into the old branch to fully replace the old implementation. We can clean up our branch structure with thedelete.branchcommand

myProject/updateType> merge /updateType /main
myProject/updateType> switch /main
myProject/main> delete.branch /updateType

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