OCaml: How to Run a Script with Libraries Included - ocaml

I'm following the Real World OCaml book to learn OCaml and many of the programs require using the Jane Street Core library. When I use functions from this core library in the toplevel, it works great. There, I just use the following commands to open the Core library.
$ #use "topfind";;
#thread;;
#camlp4o;;
#require "core.top";;
#require "core.syntax";;
open Core.Std;;
Then I can run this program line by line in the toplevel and functions like String.split work fine.
# let languages = "OCaml,Perl,C++,C";;
val languages : string = "OCaml,Perl,C++,C"
# let dashed_languages =
let language_list = String.split languages ~on:',' in
String.concat ~sep:"-" language_list
;;
val dashed_languages : string = "OCaml-Perl-C++-C"
But if I put it all in a script, how do I make the compiler recognize the core libraries? So if I'm running the same program in a script:
open Core.Std;;
open Str;;
let languages = "OCaml,Perl,C++,C";;
let dashed_languages =
let language_list = String.split languages ~on:',' in
String.concat ~sep:"-" language_list
;;
Then I compile it:
ocamlopt -o test test.ml
I get an error:
Error: Unbound module Core
So clearly Core is not being recognized. I can't put the #use and #require commands in my script because OCaml doesn't recognize those. So how can I use Core?

Since you're following Real World OCaml, you may want to use corebuild to compile executables since this is what the book recommends. In this case the command corebuild test.native should correctly compile your script. If you use libraries other than Core, you'll need to specify them with the -pkg option.
Also, note you can omit the double semicolons within a .ml file.

Make sure you have ocamlfind. Also, make sure you have the right environment for it to find packages. If you installed with opam, run "eval opam config env". Then:
ocamlfind ocamlopt -linkpkg -thread -package core test.ml -o test

Related

Syntax error when using batteries in Utop

