../

#import "/template.typ": *

#doc-template(
title: "Writing Python with an OCaml Flavor",
date: "July 14, 2026",
parindent: 1.2em,
body: [


To make code feel like OCaml, a few ingredients are essential:

- Algebraic data types (most languages already have product types; the interesting part is sum types)
- Pattern matching
- Functor-based polymorphism

Here's a snippet extracted from _Real World OCaml_ that demonstrates all three:

```ocaml
module type Comparable = sig
type t
val compare : t -> t -> int
end;;

module Make_interval(Endpoint : Comparable) = struct
type t = | Interval of Endpoint.t * Endpoint.t
| Empty
let create low high =
if Endpoint.compare low high > 0 then Empty
else Interval (low,high)
let contains t x =
match t with
| Empty -> false
| Interval (l,h) ->
Endpoint.compare x l >= 0 && Endpoint.compare x h <= 0
end;;

module Int_comparable = struct
type t = int
let compare = Int.compare
end

module Int_interval =
Make_interval(Int_comparable);;

let i1 = Int_interval.create 3 8;;
let c = Int_interval.contains i1 5;;
```

After several rounds of upgarde, modern Python now has all the features it needs to simulate these OCaml features — at least at some level. You can get close to the feel of classical, 1990s-era OCaml, but don't expect to reach for GADTs or algebraic effects any time soon.

Here's the Python equivalent of the OCaml code above:

```python
from typing import Callable
from dataclasses import dataclass

@dataclass
class Comparable[T]:
compare: Callable[[T, T], int]

@dataclass
class Empty:
pass

@dataclass
class Interval[T]:
low: T
high: T

type IntervalType[T] = Interval[T] | Empty

@dataclass
class IntervalModule[T]:
create: Callable[[T, T], IntervalType[T]]
contains: Callable[[IntervalType[T], T], bool]

def Make_interval[T](Endpoint: Comparable[T]) -> IntervalModule[T]:
def create(low: T, high: T) -> IntervalType[T]:
if Endpoint.compare(low, high) > 0:
return Empty()
return Interval(low, high)
def contains(t: IntervalType[T], x: T) -> bool:
match t:
case Empty():
return False
case Interval(l, h):
return Endpoint.compare(x, l) >= 0 and Endpoint.compare(x, h) <= 0
return IntervalModule(create=create, contains=contains)

def int_compare(a: int, b: int) -> int:
return (a > b) - (a < b)

Int_comparable = Comparable(int_compare)

Int_interval = Make_interval(Int_comparable)

i1 = Int_interval.create(3, 8)
c = Int_interval.contains(i1, 5)
```

To sum up:

#box(width:80%)[
#figure(
align(left)[
#table(
columns: 2,
stroke: 0.5pt + luma(200),
fill: (col, row) => if row == 0 { luma(240) } else { none },

[*OCaml Ingredient*], [*Python Implementation*],
[*ADT*], [Type annotations],
[*Pattern Matching*], [Built-in syntax (`match-case`) + `dataclass`],
[*Functor*], [Generic `dataclass` + generic higher-order functions],
)
]
)
]

I don't yet know how well this style holds up at scale, or what hidden pitfalls it might have. But for this small example, it's an interesting experiment.

])


Email: i (at) mistivia (dot) com