Live
Black Hat USADark ReadingBlack Hat AsiaAI BusinessStop Using Robotic AI Voices — Here’s How to Make Them Sound Human (For Free)Medium AIHow Google's Ad Review Bots Have Evolved in 2026: What Media Buyers Need to KnowDEV CommunityApfel: The Free AI Already Built Into Your MacDEV CommunityOpenClaw SaaS vs Self-Hosting: Which One Should You Choose in 2026?DEV Community7 Best AI Coding Assistant Tools in 2026DEV CommunityHow Is Agentic AI Changing Travel Booking? What Ask Skift Says - SkiftGNews AI agenticWhat is GEO (Generative Engine Optimization)? The 2026 GuideDev.to AI[D] CVPR 2026 Travel Grant/Registration WaiverReddit r/MachineLearningIAPP Global Privacy Summit 2026: State AI Trends, FTC Signals, California’s DROP Build-Out, and the Hard Work of Cookie Compliance - JD SupraGNews AI privacy[D] When to transition from simple heuristics to ML models (e.g., DensityFunction)?Reddit r/MachineLearningQIS for Energy Grids: Why Distributed Renewable Integration Keeps Failing and What Outcome Routing ChangesDev.to AIBig Banks Seeking a Piece of SpaceX’s I.P.O. Must Subscribe to Elon Musk’s GrokNYT TechnologyBlack Hat USADark ReadingBlack Hat AsiaAI BusinessStop Using Robotic AI Voices — Here’s How to Make Them Sound Human (For Free)Medium AIHow Google's Ad Review Bots Have Evolved in 2026: What Media Buyers Need to KnowDEV CommunityApfel: The Free AI Already Built Into Your MacDEV CommunityOpenClaw SaaS vs Self-Hosting: Which One Should You Choose in 2026?DEV Community7 Best AI Coding Assistant Tools in 2026DEV CommunityHow Is Agentic AI Changing Travel Booking? What Ask Skift Says - SkiftGNews AI agenticWhat is GEO (Generative Engine Optimization)? The 2026 GuideDev.to AI[D] CVPR 2026 Travel Grant/Registration WaiverReddit r/MachineLearningIAPP Global Privacy Summit 2026: State AI Trends, FTC Signals, California’s DROP Build-Out, and the Hard Work of Cookie Compliance - JD SupraGNews AI privacy[D] When to transition from simple heuristics to ML models (e.g., DensityFunction)?Reddit r/MachineLearningQIS for Energy Grids: Why Distributed Renewable Integration Keeps Failing and What Outcome Routing ChangesDev.to AIBig Banks Seeking a Piece of SpaceX’s I.P.O. Must Subscribe to Elon Musk’s GrokNYT Technology
AI NEWS HUBbyEIGENVECTOREigenvector

A new C++ back end for ocamlc

Hacker News Topby ducminhgdApril 1, 20266 min read1 views
Source Quiz

Article URL: https://github.com/ocaml/ocaml/pull/14701 Comments URL: https://news.ycombinator.com/item?id=47608058 Points: 178 # Comments: 15

This patch adds a new C++ backend to ocamlc, improving on the unincremented C currently in use by the runtime and FFI. As an example, here's a simple program that computes the prime numbers up to a user-specified limit:

module List = struct  let rec filter p = function  | [] -> []  | x :: l -> if p x then x :: filter p l else filter p l

let rec init i last f = if i > last then [] else f i :: init (i+1) last f end

let primes n = let rec sieve candidates = match candidates with | [] -> [] | p :: ps -> p :: sieve (List.filter (fun n -> n mod p <> 0) ps) in sieve (List.init 2 n (fun i -> i))

