# OCaml-containers: the CC-prefixed standard library that OCaml never shipped

> OCaml-containers is a BSD-licensed extension of the OCaml standard library that fills in the containers, combinators and iterators the language left out, with every module independent and prefixed CC, plus separate sub-libraries for unix helpers, bencode, CBOR and strongly connected components. It suits an OCaml developer who wants safe list operations and a richer set of data structures, and it is not a batteries-included framework.

**c-cube/ocaml-containers** — A lightweight, modular standard library extension, string library, and interfaces to various libraries (unix, threads, etc.) BSD license.

- Repository: https://github.com/c-cube/ocaml-containers
- Website: https://c-cube.github.io/ocaml-containers/
- Stars: 522 · Forks: 91
- Language: OCaml
- License: BSD-2-Clause
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/c-cube-ocaml-containers

## What the library is and is not

OCaml-containers is described as a lightweight, modular standard library extension, string library, and interfaces to various libraries such as unix and threads, and the readme puts it as a modular, clean and powerful extension of the OCaml standard library.

The scope is specific. Containers extends OCaml's standard library, under BSD licence, focused on data structures, combinators and iterators, without dependencies on unix, str or num. Every module is independent and is prefixed with CC in the global namespace.

Two design choices follow from that. The first is that nothing depends on unix, str or num, which means the library works in a bytecode program with no C library bindings and no external process machinery. The second is the CC prefix, and the readme explains the reasoning in a slightly self-deprecating way: modules are totally independent and are prefixed with CC, for containers-core or companion-cube because the author is a megalomaniac.

The prefix is not cosmetic. Because every module is independent, you can use CCList without pulling in CCMap, and the prefix guarantees that opening the whole library does not silently shadow a stdlib module you were relying on. If you do want the shadowing, there is a second entry point: open Containers will bring enhanced versions of the standard modules into scope, and the Containers module is intended to be opened and replaces some stdlib modules with extended ones.

The project is BSD-2-Clause licensed, the primary language is OCaml, the homepage is c-cube.github.io/ocaml-containers, and the releases are v3.16 from 2025-05-27, v3.17 from 2026-01-13 and v3.18 from 2026-02-15, with the last push on 2026-10-12.

## The packages, and what moved out of them

The library is not one package, and the split is the thing to understand before depending on it.

The core lives in src/core/ and is packaged under containers in ocamlfind. It provides the CC-prefixed modules plus the Containers drop-in module and, according to the readme, a small S-expression printer and parser that can be functorized over the representation of values.

Then there are sub-libraries with a specific focus each. There are utilities around the unix library in containers.unix, mainly to spawn sub-processes easily and deal with resources safely. There is a bencode codec in containers.bencode, described as a tiny json-like serialization format that is extremely simple and comes from bittorrent files. There is a CBOR codec in containers.cbor, a compact binary serialization format. And there is the strongly connected component algorithm, functorized, in containers.scc.

A separate library containers-data holds additional data structures that do not have an equivalent in the standard library, typically not as thoroughly maintained. That has been its own package since 3.0.

The library has also shrunk over time. Some modules have moved to their own repositories, and the readme names sequence, now called iter, gen and qcheck, and says they are on opam. Containers-thread has been removed in favour of Moonpool.

That last point is worth dwelling on, because the project description still mentions interfaces to various libraries such as unix and threads, and the readme says threads are gone. If you need a threading library, the answer is no longer this package.

The repository layout matches: src/ for the sources, containers.opam and containers-data.opam with .template counterparts for the opam files, a dune and dune-project at the root, a doc/ directory, tests/, benchs/, fuzz/, examples/, a Makefile, an .ocamlformat, a .pre-commit-config.yaml and a CHANGELOG.md.

## What the 3.0 release broke

The migration guide is the most useful documentation in the repository, and it is blunt about the cost of the 3.0 release.

The biggest change is that some sub-libraries were turned into their own packages, deleted or merged elsewhere, which means if you use them you will have to edit your dune, oasis or opam files. Specifically, if you use containers.sexp, the CCSexp module, it now lives in containers itself, and if you used anything in containers.data you need to depend on the containers-data package now.

The second large change is the removal, at last, of functions deprecated in 2.8, related to the spread of Seq.t as the standard iterator type. Functions like CCVector.of_seq now operate on the standard Seq.t type, and old-style iteration based on the iter library is now named of_iter and to_iter. The readme advises changing the code, possibly using search and replace, and adds that thankfully the typechecker should guide you.

The third is the removal of Array_slice and String.Sub to simplify the code and make String lighter, with no replacement at the moment, and an invitation to say if you need either as a sub-library.

The fourth is renaming of some functions into more explicit names. CCVector.shrink is now CCVector.truncate, CCVector.remove is now CCVector.remove_unordered to contrast with the new CCVector.remove_and_shift, and CCPair.map_fst and map_snd now transform a tuple into another tuple by modifying the first or second element rather than producing a pair.

The fifth is that all the collection pretty-printers now take their separator, start and stop arguments as unit printer functions, that is Format.formatter to unit to unit, rather than strings. The readme says this gives the caller better control over the formatting of lists, arrays, queues and tables.

And the sixth is the removal of many deprecated functions.

The 2.0 migration is worth reading too, because it changed how the library behaves in ways that are easy to miss. The type system should detect issues related to print being renamed to pp, and if you are lucky a sed -i 's/print/pp/g' might help. Many optional arguments became mandatory, because their default value would be a polymorphic magic operator such as = or >=, and during the transition you can use Stdlib.(=) and Stdlib.(>=) explicitly. And if your code contains open Containers, the biggest hurdle is that operators have become monomorphic by default, which the readme believes prevents many subtle bugs but which, during migration, might require you to add open Stdlib right after open Containers.

