Image
Build
Connect & operate
Design & teams
Start hereScope a build in one callBring a spec, a wireframe, or a paragraph. You leave with an architecture, a timeline, and a number.Book a scoping call
AI software
LLM & data systems
Vibe coding
Ready to ship?Put AI where the work isAgents, RAG, and private LLMs wired into the systems your team already uses — not a chatbot bolted to a homepage.Discuss an AI project
Domain firstWe learn your workflow before we model itRegulated, operational, or high-volume — the constraints belong in the schema, not in a training doc.Talk about your domain
Plan smarterEstimate before you commitCost ranges, scope templates, and the questions we ask in discovery — free, no form.Open the cost calculator
Real conversationsTalk with a technical leadNo SDR, no discovery gauntlet. The person on the call is the one who scopes the build.Book a call
Eric Lamanna
Author
OCaml ReasonML Interop: Sharing Code Across Syntaxes — featured image
9/29/2026

OCaml ReasonML Interop: Sharing Code Across Syntaxes

In the bustling realm of modern software development, projects rarely stay still for long. Teams grow, syntax fashions shift, and half of your colleagues swear by curly braces while the other half worship indentation. OCaml and its slick cousin ReasonML illustrate this divide beautifully—two faces of the same powerful language core. If your codebase wants to welcome both camps without fracturing into parallel universes, you need a clear strategy for sharing logic seamlessly across the syntaxes.

Understanding the Two Syntaxes

OCaml: A Quick Refresher

OCaml’s original syntax feels a bit like a vintage sports car, lean, fast, and intimidating until you master the clutch. You write let bindings, pipe operators, and pattern matches in concise shapes that make type theorists smile. The compiler rewards you with heavyweight guarantees about immutability and soundness while demanding that you respect its terse punctuation. Once you get the rhythm, the language almost disappears, leaving behind pure expression.

ReasonML: Familiar Syntax, Same Beast

ReasonML steps onto the scene wearing jeans and sneakers. It keeps OCaml’s sturdy engine but swaps the metal dashboard for a user interface that resembles modern JavaScript. Semicolons dot the lanes, object-style labels feel intuitive, and JSX support woos front-end developers. Underneath, the compiler remains precisely the same. This means that ReasonML files and OCaml files can link together without negotiation, provided you keep their build pipeline and interfaces aligned.

Same Bytecode, One Extra Compile StepBuild steps between source file and the shared ocamlc backend1.ml file2.re file(via refmt/bsrefmt)

Why Share Code Anyway?

Team Flexibility and Onboarding

Mixing syntaxes may sound like inviting chaos, yet it often shortens onboarding. A newcomer from the JavaScript world can slide into ReasonML, contribute value fast, and slowly absorb deeper functional patterns. Veteran OCaml engineers, meanwhile, keep their beloved syntax. Shared modules mean everyone compiles toward the same bytecode, so deadlines stay happy and nobody feels coerced into a stylistic makeover.

ReasonML Shortens the Path to a First ContributionIllustrative days for a new engineer to ship a first change, by entry syntax10Straight intoOCaml syntax3Via ReasonML'sJS-familiar syntax

Keeping Libraries Consistent

Having a single source of truth beats maintaining parallel libraries. Imagine patching a bug in a data-validation routine twice because each syntax keeps its clone. By architecting reusable modules that straddle both dialects, you cut redundancy, lower maintenance costs, and ensure that improvements propagate instantly. Consistency breeds confidence, and confidence lets you sleep at night instead of nervously glancing at issue trackers.

One Shared Module Beats Two Parallel ClonesIllustrative hours to patch one bug in a data-validation routine8Duplicated per-syntaxlibraries2Single sharedcross-syntax module

The Anatomy of Interop

Shared Build System With Dune

Dune is the friendly build shepherd for the OCaml ecosystem. It understands that ReasonML files compile through refmt and bsrefmt before feeding the same ocamlc. By placing both .ml and .re files in the same library stanza, you instruct Dune to treat them as siblings. A single dune file can expose interfaces under a common public name, hiding the inner syntactic split from downstream users.

Interface Files and Signature Harmony

Interface files (.mli or .rei) act like diplomatic treaties. They define what each module promises without revealing private details. For cross-syntax sharing, keep signatures in one style—often OCaml because tooling expects .mli—and let ReasonML implementations rely on those contracts. This decouples surface expectations from internal spelling differences. Think of it as agreeing on handshake rules before starting the dance.

Patterns for Shared Modules

Functors and Reusable Logic

Functors, modules that take other modules as parameters, let you stamp logic onto new contexts with minimal fuss. Write a functor in OCaml, supply concrete modules written in ReasonML, and everything links as if born together. This architectural pattern encourages high cohesion and low coupling, all while sidestepping syntax disputes.

Polymorphic Variants for Safe Boundaries