I've successfully installed Batteries and can get it work...somewhat.
Any ideas why I'm getting the syntax error since Opam list this:
depends: "ocaml" {>= "4.00.0" & < "4.10.0"}
And, I'm at: The OCaml toplevel, version 4.07.1
This code relies on camlp4 preprocessor which is deprecated and is no longer supported. Moreover, list comprehension is no longer a part of the Batteries library and is a separate package. So you need to install it using opam install pa_comprehension. You can still make your code work for OCaml 4.07.1, by issuing the following directives right after you start OCaml toplevel (or utop)
#use "topfind";;
#camlp4o;;
#require "pa_comprehension";;
The first directive (note you have to type # it is a part of the directive), enables ocamlfind in the toplevel (I think it is not needed in utop, but won't hirt). The next directive enables camlp4o syntax, so that all inputs are preprocessed. You're no longer coding in OCaml after that, but in camlp4o dialect. Finally, the last directive loads the preprocessor that supports list comprehensions.
To compile the code that uses list comprehension you need to specify the same options to the compiler (i.e., enable syntax, load the preprocessor), e.g.,
ocamlfind ocamlopt -syntax camlp4o -package pa_comprehension -linkpkg example.ml -o example
The camlp4 package also provides another list comprehension syntax, which is a little bit different from the one that is provided by Batteries. It is called camlp4.listcomprehension, and you can use it with roughly the same invocations modulo the package name, e.g., in toplevel,
#use "topfind";;
#camlp4o;;
#require "camlp4.listcomprehension";;
and to compile
ocamlfind ocamlopt -syntax camlp4o -package camlp4.listcomprehension -linkpkg example.ml -o example
With all that said, I highly discourage you from using camlp4 list comprehension in modern days. This is an obsolete technology.
Besides, your example is ill-formed, you're missing the ? character in the closing bracket, e.g., this is an example interaction with the toplevel,
# #use "topfind";;
# #camlp4o;;
# #require "pa_comprehension";;
# open Batteries;;
# [? x | x <- 1--10; x mod 2 = 0 ?];;
- : int Batteries.Enum.t = <abstr>

use packages installed with opam inside utop

I installed yojson with opam:
opam install yojson
and I want to use it inside utop, but I haven't been able to make it work. I've tried these commands inside utop and none of them worked (it complains that it can't find the file / package):
#use "Yojson";;
#use "yojson";;
#require "Yojson";;
#require "yojson";;
Is there any additional configuration I should be aware of to use opam packages in utop?
The correct invocation is (include the # character):
#use "topfind";;
#require "yojson";;
The first command enables the #require directive (it is not the standard one, but comes from the ocamlfind tool), it is a good idea to add it to your ~/.ocamlinit file, if it is not there already. The second directive, actually loads the yojson library. You can also use the #list directive to list all available packages, as well as the ocamlfind list shell command for the same puprose.

Is it possible to compile OCaml codes using Core without corebuild?

I'm using Ubuntu 18.04. I have OCaml 4.05 installed (via apt-get) as well as utop and Core (via opam). And this is the content of my ~/.ocamlinit:
(* Added by OPAM. *)
let () =
try Topdirs.dir_directory (Sys.getenv "OCAML_TOPLEVEL_PATH")
with Not_found -> ()
;;
#use "topfind";;
#thread;;
#camlp4o;;
#require "core.top";;
#require "core.syntax";;
Compiling OCaml codes which use Core works fine with corebuild. It doesn't work with plain ocamlc or ocamlopt though. They complain:
Error: Unbound module Core
And I found corebuild to be pretty picky. It doesn't like any existing non-OCaml codes in the same directory:
SANITIZE: a total of 12 files that should probably not be in your source tree
has been found. A script shell file "/home/anta40/Codes/_build/sanitize.sh"
is being created. Check this script and run it to remove unwanted files or
use other options (such as defining hygiene exceptions or using the
-no-hygiene option).
IMPORTANT: I cannot work with leftover compiled files.
ERROR: Leftover object files:
File hellod.o in . has suffix .o
File helloscheme_.o in . has suffix .o
File nqueens.o in . has suffix .o
File helloscheme.o in . has suffix .o
File HelloHS.o in . has suffix .o
File helloml.o in . has suffix .o
ERROR: Leftover OCaml compilation files:
File nqueens.cmo in . has suffix .cmo
File helloml.cmo in . has suffix .cmo
File helloml.cmi in . has suffix .cmi
File nqueens.cmi in . has suffix .cmi
File nqueens.cmx in . has suffix .cmx
File helloml.cmx in . has suffix .cmx
Exiting due to hygiene violations.
Compilation unsuccessful after building 0 targets (0 cached) in 00:00:00.
Using the -no-hygiene option, e.g corebuild sum.ml -no-hygiene doesn't produce any native executable. Is there any workaround?
`
The OPAM package manager can support multiple OCaml instances, where each groups an OCaml installation (ocaml, ocamlc, ocamlopt, etc.) with installed packages. Each instance is called a switch.
Type ocaml switch to see a list of instances. In terms of switches, the installation of OCaml using packages (apt-get) is called system.
It may be better to avoid mixing the system and OPAM installations. I suggest that you create a new switch, ensure that the OPAM settings are properly sourced, and install Core into it the new switch :
opam switch 4.06.1
eval $(opam config env)
opam install core
The automatic build tools assume that they control the entire build process and complain when they find other compilation results. Normally, running the suggested shell script and retrying side-steps the problem, even if only to uncover another one. Your second problem (with -no-hygeine) may be related to the installation issues.
Your installation is dated.
I imagine that you followed instructions in Real World OCaml but OCaml ecosystem has moved since the first edition of RWO. To get a modern installation you should follow the dev version of Real World OCaml https://dev.realworldocaml.org/install.html .
To answer your question, corebuild is a fine wrapper around ocamlbuild. To build an executable from a sum.ml with corebuild, you should use
corebuild sum.native
for native executable or
corebuild sum.byte
for bytecode compilation
Otherwise, you can invoke ocamlopt through ocamlfind:
ocamlfind ocamlopt -package core sum.ml
Finally, if you intend to build larger project, you ought to have a look to dune https://jbuilder.readthedocs.io/en/latest/overview.html .

Use ocamlfind with ocaml interpreter, without compiling

I have a file main.ml that uses some package that makes the command ocaml main.ml fail and report this error:
Error: Unbound module X
And the only solution I've found so far is to run:
ocamlbuild main.native -use-ocamlfind -package X
./main.native
But this compiles the file. Is there a way to just run the regular ocaml interpreter (ocaml main.ml) with some additional flags to make it find the package?
The easiest way is probably to use topfind and add #require "core" at the top of your file. If you want to avoid adding toplevel directives in your file, you can use ocamlfind query -i-format -r to compute the required include directories:
ocaml $(ocamlfind query -i-format -r core) main.ml
You can put at the beginning of your file some top-level directives which find and load needed modules. For example, when I want to use Core library in my script I insert:
#use "topfind";;
#thread;;
#require "core";;
open Core.Std
(* Code of my script *)
To use topfind package ocamlfind has to be installed (opam install ocamlfind)
You can also query the path of your package to ocamlfind :
ocamlfind query package_X
That will return the path of your package : /path_to/packageX
And then :
ocaml main.ml -I /path_to/packageX

OCamlfind local library unbound module

I am trying to use ocamlfind to install a library. I am using OCamlMakeFile. Here is my Makefile:
OCAMLMAKEFILE = OCamlMakeFile
RESULT = owebl
SOURCES = src/utils.ml src/verb.ml src/request.ml src/template.ml src/response.ml src/rule.ml src/handler.ml src/server.ml
PACKS = unix str
all: native-code-library byte-code-library
install: libinstall
uninstall: libuninstall
include $(OCAMLMAKEFILE)
So basically the library is a collection of modules that I want to distribute. A basic usage of the library is (main.ml):
open Response
open Rule
open Verb
open Server
let handler =
Handler.create
(StaticRouteRule.create "/" [GET])
(FileResponse.create
~static_file:(FileResponse.StaticFile "/index.html")
())
let server = Server.create "0.0.0.0" 8080 [handler];;
server#serve
I compile the library just by running "make". It generates three files: "owebl.a, owebl.cma, owebl.cmxa". Then I install the library using ocamlfind:
ocamlfind install owebl META owebl.a owebl.cma owebl.cmxa
Oh, the META file is:
version = "0.1"
name = "OWebl"
description = "A Web Framework for OCaml"
archive(byte) = "owebl.cma"
archive(native) = "owebl.cmxa"
requires = "unix,str"
Everything works until I try to compile the above example that uses the library. I run:
ocamlbuild -use-ocamlfind -pkgs owebl main.native
And I get:
ocamlbuild -use-ocamlfind -pkgs owebl main.native
+ ocamlfind ocamlc -c -package owebl -o main.cmo main.ml
File "main.ml", line 1, characters 5-10:
Error: Unbound module Owebl
Command exited with code 2.
Hint: Recursive traversal of subdirectories was not enabled for this build,
as the working directory does not look like an ocamlbuild project (no
'_tags' or 'myocamlbuild.ml' file). If you have modules in subdirectories,
you should add the option "-r" or create an empty '_tags' file.
To enable recursive traversal for some subdirectories only, you can use the
following '_tags' file:
true: -traverse
<dir1> or <dir2>: traverse
Compilation unsuccessful after building 2 targets (1 cached) in 00:00:00.
make: *** [fileserver] Error 10
I tried adding open Owebl to the beginning of main.ml but I just got "unbound module Owebl" similarly. I'm pretty lost as to why these modules are all unbound. What am I missing?
You should use LIB_PACK_NAME variable if you want to pack your modules under umbrella namespace, like OWebl, otherwise they will be available just as it is, e.g. Response, Rule, etc. Also, you should specify the mli files as well as ml files in SOURCES variable. And finally, you shouldn't forget to install cmi files, as well as .a files. Installing mli files is also considered a good tradition, although is not strictly required. Also, consider using OCamlMakefile installation facilities instead of custom installation script.
I am no longer using OCamlMakefile therefore am not sure, but assuming OCamlMakefile compiles things with -for-pack OWebl option and properly builds a packaged module owebl.cmo, you also need to install owebl.cmi.
OCaml compiler seeks cmi files to check the existence of modules and to get their type information. If not installed, the compiler cannot find it. cma and cmxa files are collections of objects and do not provide type information.