Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why "Reference to undefined global `Moduletest'" in OCaml?

I wrote

let fact x = 
let result = ref 1 in
for i = 1 to x do
    result := !result * i;
    Printf.printf "%d %d %d\n" x i !result;
done;
!result;;

in a file named "Moduletest.ml", and

val fact : int -> int

in a file named "Moduletest.mli".

But, why don't they work?

When I tried to use in ocaml,

Moduletest.fact 3

it told me:

Error: Reference to undefined global `Moduletest'

What's happening?

Thanks.

like image 491
Zhipeng YANG Avatar asked Dec 19 '22 09:12

Zhipeng YANG


1 Answers

OCaml toplevel is linked only with a standard library. There're several options on how to make other code visible to it:

  1. copy-pasting
  2. evaluating from the editor
  3. loading files #use directive
  4. making custom toplevel
  5. loading with ocamlfind

Copy-pasting

This self-describing, you just copy code from some source and paste it into toplevel. Don't forget that toplevel won't evaluate your code until you add ;;

Evaluating from the editor

Where the editor is of course Emacs... Well, indeed it can be any other capable editor, like vim for example. This method is an elaboration of the previous, where the editor is actually responsible for copying and pasting the code for you. In Emacs you can evaluate the whole file with C-c C-b command, or you can narrow it to a selected area with C-c C-r, and the most granular is to use C-c C-e, i.e., evaluate an expression. Although it is slightly buggy.

Loading with #use directive.

This directive accepts a filename, and it will essentially copy and paste the code from the file. Notice, that it won't create a file-module for you/ For example, if you have file test.ml with this contents:

(* file test.ml *)
let sum x y = x + y 

then loading it with the #use directive, will actually bring to your scope, sum value:

# #use "test.ml";;
# let z = sum 2 2

You mustn't to qualify sum with Test., because no Test module is actually created. #use directive merely copies the contents of the file to the toplevel. Nothing more.

Making custom toplevels

You can create your own toplevel with your code compiled in. It is an advanced theme, so I will skip it.

Loading libraries with ocamlfind

ocamlfind is a tool that allows you to find and load libraries, installed on your system, into your toplevel. By default, toplevel is not linked with any code except standard library. Even, not all parts of the library are actually linked, e.g., Unix module is not available, and needed to be loaded explicitly. There're primitive directives that can load any library, like #load and #include, but they are not for a casual user, especially if you have excellent ocamlfind at your disposal. Before using it, you need to load it, since it is also not available by default. The following command, will load ocamlfind and add few new directives:

# #use "topfind";;

In a process of loading it will show you a little hint on how to use it. The most interesting directive, that is added is #require. It accepts a library name, and loads (i.e., links) its code into toplevel:

# #require "unix";;

This will load a unix library. If you're not sure, about the name of the library you can always view all libraries with a #list command. The #require directive is clever and it will automatically load all dependencies of the library.

If you do not want to type all this directives every time you start OCaml top-level, then you cam create .ocamlinit file in your home directory, and put them there. This file will be loaded automatically on a top-level startup.

like image 85
ivg Avatar answered Jan 07 '23 11:01

ivg