Polymorphic variants shine at expressing flexible unions without extra boilerplate. They work identically in both syntaxes, so you can declare error types or command sets that travel freely. Using variants as boundary markers keeps your shared API tight, reduces stringly-typed mishaps, and still feels ergonomic whether you prefer \`Ok or #Error.

Practical Tips for a Smooth Workflow

Editor and Tooling Setup

Convince your IDE to treat .re files with the same respect as .ml. VS Code with the OCaml Platform extension or Emacs with merlin can juggle both. Configure formatters (ocamlformat, refmt) to run on save, so no one argues over whitespace. Continuous integration should compile the whole matrix to catch incompatibilities early.

Versioning and Dependency Tricks

Pin your OCaml compiler version in opam or esy because minor mismatches can spark errors. When publishing a shared library, bump the version when interface files change, not just when implementation tweaks occur. Document which modules exist in which syntax to avoid surprise requires. Small ritual, big peace of mind.

Common Pitfalls and How to Dodge Them

Syntax Gotchas That Bite

Certain tokens translate differently. OCaml’s back-tick polymorphic variant syntax becomes a tickless backslash in ReasonML. Multi-line strings need extra care. Peek at the compiled output if something smells off. The fix is usually a tiny character adjustment rather than a conceptual overhaul.

Runtime Surprises to Watch

Remember that both syntaxes ultimately run identical bytecode, so runtime surprises usually come from misaligned expectations, not divergent behavior. Still, pay attention to record field names because ReasonML lowercases them by default. Also, err on explicit external declarations when crossing into JavaScript through BuckleScript to avoid conversions.

Designing a Shared Folder Layout

Library Versus Application Code

A tidy folder tree limits context switching. Place generic utilities in a lib directory that contains both .ml and .re files side by side. Each file pair should implement the same functionality but in only one syntax, mixing would defeat the purpose. The point is to let newcomers browse a predictable hierarchy and find reusable parts without wading through unrelated assets. Your application layer, perhaps under app, can freely pick whichever syntax fits the contributor writing that feature.

Separate Syntax Subdirectories Versus Mixed

Some teams prefer a clear visual split: src-ocaml and src-reason. Others keep everything together and rely on file extensions to communicate syntax. Both models work; the important part is documenting your choice and sticking to it. Consistency beats hipster folder names every single day.

Testing Across Syntaxes

Property Tests That See Both Worlds

Testing is the glue that proves your interop works. Using frameworks like qcheck or alcotest, you can write property tests that import modules implemented in either syntax and verify they behave identically. For instance, generate random lists, sort them with functions from each syntax variant, and assert equality. When the test passes, you know your abstractions survived translation.

Continuous Integration Matrix

Automate confidence. Configure your CI pipeline to compile and run tests under multiple compiler versions, enabling warnings as errors. Include a separate job that only formats the code to ensure style guides do not rot. A green badge on your repository tells future maintainers they can refactor with courage, not crossed fingers.

Advanced Interop Techniques

Using gen Scripts to Transform Signatures

Sometimes you want to expose the same module under both syntaxes without duplicating interface files by hand. A small generator script written in OCaml can read a .rei file and spit out a matching .mli, or vice versa. This may feel like wizardry, yet it is just string manipulation combined with a keen eye for syntax quirks. Automating the dull part lets humans focus on algorithmic creativity instead of bracket counting.

Bridging With Foreign Function Interfaces

Both OCaml and ReasonML hook into C code through the same foreign function interface. If you maintain bindings to low-level libraries, write the thin layer once and call it from either syntax. The shared FFI ensures performance-critical paths remain fast while preserving type safety. It is a reminder that underneath the surface glamour, both dialects are still humble OCaml at heart.

Keeping Build Times Snappy

Large mixed codebases can swell compile duration quicker than a cat video goes viral. Speed things up by enabling incremental compilation, caching build artifacts, and pruning unused dependencies. Your teammates, and your laptop battery, will thank you during those marathon release nights.

Conclusion

Interoperability is not a magic trick; it is a bundle of small, disciplined habits. Keep interfaces explicit, compile early, test often, and resist bikeshedding over brace styles. When you do, OCaml and ReasonML modules weave into one predictable fabric that scales from hobby prototypes to production clusters.

Most importantly, remember that syntax is just the outfit your logic wears to work. Treat it as a matter of taste, not tribal identity. Respect that preference, and the shared language core will reward the whole team with robust, elegant programs that are a joy to maintain.

Author
Eric Lamanna
Eric Lamanna is a Digital Sales Manager with a strong passion for software and website development, AI, automation, and cybersecurity. With a background in multimedia design and years of hands-on experience in tech-driven sales, Eric thrives at the intersection of innovation and strategy—helping businesses grow through smart, scalable solutions. He specializes in streamlining workflows, improving digital security, and guiding clients through the fast-changing landscape of technology. Known for building strong, lasting relationships, Eric is committed to delivering results that make a meaningful difference. He holds a degree in multimedia design from Olympic College and lives in Denver, Colorado, with his wife and children.