Dune with MetaOCaml 5.3.0+BER help!

I’m trying to follow Oleg’s tutorials and papers on MetaOCaml and using the 5.3.0+BER switch. All I can find to help setup dune to use MetaOCaml is this Make dune works with metaocaml · Issue #1413 · ocaml/dune · GitHub which seems to have been around for some time. What is the current state of play? Is there somewhere I can find to help me get setup with dune - I am experiencing weird errors that mean nothing to me when I try and build any code:

File "_none_", line 1:              
Warning 58 [no-cmx-file]: no cmx file was found in path for module Runcode, and its interface was not compiled with -opaque
File "_none_", line 1:              
Error: No implementation provided for the following modules:
         Dynlink referenced from Runnative (/Users/simon/.opam/5.3.0+BER/lib/ocaml/runnative.cmx)

Thanks in advance for any instructions on making this work. Please be aware I am not that familiar with the build tools so I’d need exact detail of what incantations to put in whatever files. I’ve so far only ever fiddled with dune-project and dune files.

You may need to add something like this to the dune file:

(libraries dynlink metaocaml)

Thanks, that’s seems to have helped – just get a warning now:

Warning 58 [no-cmx-file]: no cmx file was found in path for module Runcode, and its interface was not compiled with -opaque

When I try and run tests I get the following - so it looks like I’m missing a further library?:

Error: No implementation provided for the following modules:
         Runcode referenced from Xxv__Sets (lib/xxv.cmxa)

xvv being my own project and Sets being a file/module therein.

No indeed runcode.cmx does not exist in the switch.

find ~/.opam/5.3.0+BER -name 'runcode.*'                                                                                5.3.0+BER
/Users/simon/.opam/5.3.0+BER/.opam-switch/sources/ocaml-variants.5.3.0+BER/ber-metaocaml-153/runcode.mli
/Users/simon/.opam/5.3.0+BER/.opam-switch/sources/ocaml-variants.5.3.0+BER/ber-metaocaml-153/runcode.ml
/Users/simon/.opam/5.3.0+BER/lib/ocaml/runcode.cmi

Is this expected? I wonder if the 5.3.0-BER switch not kosher. I’m going to try and cleanroom this in a fresh local switch.

The Runcode module is only available in bytecode; native code should use Runnative instead.

Runcode is packageed in metalib.cma:

$ ocamlobjinfo ~/.opam/5.3.0+BER/lib/ocaml/metalib.cma | grep Runcode
Unit name: Runcode
	9a06f5d5f83d46304534795ae4d692c9	Runcode

Thanks now I make progress! I’m using the right module for native :wink: however this is more of something implementation specific thing I don’t understand as this program is exactly Oleg/Ershov’s example…

dune exec xxv                                                                                 5.3.0+BER
Fatal error: exception Failure("The code built at File \"bin/main.ml\", line 15, characters 21-48 has hardly serializable CSPs: square at File \"bin/main.ml\", line 12, characters 47-53\n\nsquare at File \"bin/main.ml\", line 12, characters 47-53\n\nIt may be allowed in the future: do tell if you need it.")
let square x = x * x

let rec spower n x =
  if n = 0 then [%metaocaml.bracket 1]
  else if n mod 2 = 0 then [%metaocaml.bracket square [%metaocaml.escape spower (n/2) x]]
  else [%metaocaml.bracket [%metaocaml.escape x] * [%metaocaml.escape spower (n-1) x]]

let spower7_code = .<fun x -> .~(spower 7 .<x>.)>.

open Runnative
let spower7 = run spower7_code

let () =
  Printf.printf "%d" (spower7 7)

I may be missing the entire point here but shouldn’t that work?

You’ve encountered a limitation in the MetaOCaml implementation. The use of square within brackets is an instance of “cross-stage persistence”, because square is defined at one stage and used in a later stage. Cross-stage persistence of functions (closures) is not currently supported with native code compilation.

One workaround is to define square in a separate module, which will give it a globally-visible name, which can be saved in the generated code in place of the function value, and will work as expected with native code compilation.

There are more details here: https://okmij.org/ftp/ML/MetaOCaml.html#CSP

Thanks for the explanation and the reference- I think I’d seen that limitation w.r.t. data; putting those symbols in another module makes perfect sense.

Spoke too soon: I factored out the square function into another module. Which I checked could be opened and used from my main.ml. So module is ok I even generated an interface to be sure… I could get the MetaOCaml to generate and run some code ok if I did not use the square function at all. Now using the square function from this other module results in the following:

simon@eta [xxv]> dune exec xxv                                                                                                                                                                                                                                                                                           5.3.0+BER
File "/var/folders/q6/nff8jbj523s0cy2xz9rchh1r0000gn/T/runnadc6e8.ml", line 3, characters 43-59:
3 |                                           (Xxv.Extra.square
                                               ^^^^^^^^^^^^^^^^
Error: Unbound module Xxv
Fatal error: exception Sys_error("/var/folders/q6/nff8jbj523s0cy2xz9rchh1r0000gn/T/runnadc6e8.cmi: No such file or directory")
Raised by primitive operation at Runnative.compile_source in file "runnative.ml", line 40, characters 12-22
Called from Stdlib__List.iter in file "list.ml", line 112, characters 12-15
Called from Runnative.compile_source in file "runnative.ml", line 40, characters 2-34
Called from Runnative.run_native in file "runnative.ml", line 84, characters 21-56
Called from Dune__exe__Main.spower7 in file "bin/main.ml", line 11, characters 14-30
simon@eta [xxv]> more /var/folders/q6/nff8jbj523s0cy2xz9rchh1r0000gn/T/runnadc6e8.ml                                                                                                                                                                                                                                     5.3.0+BER
Runnative.result__ := Some (Obj.repr (fun x_1 ->
                                        x_1 *
                                          (Xxv.Extra.square
                                             (x_1 *
                                                (Xxv.Extra.square (x_1 * 1))))))

I thought maybe installing the package in the current switch might fix that but to no avail.

Also tried putting the module in the bin directory the runtime compilation could not still not find the module. I guess that’s a wrap for me as I can make no further progress as is; maybe some paths need to be setup for the runtime compilation to succeed. I guess this would be a general problem for all JIT style module resolution. Maybe inlining? Anyway as far as I see it from a naive users point of view:

  • Runnative vs. Runcode - need to decide upfront (in the source) which runtime is targeted. (would need some macro to detect this e.g. when loading into utop?)
  • No tooling support for MetaOCaml syntax: seems ocamlformat, Merlin (at least) need updating
  • Outstanding issue with runtime compilation dependent on any user modules (seems ok for stdlib tho’)

There is a PR to support metaocaml in Ocamlformat Initial support for metaocaml by hhugo · Pull Request #2630 · ocaml-ppx/ocamlformat · GitHub