# Refactoring tools for OCaml? Type-based refactoring?

**URL:** <https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206>\
**Category:** Ecosystem\
**Tags:** refactoring\
**Created:** [July 27, 2021, 7:56am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206 "2021-07-27T07:56:05Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![aryx](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/aryx/32/2356_2.png) [@aryx](https://discuss.ocaml.org/u/aryx)\
**Post date:** [July 27, 2021, 7:56am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/1 "2021-07-27T07:56:05Z")

</div>

Hi,

What are the current tools to help refactor OCaml code?

I have a very heavy refactoring where I previously defined an AST like  
type expr =  
| Int of int  
| String of string  
| Plus of expr \* expr

and I need to add some extra information at each node, like  
type expr = { e: expr\_kind; id: int }  
and expr\_kind =  
| Int of int  
| String of string  
| Plus of expr \* expr

That means old code like  
match e with  
| Int i → …  
must become  
match e.e with  
| Int i → …

and each time I return an expression, I must now encapsulate it with a constructor function.  
For example  
match stuff with  
| Whatever → Int 2

must now become  
match stuff with  
| Whatever → mke (Int 2)

where let mke ekind = { e = ekind; id = gensym() }

There are lots of files involved, so I’d rather use a tool that can help automate some of it.

---

<div class="post-metadata">

**Author:** ![aryx](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/aryx/32/2356_2.png) [@aryx](https://discuss.ocaml.org/u/aryx)\
**Post date:** [July 27, 2021, 7:57am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/2 "2021-07-27T07:57:48Z")

</div>

Right now the only solution I see is to abuse the LSP server to identify all  
the places programmatically and combine that with some emacs kungfu (macros)  
to help automate most of it (and then rely on the typechecker to manually tune things).

---

<div class="post-metadata">

**Author:** ![talex5](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/talex5/32/353_2.png) [@talex5](https://discuss.ocaml.org/u/talex5)\
**Post date:** [July 27, 2021, 9:02am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/3 "2021-07-27T09:02:10Z")

</div>

There’s the [rotor](https://gitlab.com/trustworthy-refactoring/refactorer/-/blob/master/README.md) tool, which was demoed at last year’s OCaml workshop and looked very promising ([API migration: compare transformed (OCaml 2020) - ICFP 2020](https://icfp20.sigplan.org/details/ocaml-2020-papers/7/API-migration-compare-transformed)).

However, it only supported old versions of OCaml when I tried it, and there doesn’t seem to have been any recent activity.

---

<div class="post-metadata">

**Author:** ![aryx](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/aryx/32/2356_2.png) [@aryx](https://discuss.ocaml.org/u/aryx)\
**Post date:** [July 27, 2021, 9:35am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/4 "2021-07-27T09:35:32Z")

</div>

It looks mostly restricted to renaming though? Could it perform the refactoring  
on ASTs I presented above?

---

<div class="post-metadata">

**Author:** ![Yaron\_Minsky](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yaron_minsky/32/11_2.png) [@Yaron\_Minsky](https://discuss.ocaml.org/u/Yaron_Minsky)\
**Post date:** [July 28, 2021, 12:28am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/5 "2021-07-28T00:28:46Z")

</div>

I know of nothing that you could use. Internally at Jane Street, @ceastlund has built some internal tools that do what you suggest: basically run a build, observe build errors, and apply transformations based on the returned errors. But I don’t know of any generally available versions of said tools.

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 28, 2021, 5:39am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/6 "2021-07-28T05:39:05Z")

</div>

> [@aryx](#):
>
> There are lots of files involved, so I’d rather use a tool that can help automate some of it.

As others have said, I don’t think there is anything publicly available for this kind of thing. When we have had to make large-scale refactorings like this at LexiFi, we have sometimes written ad-hoc automatic tools to help us. It is not as hard as one would think 🙂

The easiest case is when the transformation can be phrased in purely syntactic terms. In this case, you can write a tool using `compiler-libs` that parses each file, obtaining the corresponding Parsetree. Then you perform two passes. First, then you walk through the Parsetree, keeping track of the the places where you need to change something: in you situation, it would be the match scrutinee and the final expression of each match case. Second, you do a “rewriting” pass where you use the information that you obtained in the previous pass to textually insert the new code fragment in the original source files.

If the transformation cannot be phrased in purely syntactic terms, then you need to combine it with reading the `.cmt` files which contain the type-annotated Typedtree in order to find the places that need rewriting, and it is a bit more involved, but the overall logic remains the same.

Of course, it is so easy to do this kind of refactoring “by hand” just by following the compiler errors that the investment of writing this kind of tools is only worth it if you have a large codebase to refactor. It was worth it for us at LexiFi (~600k LOC), but for more reasonably-sized codebases the tradeoff may be less clear.

Cheers,  
Nicolas

---

<div class="post-metadata">

**Author:** ![UnixJunkie](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unixjunkie/32/638_2.png) [@UnixJunkie](https://discuss.ocaml.org/u/UnixJunkie)\
**Post date:** [July 28, 2021, 7:08am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/7 "2021-07-28T07:08:02Z")

</div>

Even a tool to detect and remove dead code from sources would be useful.  
I.e. you want to release some code from a prototype: everything not reachable  
from main() should be removed.  
For the mental sanity of people who will try to read the code later…

---

<div class="post-metadata">

**Author:** ![nojb](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/nojb/32/519_2.png) [@nojb](https://discuss.ocaml.org/u/nojb)\
**Post date:** [July 28, 2021, 8:01am UTC](https://discuss.ocaml.org/t/refactoring-tools-for-ocaml-type-based-refactoring/8206/8 "2021-07-28T08:01:58Z")

</div>

For global dead code elimination (but note this is not exactly the same as “everything not reachable from main() should be removed”), I know of

- [GitHub - rescript-association/reanalyze: Experimental analyses for OCaml/ReScript: for globally dead values/types, exception analysis, and termination analysis.](https://github.com/rescript-association/reanalyze)
- [GitHub - LexiFi/dead\_code\_analyzer: Dead-code analyzer for OCaml](https://github.com/LexiFi/dead_code_analyzer) (not currently being worked on; would need to be adapted to work with the latest OCaml),

Cheers,  
Nicolas
