Reload modules in utop?

Hi, suppose I, a noob learner do:

  • dune utop
  • open Lib;;
  • play a bit
  • modify sources for Lib from a different terminal

Is there a way for me to have the code in utop updated?

P.S. I’m actually doing M-x utop in Emacs and specify dune utop .. -- -emacs as my command; this allows me to do open Lib;; but not #require "Lib";; and thus presumably I do not benefit from Topfind.reset();; Am I using utop wrong?

hmm… seems one way is to

questions:

  • is that indeed the only way?
  • indeed no “hot code update”?
  • does it even make sense to run that in Emacs?
  • what’s the best setup to invoke it from tuareg mode?

btw I put the following .dir-locals.el into lib subfolder of my project

((tuareg-mode .
    ((utop-command . "opam config exec -- dune utop .. -- -emacs"))))

but somehow M-x utop still offers utop -emacs as the utop command when editing lib/math.ml located in that folder… any help will be appreciated…

[it’s possible that what I’m writing below is incorrect and has been superseded by new developments in the Ocaml tooling. But I kinda doubt it.]

I’m not sure what you’re asking, but … depending on what you mean by “reload”, it’s possible that what you’re asking for is impossible.

(1) Ocaml is a lexically-scoped language, and adheres pretty carefully to the “don’t modify things that are not marked as mutable”.

(2) module-level bindings (name-bindings in general) are not mutable

(3) So perhaps what you mean is that you want to load some code (e.g. a “.cmo”), modify/recompile, and load that “.cmo” again? This is possible (or at least, I’ve done it with “.ml” files, e.g.

# #use "foo.ml" ;;

several times in a single toplevel ocaml instance. It’s been so long since I did it with a “.cmo” that I forget whether that works, but I’d be surprised if it didn’t. And of course, I’d expect that you could load a bunch of code that way in series.

(4) BUT this is just loading new code, that shadows/obscures previously-loaded code with the same names, right? It’s not/NOT “reloading”. If what you want is (e.g.) to load modules A,B,C, then modify module A and somehow cause it to be reloaded so that B,C “automatically” see the new version of A, I think you’re out-of-luck.

This is a basic principle of ML-like languages, at play here. The idea is, instead of making it possible to “persistently update” some complex dependency-graph of code and objects, ML-like languages try to make it efficient to bootstrap that graph from scratch.

(5) I should add that of course all the tooling I know of, supports this model. If you were to load an ocamlfind module, then modify/reinstall it, and try to load it again, I can’t imagine any results other than chaos … complete chaos.

(6) BTW, there are other languages that support such recompile/reloading. There are always significant limitations (e.g. in Java, Erlang) and you can always get yourself wedged, sometimes with great ease and hilarity ensuing (e.g. Java). This stuff has existed since the days of LISP, and always with interesting caveats. Which is why, I suspect, the ML languages just don’t bother.

1 Like

Thx a bunch, yes, I’m a Java boy learning his ropes :slight_smile:

…actually case in point how do you guys quickly (re-)start interactive top-level? say using dune?

  • via a 2nd terminal?

    #quit;;/dune utop
    ?

  • via emacs?

  • .dirs-local.el like this

    ((nil . ((utop-command . "dune utop .. -- -emacs"))))

    helps but is putting a similar file into each folder the best way?

  • is there a nice way to re-start dune utop running in Emacs?

    #quit;;/C-x o/M-x utop

    ?..

I don’t mind Visual Studio Code + vscode-reasonml. Haven’t figure out if it supports dune, nor found a way to build/run utop/debug though…

Apologies for the barrage…

I can’t answer for how others do it. And I don’t use any of these new tools/wrappers, so maybe there’s something better. What I do:

(0) I use emacs+tuareg, and use the toplevel from inside emacs.

(1) I maintain in each project an “include_ml” file that contains all the toplevel directives to get my project all loaded. E.g. getting “topfind” in, requiring all my prereqs, etc. Maybe even running some code to set things up.

(2) Each time I run “make”, I go to one of my source-files, type C-c C-s (which brings up the “OCaml” interactive buffer), switch to it, hit C-d RET (which terminates the process, then switch back to the source-file, hit C-c C-s RET (which restarts ocaml), go back to the interactive buffer, and hit M-p until I see the use-directive for my “include_ml”

(3) meanwhile, the build finished. If it succeeded, I hit RET on that include-directive, and soon thereafter, I’m back where I need to be, to continue debugging.

Don’t know if that helps. At this point, it’s pretty automatic in my fingertips, so I don’t notice.

1 Like

Thx a lot, big help!

Somehow this doesn’t kill utop in my Emacs
#quit;; works but is more keys to press

Even better: C-c C-s RET in dead utop buffer apparently restarts even w/o buffer switching!

I’ve also been very happy to find that (put 'utop-command 'safe-local-variables #'stringp) in ~/.emacs helps avoid Emacs asking me to confirm I really want to set variables via .dir-locals.el on file open; it’s a little beyond me why I don’t have to do the same for 'compile-command even though I set both in .dir-locals.el

Ha! It’s been so long since I wrote any emacs-lisp, that I just got used to this lossage. Your comment prompted me to go make the (one-line) change to my emacs init file, to add this binding to the standard tuareg-interactive-mode-map. Nice! Thank you!

…another mystery: I’m in control of what C-c C-c runs
but how do these commands find errors?..

C-x C-s
C-c C-x

Update: C-x C-s error checking resembles C-x C-r
which can also be activated from Merlin menu…