How to use a prebuilt OCaml string as an argument to Toplevel directive #use?

This is really a side question to OCaml programming, just to get a deeper understanding of the Toplevel directives.

When using OCaml’s Toplevel, one may sometimes use some directives such as #load "foo.cmo", #use "foo.ml" or #mod_use "foo.ml" .

The normal way to go is to give a direct string argument to the #use directive:

#use "/home/test/ocaml/lists.ml";; (* OK *)

But giving an OCaml string argument to the #use directive doesn’t work:

Sys.chdir "/home/test/ocaml";;
let s = ( Sys.getcwd () ) ^ "/" ^ "lists.ml";;
(* or *) 
let s = "/home/test/ocaml" ^ "/" ^ "lists.ml";;
  val s : string = "/home/test/ocaml/lists.ml"

#use s;; (* NO *)
(* or *)
#use ( Sys.getcwd () ) ^ "/" ^ "lists.ml";; (* NO *)
  Wrong type of argument for directive `use'.

#use ( ( Sys.getcwd () ) ^ "/" ^ "lists.ml" );; (* NO *)
  #use (Sys.getcwd ())^"/"^"lists.ml";;
        ^^^
  Error: Syntax error: operator expected.

How can we tweak that to feed a Toplevel directive such as #use with a prebuilt OCaml string?

++++++++
Side-side question:
is there a way/ a command to get ALL the defined types and bound values of the current environment (within some Toplevel session)?

You can call the directive directly from the Topdirs module:

Topdirs.dir_use Format.std_formatter s;;

You can access Toploop.toplevel_env. I’m not sure what the best way to iterate on it is, but maybe calling Env.summary then traversing the summary manually would work for you.

2 Likes

Nice. Thanks.

It looks like Toploop.use_file is equivalent to Topdirs.dir_use

Toploop.use_file Format.std_formatter s;;

I could find #mod_use equivalent in module Toploop:

Toploop.mod_use_file Format.std_formatter s;;

and also #load in Topdirs;

let s' = "/home/test/ocaml/lists.cmo"
Topdirs.load_file Format.std_formatter s';;

Concerning traversing the summary, I cannot get values from the module Env:

let e = ! Toploop.toplevel_env;
  val e : Env.t = <abstr>
Env.summary e ;;
  Error: Unbound module Env

Where is this module Env located?
It has documentation: Env · ocaml-base-compiler 4.14.0 · OCaml Packages

1 Like

Ah, sorry, it’s one of these little quirks of the toplevel I forgot about. The toplevel has access to the Env module, but doesn’t re-export it, so you need to load it again yourself. In practice, #load "ocamlcommon.cma";; should do that.

1 Like

Here is what I get:

#load "ocamlcommon.cma";;
(* NO : Cannot find file ocamlcommon.cma. *)

#load "/home/test/.opam/4.07.1/lib/ocaml/compiler-libs/ocamlcommon.cma";;
(* OK ? No error message... *)

Env.summary

Characters 0-11:
  Env.summary;;
  ^^^^^^^^^^^
Error: Unbound module Env

On github, I can find typing/env.mli (4.07.1) that seems to mach the documentation for Env.summary etc..

Have you an idea of the involved module hierarchy under the hood ?
Also, these Toplevel modules seem (voluntarily?) under-documented.

It seems that for some reason, on 4.12.0 you can load ocamlcommon.cma directly, so I forgot to add the extra #directory "+compiler-libs";;. With that extra directory, you should be able not only to load ocamlcommon.cma without the path, but also to use its components (without the directive the toplevel can’t find the corresponding cmi files).

It works (4.07.1). Thanks.

#directory "+compiler-libs";;
#load "ocamlcommon.cma";; (* OK ; but no feedback *)

#show Env;;

module Env :
  sig
    module PathMap : sig ... end
    type summary =
        Env_empty
      ...

Env.summary

- : Env.t -> Env.summary = <fun>

I tested that it’s required (and enough) to set the directory to get the signature of module Env:

#directory "+compiler-libs";;
#show Env;;

module Env :
  sig
  ...

But #load is still required to get access to the implementation of module Env:

Env.summary

Characters -1--1:
  Env.summary;;
  
Error: Reference to undefined global `Env'

#load "ocamlcommon.cma";; (* no feedback *)

Env.summary

- : Env.t -> Env.summary = <fun>

a findlib alterative is #require "compiler-libs.common";; which should handle all this for you.

2 Likes

Yes, and this directive offers the useful and expected feedback:

#require "compiler-libs.common"
/home/lm/.opam/4.07.1_ocsigen/lib/ocaml/compiler-libs: added to search path
/home/lm/.opam/4.07.1_ocsigen/lib/ocaml/compiler-libs/ocamlcommon.cma: loaded

Env.summary

- : Env.t -> Env.summary = <fun>
1 Like

In addition to the very basic information about Toplevel directives in refman and to the ultra basic information from #help;; (topfind), I was wondering if there a place where this is fully documented.

It looks like camlcity.org/Findlib is the expected place.
Is that right? Or are there other places “to know all about Findlib and related tools” ?
Maybe @gstolpmann (author of Findlib) or @samoht (maintainer) (see opam info ocamlfind) can enlighten us about that?

I agree, this needs more visibility. While most functions of Toploop and Topdirs, etc. seem very advanced, the functions for adding directories, loading .cmo files and using .ml files allow to completely eschew using toplevel directives #bla, which also eschews the need for ;; before/after them.

1 Like

The problem is that you are not really looking a public interface here.

That explains why some functions look a bit too general for basic usage (having a formatter as first parameter). Would having a simplified, select subset of these function as a public module, so that #command;; could be deprecated, be an interesting OCaml RFE ?

Not only would this bring the syntax in line, they are also more powerful (taking real OCaml string, variables, etc.).