## Why the operators are monomorphic

The readme has a section on monomorphic operators with a why and a how, and the why is one of the better arguments in OCaml library design.

The explanation is quoted from a contributor in issue 196. The main problem with polymorphic comparison is that many data structures give one result for structural comparison and a different result for semantic comparison. The classic example is comparing maps: if you have a list of maps and use comparison to sort them, you get the wrong result, because multiple map structures can represent the same semantic mapping from key to value, and comparing them structurally is simply wrong.

The map case is bad. The hashtable case is described as far more pernicious. Identical hashtables will seem to be identical for a while, as before they have had a key clash, and the outer array is likely to be the same. Once you get a key clash you start getting lists inside the arrays, or maps inside the arrays if you try to be smarter with a hashtable, and that causes comparison errors, meaning identical hashtables will be seen as different or vice versa.

The summary rule is that every time you use a polymorphic comparison where structural comparison is not semantic comparison, it is a bug. And every time you use polymorphic comparison where the type of data compared may vary, such as being an int now but a map later, you have the same problem.

That is why the migration advice is to use proper combinators for equality with CCEqual, comparison with CCOrd and hashing with CCHash. The library has to make the comparison explicit, because OCaml's structural equality is only correct for some types, and the cost of that choice is that every comparison site needs to say what kind of comparison it means.

This is the sharpest trade-off in the library, and it is worth naming explicitly when you evaluate it: the safety is real, but it is paid for in verbosity at every call site, and in code that uses open Containers the shadowing can change the meaning of an existing comparison without a type error.

## Where OCaml-containers is the wrong choice

The first limitation is that this is a data structure library and not a framework. There is no HTTP client, no logging abstraction, no configuration handling and no command line parsing in the core package. If your project needs those, you are combining several libraries, and the benefit of Containers is only in the parts it covers.

The second is the module count. The CC prefix means you can take one module, but the ecosystem around a large OCaml project usually ends up using a dozen of them, and each has to be opened or qualified at every use site. The drop-in Containers module removes the qualification but reintroduces the shadowing problem that the monomorphic operator change was meant to address.

The third is the maintenance asymmetry. The readme is explicit that containers-data holds structures that do not have a standard library equivalent and are typically not as thoroughly maintained, and it is a separate package since 3.0. So the quality bar differs between the core and the extras, and the extras are the ones with the interesting data structures.

The fourth is that several pieces have left the project. sequence became iter, gen and qcheck are separate repositories, and containers-thread is gone in favour of Moonpool. That is good for the ecosystem and bad for anyone who wants one dependency, and it means the version numbering across the family no longer lines up.

The fifth is the migration cost for anyone arriving at 3.x. The renaming of shrink to truncate and remove to remove_unordered, the removal of the old iteration functions, the mandatory pretty-printer arguments and the package splits are all documented, and the typechecker will help, but it is a real amount of churn for a library whose selling point is stability and independence.

## Conclusion

OCaml-containers fits an OCaml developer who wants the parts of a standard library that OCaml never provided, since the modules are independent, BSD licensed, free of unix and str dependencies, and can be opened as a drop-in replacement for the stdlib modules or used one at a time by their CC-prefixed names. It is the wrong choice if you want a batteries-included framework, since the scope is deliberately data structures and combinators and nothing else, and it is the wrong choice if you want the thread support the library used to have, since containers-thread has been removed in favour of Moonpool. Before adopting it, check the CHANGELOG for the 3.0 breaking changes, note that v3.18 was released on 2026-02-15 while the last push was on 2026-10-12, and expect to have to rename some functions when you move from the 2.x series.

## FAQ

### What is OCaml-containers?

It is a modular, clean extension of the OCaml standard library focused on data structures, combinators and iterators, with no dependencies on unix, str or num. Every module is independent and prefixed with CC in the global namespace, and open Containers brings enhanced versions of the standard modules into scope.

### What breaking changes came with OCaml-containers 3.0?

Some sub-libraries became their own packages or were removed, so containers.sexp now lives in containers and containers.data needs the separate containers-data package. Functions deprecated in 2.8 were removed, so old-style iteration is now named of_iter and to_iter over the standard Seq.t. Array_slice and String.Sub were removed with no replacement, some functions were renamed, and pretty-printer separators became unit printer arguments.

### Why did OCaml-containers make operators monomorphic?

Because polymorphic comparison gives one answer for structural comparison and another for semantic comparison, which is wrong for maps and badly wrong for hashtables, where a key clash can make identical tables compare as different. The readme frames it as a bug every time the compared type may vary, and points to CCEqual, CCOrd and CCHash as the explicit alternatives.

### Which sub-libraries does OCaml-containers provide?

There is a core package in containers, plus containers.unix for unix helpers around spawning subprocesses and handling resources, containers.bencode for the simple json-like bittorrent-derived format, containers.cbor for CBOR, containers.scc for the functorized strongly connected component algorithm, and a separate containers-data package for extra structures.

### What licence is OCaml-containers released under?

BSD-2-Clause, with the LICENSE file at the root of the repository, and the library extends the OCaml standard library under the same BSD licence. The last push was on 2026-10-12 and the newest release is v3.18 from 2026-02-15.

## Sources

- [c-cube/ocaml-containers on GitHub](https://github.com/c-cube/ocaml-containers)
- [License: BSD-2-Clause](https://github.com/c-cube/ocaml-containers/blob/main/LICENSE)
- [Project website](https://c-cube.github.io/ocaml-containers/)
- [README](https://github.com/c-cube/ocaml-containers/blob/main/README.md)
- [Releases](https://github.com/c-cube/ocaml-containers/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/c-cube-ocaml-containers
