Setting up a new Ocaml environment in 2026 is not yet a fluid experience

TL;DR : quite messy experience with OCaml tooling installation ; is there a straightforward way to setup OCaml with all its tools ?


Back again to Ocaml, I first tried to quickly setup tools on my Win11 laptop.

It was messy with WSL with a strange message (“The file (wsl.exe ?) doesn’t exist”). No taste for digging in Win stuff…
And I didn’t want to use Docker nor Virtualbox or VMware workstation.

So I used my good old PC with Debian.
No problem to install opam and the packages I need.
I installed VS code and the extension VScode OCaml platform. And also installed ocaml-lsp-server and ocamlformat with opam.

Of course, I had a look at the Get started page and especially to Configuring Your Editor · OCaml Documentation

In Your First OCaml Program · OCaml Documentation I found a ugly
$ opam exec -- dune init proj hello
followed by “if … you can omit opam exec -- from the start of dune commands”. ??!
Seriously? How could they/you do that in this landing page for beginners?!
It would have been concise and easy to remember to write:

dune init proj hello
cd hello
dune build
dune exec hello

And putting some information about eval $(opam env) and opam exec -- in an appendix.

The Dune documentation appeared to first be simple in its (same) introduction (https://dune.readthedocs.io/en/stable/quick-start.html#initializing-projects ) then quickly became complicated mixing
Dune Quick-start initializing-a-library
and
Dune Quick-start building-a-hello-world-program-from-scratch
with a confusing syntax such as dune exec -- ./hello_world.exe without introducing context. Then, followed by a bunch of various examples.

Dune started again to make me headache… :

First, bin/ is (still) a place defined for holding… src / source program… (?!) Seriously ? Why naming that way?

The available ocaml and dune and other tools documentation seems not very consistent depending on the path you follow (VScode extension, ocaml.org, etc.)

.ocamlformat file is required to get formatting, but Dune doesn’t produce it while it pretends to be THE build system.

.merlin files seem not be used to handle while using ocaml-lsp-server.

Dune needs a first build to handle completion…
However Dune now has a watch mode (dune build -wand dune exec hello -w).

Dune produces an opam file but if you change the file dune-project, next build won’t update it…

It’s just the start and I think I need to be vigilant all the time to prevent that kind of problems.

Of course, I had no problem with ocamlc and agnostic Make files.

I didn’t find yet integration details in VScode (buttons and shortcuts except Shift+Enter (evaluate selection), F12 and Ctrl+Click on a value.

F12 is bound to Go to definition (say in stdlib).

Ctrl click on a value is bound to Jump to definition, and Alt+left arrow to get back (NOT mentioned in https://ocaml.org/docs/set-up-editor#2-jump-to-definitions-with-ctrl–click ) is nice (in VS code) .

I would have prefered a unique function to “go/jump to definition” accessed with F12 or Ctrl+click.

In 2025, I expected a much more nice experience with Ocaml tooling.

Is there a more straightforward and tested installation guide for linux? Windows? (OCaml and VScode, or whatever vim, Emacs)

For OCaml and VScode, is there a global installation script that handles all details and proposes some choices?

EDIT : My best wishes for 2026 to all of you, and to Ocaml!

3 Likes

is there a global installation script that handles all details and proposes some choices?

user-setup’s what’s supposed to do this, but it doesn’t support vscode or nvim.

I spun back up on OCaml a month ago and have been very pleased and found it pretty smooth going, but

  1. I started with Linux and nvim and didn’t miss what was broken after I copied user-setup’s vim config into nvim. I’ve been much more pleased after switching to vim and getting to like Merlin.
  2. I was distracted by Advent of Code and not needing much support for a while.
  3. I am mostly comparing OCaml with system-administration languages with very low expectations for tooling.
  4. Plus, coming back to OCaml, I already had some idea of the value to find here and received the language and ecosystem with much more charity.

ocaml.nvim, I couldn’t figure out from the lazy.nvim setup path, and after getting ocamllsp to work on its own, I didn’t like it as much. Especially from something in filetype.lua filling the page with errors if I :e another file, which I do often in split windows.

My personal notes for vim are just

opam install merlin
opam user-setup install

plus

au FileType ocaml setlocal formatoptions-=r formatoptions-=o formatoptions-=q
au BufRead,BufNewFile *.ml map <C-p> :silent:w!<Return>:%!ocamlformat %<Return>
au BufRead,BufNewFile *.ml map K :MerlinDocument<Return>
au BufRead,BufNewFile *.ml map <C-n> :MerlinErrorCheck<Return>
au BufRead,BufNewFile *.ml map <Leader>, :MerlinLocateImpl<Return>
au BufRead,BufNewFile *.ml map <Leader>. :MerlinLocateIntf<Return>
au BufRead,BufNewFile *.ml map <Leader>T :MerlinLocateType<Return>
au BufRead,BufNewFile *.ml imap <C-n> <C-x><C-o>

But those last two reasons are the main ones.

On charity, it’s easy to immediately run into something about OCaml that can be received as the way it is, only because some very low-hanging fruit wasn’t picked, or because of some developer’s personal idiosyncrasy, or because OCaml’s just old and better ways weren’t in vogue yet. Some familiarity with programming languages in general and some confidence (to say it nicely) is all that’s needed to write an extremely hostile review and move right on. It takes longer to see that some decisions weren’t so lightly made or that they come with some serious benefits as well.

Those are general thoughts and not commentary on your remarks here. I’ve run into a lot of this as well

I appreciated dune much more after finding --display=shortand _build/logand especially dune utop. Separating most of a project into libraries in lib/ and having only CLI stuff in bin/ makes it easy to interactively use the libraries which is something very neat that a lot of similar languages can’t do, and which also takes some pain out of not having the easy print debugging that I’d reach for in those languages.

2 Likes

Happy New Year!

As a small side note, depending on how long ago “Back again to OCaml” meant, but the digging here was:

winget install opam

and the rest of the experience (sadly) would have been the same (though I’m not quite sure why failing to start WSL is OCaml’s fault :face_with_peeking_eye:)

3 Likes

Hello @Luc_ML,

Sorry to hear you had this experience with OCaml. Personally, without being a very experienced OCaml programmer, my experience has been good. I haven’t tried it on Windows, but it works well on Linux and macOS.

  • About opam exec -- dune init proj hello, there’s a clear explanation right below the code snippet. Most likely, when this tutorial was written, the main goal was to ensure that a beginner would not run into errors right from the start, and opam exec -- helps to avoid that. I actually appreciate seeing it, as it helps understand how opam and dune interact. It’s my point of view, but I understand yours.

  • ocamlformat can be used standalone or with dune. It has its own CLI, and different projects choose different formatting options, so it makes sense that dune/ocamlformat doesn’t enforce a style (this is not golang). Once installed, it’s as simple as ocamlformat file.ml or dune fmt, and it integrates well with editors. To know how to create it, with ocamlformat --help you have the details.

  • For Dune workflow issues, it’s fine to raise them on GitHub. Feedback is welcome if presented politely. Regarding dune project structure (bin, lib, test), all these are source directories (we don’t need to say src); final executables go into _build. Splitting a project into library, tests, and an executable entry point is also common and makes sense to me.

  • As for editors, I can’t comment on VS Code, but for Neovim things are evolving. Merlin can be used with or without ocaml-lsp; when using ocaml-lsp, .merlin files aren’t needed (I think), but they remain useful for older setups.

Note: I think that in the case of Neovim there have been several plugings providing ocaml.nvim, and that Tarides has recently taken the initiative to create a more official solution (if I understand correctly), so this is still a work in progress.

1 Like

That’s weird because that’s what Dune is supposed to be doing and I can confirm that this has been working for years. Could you file an issue?

1 Like

If the build fails it doesn’t generate an OPAM file. Also I think the changes need to be committed to git.

Thanks for your answer.

So it’s useless…

I’ve never been comfortable with Emacs but I could be using vim if Ocaml support was enough friendly.

Today, for me an new-to-Ocaml, VScode offers a comfortable enough IDE (even if some tweaks seem required)

I’m much more comfortable with linux systems than with Win.

I tried again with Install OCaml
winget install Git.Git OCaml.opam

Always messing with WSL. See Failed in attempting to update the source: winget · Issue #2666 · microsoft/winget-cli · GitHub etc.

As any ordinary Win user, we just want to use OCaml (we know) and is tools (we hope will be improved).

It relies on Ocaml’s side (or any other PL that want to be used with Win) to detail all requirements and give tips or even checking/fixing tools to easily use it, isn’t it ?

Hello,

I’m talking about someone new to Ocaml (or coming back again), who is motivated to discover it.

This person must not need to understand the whole story of Ocaml and its tools, and how to combine them. And just need to quickly get an up and running with a full featured OCaml IDE, on any OS (linux, macOS or Win). Regarding this requirement, a VM or Docker container or some OCaml online tool might be a relevant solution. A good script is a more risky (many OS parameters) but interesting solution.

opam exec -- dune init proj hello is mixing up 2 systems (opam and dune). It’s not the right way for learning. Even with a Note 1 and Note 2 or even Note 3… which add additional noise…

ocamlformat : if Dune pretends to be a reference tool, it should propose to configure optional tools such as ocamlformat (doing Crtl-shift i is simple and useful)

Merlin files : it’s not clear if I should use it as in the past or if ocaml-lsp-server is doing the job?

dune workflow… flaws or seems to.
It appears it needs to experience it to understand its hidden behaviour.
Is there a single point where one can find a clear description about how to use dune and its simple and more complex behaviour?
And bin/ is confusing when talking about the source (of core program). I’ve seen several people confused about this weird naming.

1 Like

Build did succeed then I just modified generated dune-project file and build again.

No git used (for @brianwk ).

I’ve just tried again (debian, VS code and CLI, in the same place and Terminal, with all previous commands available in bash).
Now, after a successul build, if only the dune-project file is modified then the opam file is updated.

I’ll watch if this happens again or in which context it could happen.

Next time, I’ll make a video of my trials.

That’s the expected behaviour. Dune generates and maintains the opam file based on the dune-project file. You are not supposed to manually edit the opam file, as it will get overwritten by dune by the next build.

I know the opam file is generated as it’s written at its first line “# This file is generated by dune…”

I had one context where it didn’t work as expected. See herebefore.

Now it works.

Hello @Luc_ML,

I’m a beginner, so I think I was speaking from a similar position. That said, I usually take a different approach: rather than pushing others to see things my way, I try to understand the value in how things are done by others first. Of course, OCaml’s tutorials and style can be improved (and need to be improved), but when improving someone else’s work, I find it rewarding first to understand why it was done that way. That was my point. I never said that a beginner must know the full history of OCaml tools, but the tutorial’s style may explore different approaches that gradually reveal the rationale behind them.

Regarding opam exec -- dune init proj hello, this is simply a continuation of a tutorial. Users were introduced to opam earlier, and dune was mentioned in the previous steps, so it’s expected that the tutorial is followed in order. That said, I think all readers of ocaml.org can propose improvements on GitHub, so your suggestions would be welcome. As I already said in previous post, I understand your point.

Finally, .merlin files are mainly added by dune to support older editors. With ocaml-lsp-server, they are probably not needed, but dune creates them by default since it doesn’t impose a specific editor, just as it doesn’t enforce any particular OCaml formatting style. That said, I would find it useful if .ocamlformat files were easier to create, for example by allowing users to select from a few widely used predefined styles (maybe there is already a way on dune to do that, I don’t know) at first instance.

1 Like

This is one that I remember running into and also can’t replicate easily.

What I might be remembering is that dune complains about the orphan .opam file if you change the name of the project:

$ dune init proj d1 && cd d1
d1$ sed -i 's/d1/d2/' dune-project
d1$ dune build
File "d1.opam", line 1, characters 0-0:
Error: This opam file doesn't have a corresponding (package ...) stanza in
the dune-project file. Since you have at least one other (package ...) stanza
in your dune-project file, you must a (package ...) stanza for each opam
package in your project.

deleting d1.opam and rebuilding indeed leaves you with a good d2.opam.

Guides try to provide the desired fluid experience by ushering you past the details of dune to get you right into a reasonable default project that can be worked with. But then, since an understanding of dune was skipped, anything it does that’s different is automatically frustrating. I’m using to verbose build systems and dune’s terse (“did my tests pass? did it even do anything?”). I’m used to build systems strictly working from the project root but dune works in relation to a dynamically found root. I’m used to a strict filesystem layout or exhaustive configuration in one file but dune’s much more flexible and has configuration distributed through ‘dune’ files.

With more of an understanding of dune, I like it a lot more. The impression I get from it is the same as I get from a lot of OCaml, that it’s aimed at reducing friction for more experienced users and is providing features that I don’t want yet. There’s a universe of alternatives that “just work” at the start but then get more frustrating as you want to do more with them. A build system that’s just a short bash script or a short Makefile is famously like this. A cargo hello-world .toml is no work at all (some guides even have you install a 128-dependency CLI program to edit dependencies into it for you) but a more advanced project has multiple such files that path to each other, and additional configuration like .cargo/config.toml, and a build.rs.

Giant YAML files have me really tired of configuration languages so I like Zig just making the build system part of the standard library, but that

  1. draws the same complaint that there’s more early-on friction
  2. in my experience, requires a lot more time and configuration work, and more frequent work. Dune stanzas are very little to type and very little to read and there’s a lot of development that I can do without having to touch them at all.

On OCaml generally optimizing for the more experienced user, I don’t mean that some more friction early on is always necessary, just that it’s given less attention. Take ocamlgraph’s docs. I used this library for the first time last month and I really like the library and the docs after I found some other help on even getting to “hello world” with it. Or fmt’s documentation which I got nothing out of until I found some Q&A here. For contrast, man rsync and man getaddrinfo and imitators like perldoc File::Find::Rule. There’s a synopsis which is expected to be all you need to immediately get some use, there’s a long description, there can be examples that you don’t have to go hunting for because they’re on the first page that you see.

From my experience it also will not re-generate the OPAM file on a build failure.

If so, this is a bug, not a feature.

The “goodness” of a build system is how well it can be used by the inexperienced, not the hoary greybeards. A build system is used way, way, way more by people who have little to no understanding of it than by anybody else.

Priority 1 needs to be getting inexperienced people running. Priority 2 is giving good feedback when things don’t go as expected so the inexperienced can at least ask the right questions. After that, Priority 3 is making even complicated things possible. Finally, Priority 4 is making things easier for the experienced–and should be a long, long way down the priority list.

The “goodness” of a build system is how well it can be used by the inexperienced, not the hoary greybeards.

OK, forget about “experience”. The impression I get from dune is the same as I get from a lot of OCaml, that it’s aimed at reducing friction across a longer time horizon for a project. If I want “hello world”, dune is much more work than calling ocamlc directly. If I want raylib “hello world”, dune is on par with calling ocamlfind directly. As I want more than that, even something like adding some unit tests, dune starts paying for itself.

Someone new to OCaml will not grow a long grey beard in a day, but can still start my-first-ocaml-projectand within an hour want to add something small to that project which, without dune, would feel like hitting a brick wall. It’s really not a better build system that gets people running as fast as possible when what they are running into is an abrupt and large increase in friction after a small increase in project requirements.

Personally, I have an arm64 macOS work laptop and all of my build targets are x86_64 Linux. Golang asks me to set two environment variables to make this work, and pretty much every other language is failing at priority 1 where I am concerned. People are different and have different situations and the very first things they want to do is also different.

3 Likes

Here is a sample C/C++ program tree:

MyProject/
├── src/            // Source files
│   ├── main.cpp
│   └── utils.cpp
├── include/        // Header files
│   ├── utils.h
└── build/          // Build files

Ref : C++ Project Structure: Building Blocks for Success

Here is a sample Rust program tree:

my_package/
├── .git/ # Git repository data (if initialized by cargo new)
├── .gitignore # Git ignore file (typically includes /target/)
├── Cargo.toml # Package manifest file
├── Cargo.lock # Locked dependency versions (generated after first build/add)
├── src/ # Source code directory for crates
│ └── main.rs # Crate root for a binary crate
│ # Or:
│ └── lib.rs # Crate root for a library crate
└── target/ # Build artifacts (compiled code, cache) - not version controlled

Ref : Directory structure - Rust for C-Programmers

In Java, it’s the same :

src/main
src/main/java/
src/main/resources/

Ref : Java Project Directory Layout: Standard Structure Guide (Tests, Build Configs & Beyond src/bin) — javathinking.com

Now, let’s imagine a C++ or Java or Rust programmer coming to OCaml because he heard a lot of good comments about it.

He should or will:
1/ find uneasy and not user-friendly to struggle for setting up an OCaml IDE, without knowing how to choose and configure differents tools that “should or may work together” ; depending on his editor (Emacs, vim, VScode, etc.) and his OS
2/ find weird to have a bin/ directory instead or a… src/ directory (which is universal and self explanatory) ; and wonder which argument could justify such a choice, while a recent and serious PL such as Rust uses src/, and what the hell could happen in his life so the chief dune designer let make that choice.

Rust and C++ don’t have REPLs. OCaml does, and dune utop allows you to load the project’s libraries into the REPL and interact with them. Some else also explained this earlier. So in a dune project it makes sense to keep much of the functionality in libraries and keep a small shell executable in bin/.

As for Java–Java’s Maven project layout structure is famous for its high number of directories. Dune projects are much simpler by comparison.

1 Like