Jump to content

OCaml: Difference between revisions

From Official NixOS Wiki
Aura (talk | contribs)
Updated OCaml language by providing additional references to Nixpkgs manual.
Aura (talk | contribs)
Clarified interaction with Opam.
 
Line 1: Line 1:
[[File:OCaml Logo.svg|alt=The OCaml logo: an orange background with a white foreground of a camel.|thumb|OCaml logo]]
[[File:OCaml Logo.svg|alt=The OCaml logo: an orange background with a white foreground of a camel.|thumb|OCaml logo]]


According to the official [ocaml.org Ocaml website]:<blockquote>
According to the official [ocaml.org Ocaml website]:<blockquote>
Line 9: Line 7:




Most packages related to OCaml, in particular the OCaml compiler and many libraries, belong to the '''ocamlPackages''' attribute set of nixpkgs. Tools for OCaml development are, in particular, available:
Most packages related to OCaml, in particular the OCaml compiler and many libraries, belong to the <code>ocamlPackages</code> attribute set of nixpkgs. Tools for OCaml development are, in particular, available:


* the [https://dune.build/ dune] builder;
* the [https://dune.build/ dune] builder;
Line 16: Line 14:


=== Build tooling ===
=== Build tooling ===
Modern development practices in OCaml tend to use '''dune''', a builder dedicated for OCaml projects. Nixpkgs provide a dedicated builder: [https://nixos.org/manual/nixpkgs/stable/#sec-language-ocaml-packaging buildDuneProject].
Modern development in OCaml tend to use <code>dune</code>, a build system dedicated for OCaml projects. Nixpkgs provide a dedicated helper [https://nixos.org/manual/nixpkgs/stable/#sec-language-ocaml-packaging buildDuneProject] to facilitate integration with <code>dune</code>.
 
An example minimal flake for OCaml development is available below:
 
{{Code|1=// flake.nix
{
  description = "Minimal flake for OCaml projects.";
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "nixpkgs";
  };
  outputs =
    {
      self,
      flake-utils,
      nixpkgs,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in
      {
        packages = {
          default =
          buildDunePackage {
            pname = "My Ocaml Package";
            duneVersion = "3";
            version = "0.1";
            minimalOCamlVersion = "4.14";
            installTargets = "all doc";
            src = ./.;
            nativeBuildInputs = with p; [
              dune_3
            ];
          };
        };
        devShells = {
          default = pkgs.mkShell {
            name = "Ocaml development shell environment.";
            inputsFrom = [ self.packages.${system}.default ];
            packages = with pkgs.ocaml-ng.ocamlPackages; [
              ocamlformat
              ocaml-lsp
              utop
              pkgs.gnumake
            ];
            shellHook = ''
              echo "Welcome in the devshell."
            '';
          };
        };
      }
    );
}|lang=nix}}


'''dune''' can generate a '''.opam''' file, which can be ingested by tools like [https://github.com/tweag/opam-nix opam-nix] to automatically generate a derivation.  
<code>nix develop</code> followed by <code>dune init project myproject</code> will drop you in a development shell.  


Manual package building is also possible using the following approaches:
==== Opam ====
OCaml has its own package manager, [https://opam.ocaml.org/ opam]. Trying to use it imperatively with Nix will result in errors. However, <code>opam</code> can generate a <code>.opam</code> file, which can be ingested by tools like [https://github.com/tweag/opam-nix opam-nix] to automatically generate a Nix derivation.


==== Findlib, ocamlfind ====
==== Manual build with ocamlc, findlib, ocamlfind ====
OCaml libraries are usually located using [http://projects.camlcity.org/projects/findlib.html findlib] and the associated '''ocamlfind''' tool. These tools are found under the '''ocamlPackages.findlib''' attribute and rely on the '''OCAMLPATH''' environment variable.
OCaml libraries are usually located using [http://projects.camlcity.org/projects/findlib.html findlib] and the associated <code>ocamlfind</code> tool. These tools are found under the <code>ocamlPackages.findlib</code> attribute and rely on the <code>OCAMLPATH</code> environment variable.


A hook in the '''findlib''' package will automatically populate this variable with the paths to the other libraries. For instance, when starting a shell with
A hook in the <code>findlib</code> package will automatically populate this variable with the paths to the other libraries. For instance, when starting a shell with
<code>nix-shell --packages ocamlPackages.findlib ocamlPackages.batteries</code>
<code>nix-shell --packages ocamlPackages.findlib ocamlPackages.batteries</code>
will set the '''OCAMLPATH''' variable so that '''ocamlfind''' can locate the '''batteries''' library.
will set the <code>OCAMLPATH</code> variable so that <code>ocamlfind</code> can locate the <code>batteries</code> library.


This hook also sets the '''CAML_LD_LIBRARY_PATH''' environment variable that is used for locating the dynamically loaded shared libraries (aka stublibs).
This hook also sets the <code>CAML_LD_LIBRARY_PATH</code> environment variable that is used for locating the dynamically loaded shared libraries (aka stublibs).


=== Scripting with OCaml ===
=== Scripting with OCaml ===
Line 46: Line 100:
<code>nix-env -iA nixpkgs.emacsPackagesNg.tuareg</code>.
<code>nix-env -iA nixpkgs.emacsPackagesNg.tuareg</code>.


Emacs must be configured to be able to find this package: you may need to add a line like this to your '''.emacs''' file, somewhere between <code>(require 'package)</code> and <code>(package-initialize)</code>:
Emacs must be configured to be able to find this package: you may need to add a line like this to your <code>.emacs</code> file, somewhere between <code>(require 'package)</code> and <code>(package-initialize)</code>:


<syntaxHighlight lang=emacs>
<syntaxHighlight lang=emacs>
Line 58: Line 112:
Beware that merlin is specific to one particular version of OCaml (it won’t work correctly with a compiler of a different version).
Beware that merlin is specific to one particular version of OCaml (it won’t work correctly with a compiler of a different version).


To configure Emacs and enable the merlin mode, add the following to your '''.emacs''':
To configure Emacs and enable the merlin mode, add the following to your <code>.emacs</code>:


<syntaxHighlight lang=emacs>
<syntaxHighlight lang=emacs>
Line 66: Line 120:
</syntaxHighlight>
</syntaxHighlight>


Also, a specific variable ('''merlin-command''') of the merlin mode must be overridden (its default is '''opam'''):
Also, a specific variable (<code>merlin-command</code>) of the merlin mode must be overridden (its default is <code>opam</code>):


<syntaxHighlight lang=emacs>
<syntaxHighlight lang=emacs>
Line 74: Line 128:
</syntaxHighlight>
</syntaxHighlight>


A useful complement to merlin is [https://www.typerex.org/ocp-indent.html '''ocp-indent'''], a customizable tool to indent OCaml code. It can be installed using <code>nix-env -iA nixpkgs.ocamlPackages.ocpIndent</code>. To enable it in Emacs, don’t forget to add the following line to your '''.emacs''':
A useful complement to merlin is [https://www.typerex.org/ocp-indent.html '''ocp-indent'''], a customizable tool to indent OCaml code. It can be installed using <code>nix-env -iA nixpkgs.ocamlPackages.ocpIndent</code>. To enable it in Emacs, don’t forget to add the following line to your <code>.emacs</code>:


<syntaxHighlight lang=emacs>
<syntaxHighlight lang=emacs>
Line 81: Line 135:


=== Specific version of the OCaml compiler ===
=== Specific version of the OCaml compiler ===
Various versions of the '''ocamlPackage''' attribute set are available, corresponding to various versions of OCaml. For instance, the attribute set '''ocaml-ng.ocamlPackages_4_04''' contains the OCaml compiler at version 4.04 and OCaml libraries compiled with that particular compiler.
Various versions of the <code>ocamlPackage</code> attribute set are available, corresponding to various versions of OCaml. For instance, the attribute set <code>ocaml-ng.ocamlPackages_4_04</code> contains the OCaml compiler at version 4.04 and OCaml libraries compiled with that particular compiler.


==== Custom version ====
==== Custom version ====
So as to get the set of OCaml libraries built with/for a custom version of the OCaml compiler, e.g., to enable '''flambda''' support, you may use the '''ocamlPackages.overrideScope''' function:
So as to get the set of OCaml libraries built with/for a custom version of the OCaml compiler, e.g., to enable <code>flambda</code> support, you may use the <code>ocamlPackages.overrideScope</code> function:


<syntaxHighlight lang=nix>
<syntaxHighlight lang=nix>

Latest revision as of 13:24, 12 May 2026

The OCaml logo: an orange background with a white foreground of a camel.
OCaml logo

According to the official [ocaml.org Ocaml website]:

[Ocaml is]... an industrial-strength functional programming language with an emphasis on expressiveness and safety

Developping in OCaml with Nix

Most packages related to OCaml, in particular the OCaml compiler and many libraries, belong to the ocamlPackages attribute set of nixpkgs. Tools for OCaml development are, in particular, available:

Build tooling

Modern development in OCaml tend to use dune, a build system dedicated for OCaml projects. Nixpkgs provide a dedicated helper buildDuneProject to facilitate integration with dune.

An example minimal flake for OCaml development is available below:

// flake.nix
{
  description = "Minimal flake for OCaml projects.";
  inputs = {
    flake-utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "nixpkgs";
  };
  outputs =
    {
      self,
      flake-utils,
      nixpkgs,
      ...
    }:
    flake-utils.lib.eachDefaultSystem (
      system:
      let
        pkgs = nixpkgs.legacyPackages.${system};
      in 
      {
        packages = {
          default = 
          buildDunePackage {
            pname = "My Ocaml Package";
            duneVersion = "3";
            version = "0.1";
            minimalOCamlVersion = "4.14";
            installTargets = "all doc";
            src = ./.;
            nativeBuildInputs = with p; [
              dune_3
            ];
          };
        };
        devShells = {
          default = pkgs.mkShell {
            name = "Ocaml development shell environment.";
            inputsFrom = [ self.packages.${system}.default ];
            packages = with pkgs.ocaml-ng.ocamlPackages; [
              ocamlformat
              ocaml-lsp
              utop
              pkgs.gnumake
            ];
            shellHook = ''
              echo "Welcome in the devshell."
            '';
          };
        };
      }
    );
}

nix develop followed by dune init project myproject will drop you in a development shell.

Opam

OCaml has its own package manager, opam. Trying to use it imperatively with Nix will result in errors. However, opam can generate a .opam file, which can be ingested by tools like opam-nix to automatically generate a Nix derivation.

Manual build with ocamlc, findlib, ocamlfind

OCaml libraries are usually located using findlib and the associated ocamlfind tool. These tools are found under the ocamlPackages.findlib attribute and rely on the OCAMLPATH environment variable.

A hook in the findlib package will automatically populate this variable with the paths to the other libraries. For instance, when starting a shell with nix-shell --packages ocamlPackages.findlib ocamlPackages.batteries will set the OCAMLPATH variable so that ocamlfind can locate the batteries library.

This hook also sets the CAML_LD_LIBRARY_PATH environment variable that is used for locating the dynamically loaded shared libraries (aka stublibs).

Scripting with OCaml

OCaml can be used in nix-shell scripts as follows:

#!/usr/bin/env nix-shell
(*
#!nix-shell --pure -i ocaml -p ocaml
*)

print_string "Hello world! 🚀 \n";;

Using Emacs

Tuareg is an Emacs OCaml mode. It can be installed with nix-env -iA nixpkgs.emacsPackagesNg.tuareg.

Emacs must be configured to be able to find this package: you may need to add a line like this to your .emacs file, somewhere between (require 'package) and (package-initialize):

(add-to-list 'package-directory-list "~/.nix-profile/share/emacs/site-lisp/elpa")

There is some more documentation in the nixpkgs sources.

Merlin provides additional functionality to Emacs. It can be installed through nix-env -iA nixpkgs.ocamlPackages.merlin.

Beware that merlin is specific to one particular version of OCaml (it won’t work correctly with a compiler of a different version).

To configure Emacs and enable the merlin mode, add the following to your .emacs:

(add-to-list 'load-path "~/.nix-profile/share/emacs/site-lisp")
(require 'merlin)
(add-hook 'tuareg-mode-hook 'merlin-mode t)

Also, a specific variable (merlin-command) of the merlin mode must be overridden (its default is opam):

(custom-set-variables
  '(merlin-command "ocamlmerlin")
)

A useful complement to merlin is ocp-indent, a customizable tool to indent OCaml code. It can be installed using nix-env -iA nixpkgs.ocamlPackages.ocpIndent. To enable it in Emacs, don’t forget to add the following line to your .emacs:

(require 'ocp-indent)

Specific version of the OCaml compiler

Various versions of the ocamlPackage attribute set are available, corresponding to various versions of OCaml. For instance, the attribute set ocaml-ng.ocamlPackages_4_04 contains the OCaml compiler at version 4.04 and OCaml libraries compiled with that particular compiler.

Custom version

So as to get the set of OCaml libraries built with/for a custom version of the OCaml compiler, e.g., to enable flambda support, you may use the ocamlPackages.overrideScope function:

ocamlPackagesFlambda = ocamlPackages.overrideScope (self: super: {
  ocaml = super.ocaml.override { flambdaSupport = true; };
});

More details: https://github.com/NixOS/nixpkgs/pull/53357#issuecomment-451727433