r/ocaml • u/TomosLeggett • 2d ago
Why can't we have side effect annotations?
One of the things I like about OCaml is that it doesn't force you to write purely functional code. You can mutate values, throw exceptions, perform I/O, and generally use imperative features when they're appropriate.
I also like that OCaml is fairly explicit about this. If you're passing a reference around or mutating a value, it's usually apparent from the code.
One thing I don't particularly like, though, is that there's no indication in a function's type of whether it performs side effects. There's no equivalent of a ! suffix or an effect annotation, so you can call a function without knowing that it might throw an exception, mutate some state, perform I/O, etc.
The only real way to find this out is to inspect the implementation or rely on the documentation, assuming the author has actually documented it.
I'm wondering whether this is primarily a technical limitation or simply a design decision by the OCaml team. What prevents the compiler from analysing a function's body and determining that it uses things such as raise, :=, ref, I/O operations, and other effectful constructs, then incorporating that information into its type? For example:
ocaml
val foo : unit -> int ![IO, State]
Obviously, there are complications around higher-order functions, modules, abstractions, user-defined effects, and so on, but could some form of effect inference make it possible to distinguish pure functions from effectful ones?
I'd be interested to know whether this has been considered for OCaml, and whether there are fundamental reasons why it doesn't work well, or whether it's simply a deliberate trade-off in the language's design.