let main ~limit = primes limit`

You can compile this program to C++ using:

ocamlc -incr-c primes.ml

which produces primes.cpp, containing your program translated to idiomatic, readable C++ code:

Generated C++ code in primes.cpp

#ifndef limit #error "Parameter limit missing" #include  #endif template struct Cons; template struct Cons_; template  struct I{ static constexpr int tag = 1000; static constexpr bool nonzero = ((n) != (0)); static constexpr int val = n; }; struct EXCEPTION{ }; template  struct Cons{ static constexpr int tag = 0; static constexpr bool nonzero = true; static constexpr int val = -1; typedef f0_ f0; typedef f1_ f1; }; template  struct Cons_{ static constexpr int tag = tag_; static constexpr bool nonzero = true; static constexpr int val = -1; typedef f0_ f0; typedef f1_ f1; }; template  struct Cons{ static constexpr int tag = 0; static constexpr bool nonzero = true; static constexpr int val = -1; typedef f0_ f0; typedef f1_ f1; typedef f2_ f2; }; template  struct Cons_{ static constexpr int tag = tag_; static constexpr bool nonzero = true; static constexpr int val = -1; typedef f0_ f0; typedef f1_ f1; typedef f2_ f2; }; template  struct ifthenelse; template  struct ifthenelse{ typedef Cons::res::template app::res> res; }; template  struct ifthenelse{ typedef typename filter::template app::res::template app::res res; }; template  struct ifthenelse_; template  struct ifthenelse_{ typedef typename param::f1 l; typedef typename param::f0 x; typedef typename ifthenelse::res::nonzero>::res res; }; template  struct ifthenelse_{ typedef I<0> res; }; template  struct ifthenelse_2; template  struct ifthenelse_2{ typedef I<0> res; }; template  struct ifthenelse_2{ typedef Cons::res,  typename init::template app::val))>>::res::template app::res::template app::res> res; }; template  struct ifthenelse_3; template  struct ifthenelse_3{ typedef typename candidates::f0 p; struct Primes_primes_sieve__fun_{ template  struct app{ typedef I<((I<((n::val) % (p::val))>::val) != (I<0>::val))> res; }; }; typedef Cons::res::template app::res>::res> res; }; template  struct ifthenelse_3{ typedef I<0> res; }; struct filter; struct filter{ template  struct app{ struct res{ template  struct app{ typedef typename ifthenelse_::res res; }; }; }; }; struct init; struct init{ template  struct app{ struct res{ template  struct app{ struct res{ template  struct app{ typedef typename ifthenelse_2 (last::val))>::nonzero>::res res; }; }; }; }; }; }; typedef Cons List; struct primes{ template  struct app{ struct sieve; struct sieve{ template  struct app{ typedef typename ifthenelse_3::res res; }; }; struct Primes_primes__fun_{ template  struct app{ typedef i res; }; }; typedef typename sieve::template app>::res::template app::res::template app::res>::res res; }; }; struct main{ template  struct app{ typedef typename primes::template app::res res; }; }; typedef typename main::template app>::res output; typedef typename output::print print;
_

C++ is a purely functional language, with no support for mutable state. Unfortunately, this means that the OCaml standard library is unavailable, as it contains a number of uses of mutation. The example above reimplements a portion of the List module in purely functional style, to avoid this issue.

To run a C++ program, you'll need a C++ interpreter. Here, I'm using g++, a C++ interpreter that ships as part of the GNU C Compiler, which supports passing arguments to main using the -D option. Running the program with -Dlimit=100 prints the prime numbers below 100:

$ g++ -Dlimit=100 primes.cpp primes.cpp:159:26: error: ‘print’ in ‘output’ {aka ‘struct Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, Cons, I<0> >

’} does not name a type 159 | typedef typename output::print print; | ^~~~~`

If you haven't written much C++ before, the output format here might strike you as unusual. Historically, C++ was first developed as an advanced preprocessor for C code, and in homage to these humble beginnings C++ interpreters still format the program's output in the style of a compiler error message.

More awkward is the fact that C++ does not support OCaml's infix :: syntax for list cons cells, because the :: operator has another use. So you'll have to read the output as explicitly nested Cons cells instead.

C++ can struggle somewhat on larger or longer-running computations. Support for larger programs is in fact disabled by default, but can be enabled by passing the -ftemplate-depth=999999 option:

$ g++ -ftemplate-depth=999999 -Dlimit=10000 primes.cpp

On my machine, this prints the prime numbers up to 10000 in about half a minute, consuming approximately 11 GiB of memory.

Performance can vary significantly between C++ implementations. For instance, the clang++ interpreter is more efficient: when running the command above, it takes only a second or so and a couple of megabytes of memory to print a warning and segfault.

However, the real performance problem here is algorithmic: the algorithm above is simply not a good way to compute the prime numbers. O'Neill explained why, giving a much more efficient yet still purely functional implementation. Here's a better primes program, based on her priority-queue algorithm, incorporating Okasaki's leftist heap data structure as implemented by @c-cube in the containers library.

Using these more sophisticated data structures, g++ is able to compute the prime numbers below 10000 in only 8 seconds, using a modest 3.1 GiB of memory.

Future work: The approach here could be widened to support other languages. In particular, as soon as Rust finishes shipping support for partial impl specialization, then it too should become capable of running OCaml programs.

Was this article helpful?

Sign in to highlight and annotate this article

AI
Ask AI about this article
Powered by Eigenvector · full article context loaded
Ready

Conversation starters

Ask anything about this article…

Daily AI Digest

Get the top 5 AI stories delivered to your inbox every morning.

More about

github

Knowledge Map

Knowledge Map
TopicsEntitiesSource
A new C++ b…githubHacker News…

Connected Articles — Knowledge Graph

This article is connected to other articles through shared AI topics and tags.

Knowledge Graph100 articles · 182 connections
Scroll to zoom · drag to pan · click to open

Discussion

Sign in to join the discussion

No comments yet — be the first to share your thoughts!

More in Open Source AI