Is there a "dune utop" that works more like utop's #use?

First, is there comprehensive documentation available for utop? I can’t seem to find any. I am using the CS3110 book (2.1. The OCaml Toplevel — OCaml Programming: Correct + Efficient + Beautiful) and information I glean from various blog posts such as opam - UTop: a much improved interface to the OCaml toplevel and Introduction to the OCaml Toplevel · OCaml Documentation.

I want to use a workflow like the #use "code.ml" one described in the CS3110 book, but for more complex code that depends on a few libraries and multiple ppx. Currently if I #use my .ml files in utop they bug out with various errors related to ppx processing and missing libraries.

I haven’t successfully figured out how to get utop loading this code in a way that is all of these things:

  1. convenient (in the sense that the incantations required to load my code come from dune using at most one command)
  2. functional (in the sense that the code loads at all)
  3. allows me to interact with the code as if I were in the unsealed module (in the way you can after using #use in utop)

I have been able to load my module using dune’s integration with utop, but this seems to load the sealed module. For example, I can’t call functions that aren’t exported. This is inconvenient, because my goal is to interactively explore and debug the module.

Taking it down a level, I can reproduce my problem with a minimal test case, using instructions from Command-Line Interface — Dune documentation

$ dune init project --kind=l myproj
$ cd myproj
$ echo 'let i = 42' > lib/code.ml
$ dune build

Now, I’d like to use something like dune utop to load this library such that entering i;; into utop will print the value 42.

Following Command-Line Interface — Dune documentation I try:

$ dune lib

but…

utop # module S = Myproj;;
modle S = Myproj

…in other words, it looks like Myproj doesn’t export i. I’ve also tried dune lib -- -implicit-bindings but it makes no difference. The dune docs say -implicit-bindings means there will “implicit bindings for toplevel expressions” but I’m not sure what that means.

The alternative also produces the same problem:

$ utop
utop # #use_output "dune ocaml top-module lib/code.ml";;
utop # i;;
Error: Unbound value i
utop # module S = Myproj;;
module S = Myproj

This makes sense to me. utop is loading my module, but the module, having no .mli, is apparently completely sealed.

Of course, with an example as simple as this, I can #use "code.ml" in utop. In my actual use case my dune file is non-trivial, and so the utop setup is also non-trivial. For example:

(library
 (name day07)
 (inline_tests)
 (libraries str)
 (preprocess (pps ppx_jane ppx_deriving.show)))

I’m not sure what you mean by “sealed”, but i is accessible:

$ utop
utop # #use_output "dune ocaml top-module lib/code.ml";;
utop # module S = Myproj;;
module S = Myproj
utop # S.Code.i;;
- : int = 42

Or:

$ dune utop
utop # open Myproj.Code;;
utop # i;;
- : int = 42

@lastgenius, thanks, I never would have guessed! Do you know where the Myproj.Code submodule (for lack of a better description) is documented? Is this a feature of utop or a language feature of OCaml itself? This may go back to my question about where to find documentation for utop.

On the contrary, when a module doesn’t have an .mli, it’s completely unsealed.

1 Like

Modules · OCaml Documentation goes over the basics of modules and submodules (you are right, and not for the lack of a better description, that’s its name!).

1 Like

Ah, I am embarrassed to admit that one of my misconceptions was so obvious that nobody thought to point it out. :person_facepalming:

The “Code” module in my example project is named after the file name in my example: code.ml

I had read that this would be so multiple times in documentation, which is why re-reading the same documentation didn’t help me. When it came time to exercise this for the first time myself, I was not making the conceptual leap. I was stuck in a “package per directory” mindset. Perhaps I was stuck in a quantum state, thinking I was writing Go or Java and OCaml simultaneously. :wink: