OCaml: Difference between revisions

imported>Mic92
fix markup
Aura (talk | contribs)
Clarified interaction with Opam.
 
(8 intermediate revisions by 6 users not shown)
Line 1: Line 1:
Most packages related to OCaml, in particular the OCaml compiler and many libraries, belong to the '''ocamlPackages''' attribute set of nixpkgs.
[[File:OCaml Logo.svg|alt=The OCaml logo: an orange background with a white foreground of a camel.|thumb|OCaml logo]]


== Findlib, ocamlfind ==
According to the official [ocaml.org Ocaml website]:<blockquote>
[Ocaml is]... an industrial-strength functional programming language with an emphasis on expressiveness and safety        </blockquote>


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.
== Developping in OCaml with Nix ==


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
 
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://github.com/ocaml/ocaml-lsp OCaml language server];
* the [https://github.com/ocaml-ppx/ocamlformat ocamlformat formatter].
 
=== Build tooling ===
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}}
 
<code>nix develop</code> followed by <code>dune init project myproject</code> will drop you in a development shell.
 
==== 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.
 
==== Manual build with ocamlc, findlib, ocamlfind ====
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 <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 <code>CAML_LD_LIBRARY_PATH</code> environment variable that is used for locating the dynamically loaded shared libraries (aka stublibs).


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:
<syntaxHighlight lang=ocaml>
#!/usr/bin/env nix-shell
(*
#!nix-shell --pure -i ocaml -p ocaml
*)


== Using Emacs ==
print_string "Hello world! 🚀 \n";;
</syntaxHighlight>


=== Using Emacs ===
[https://github.com/ocaml/tuareg Tuareg] is an Emacs OCaml mode. It can be installed with
[https://github.com/ocaml/tuareg Tuareg] is an Emacs OCaml mode. It can be installed with
<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>:


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


There is some more documentation in the [https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/emacs-packages.nix nixpkgs sources].
There is some more documentation in the [https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/emacs-packages.nix nixpkgs sources].
Line 26: 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>
(add-to-list 'load-path "~/.nix-profile/share/emacs/site-lisp")
(require 'merlin)
(add-hook 'tuareg-mode-hook 'merlin-mode t)
</syntaxHighlight>
 
Also, a specific variable (<code>merlin-command</code>) of the merlin mode must be overridden (its default is <code>opam</code>):
 
<syntaxHighlight lang=emacs>
(custom-set-variables
  '(merlin-command "ocamlmerlin")
)
</syntaxHighlight>


  (add-to-list 'load-path "~/.nix-profile/share/emacs/site-lisp")
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>:
  (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'''):
<syntaxHighlight lang=emacs>
(require 'ocp-indent)
</syntaxHighlight>


  (custom-set-variables
=== Specific version of the OCaml compiler ===
  '(merlin-command "ocamlmerlin")
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.
  )


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''':
==== 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 <code>flambda</code> support, you may use the <code>ocamlPackages.overrideScope</code> function:


   (require 'ocp-indent)
<syntaxHighlight lang=nix>
ocamlPackagesFlambda = ocamlPackages.overrideScope (self: super: {
   ocaml = super.ocaml.override { flambdaSupport = true; };
});
</syntaxHighlight>


== Specific version of the OCaml compiler ==
More details: https://github.com/NixOS/nixpkgs/pull/53357#issuecomment-451727433


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.
[[Category:Languages]]