How are comparison and arithmetic operators implemented in OCaml standard library?

I am looking to implement my own library to replace the OCaml standard library, as I am starting to use the language enough that having things named and implemented the way I find most sensible would be worthwhile.

I have replacements for a variety of data structures, etc, but not operators yet. I tried compiling without the standard library to see what I was missing and of course I need implementations of comparisons (=, <>, <=, != etc) and arithmetic (+, -, *, etc). Looking at the github repo for the standard library I see that they are external C functions, and was wondering where these implementations are,.I can’t seem to find the C files with those functions in the standard library, and ultimately these are parts of the Stdlib I wouldn’t change, but also it would be nice to see some examples of how calling C functions work in OCaml as I will inevitably need this for low level stuff and IO type stuff, and also how operator precedence and associativity is handled.

Any help is greatly appreciated :slight_smile:

1 Like

Sorry this isn’t particularly helpful but I would strongly advise not writing yet another standard library and instead just working with one that’s already there, maybe even the default Stdlib to minimize your dependency cone. My advice: get used to the idioms of the language and its standard libraries instead of writing your own. You’ll be more productive and your code will look more standardized, instead of an offshoot that no one recognizes.

2 Likes

I appreciate your point, but as someone who has done a lot of programming in C, where the standard library is far more sparse than a language like OCaml, I have found this to never be a huge problem, given people all use their own collection implementations anyway for example. But regardless, I am also in a fortunate position where all the code I write will only be written by me, for my own purposes, and in general I have found the more bespoke me tooling is to my needs the more productive I am.

1 Like

To answer your questions:

For the builtin comparisons, you’ll need to look at the OCaml repository, under the runtime directory - for example, here’s the compare function (and equal etc. below it): ocaml/compare.c at bf777c514f700154a7e08a584a19d0b79bc8d999 · ocaml/ocaml · GitHub.

For interfacing with C, the OCaml manual has a pretty self-enclosed guide on writing low-level bindings which should provide all you need: OCaml - Interfacing C with OCaml

Real world OCaml also has a section on using Ctypes, which is a library that provides a slightly more high-level interface for writing bindings: Foreign Function Interface - Real World OCaml.

That being said, echoing @yawaramin, writing yet another stdlib might not be a good idea. If you really want to, it might also be worthwhile checking out the existing stdlib replacements/enhancements first (containers, core, batteries) to see if there are any idioms you want to adopt.

3 Likes

Just as a side comment, note that this C code corresponds to the generic case when the type of the argument is not known. But the comparison operators (as well as the compare function) found in the stdlib use special compiler primitives which are type-specialized at compilation time for efficiency.

Cheers,
Nicolas

3 Likes

I notice the runtimes directory has a bunch of other stuff including stuff related to garbage collection. I presume this is separate but happens to be in the same folder, but just checking what do I actually lose when compiling with -nopervasives? Is the runtime otherwise untouched, but I am losing a bunch of functions, types and operators?

If anyone’s interested, I assume that happens here:

1 Like

You don’t lose anything; the only difference is that Stdlib is not opened by default anymore so in order to access its members you need to either use a prefix (Stdlib.open_in) or open it explicitly (open Stdlib).

Cheers,
Nicolas

1 Like

The comparison and arithmetic operations are implemented with compiler primitives in the standard library.
For instance, the = operator is defined as

external ( = ) : 'a -> 'a -> bool = "%equal"

where %prim is the name of a compiler primitive. If you want to reimplement your own standard library, you should bind those compiler primitives rather than use the FFI to reach the underlying C implementations. Not only it would be more efficient (since the compiler optimizes compiler primitives and not some arbitrary C bindings), it would be less brittle than relying on an implementation of the stdlib. Also, the lazy semantics of && and || can only be achieved with compiler primitives and not a C binding.

2 Likes

When compiling only (ocaml{c,opt} -c foo.ml), the only difference is that the standard library module (Stdlib, or Pervasives in old versions of the compiler) will not be opened by default.
When linking, roughly speaking none of the OCaml and C libraries that would normally be included by default are added. In bytecode, this mostly means stdlib.cma and std_exit.cmo, but in native mode even the runtime library (libasmrun.a) is not linked.
In theory this can be used to make very small OCaml executables (linking the strict minimum), but in practice without some deep knowledge of the compiler internals it’s very risky (and it’s not officially supported). So I would advise to make sure that you don’t pass the -nopervasives flag to any link command.

I have a solution that I’m pretty happy with, compiling all files individually with -nopervasives, except one which I am using to shadow and redefine some parts of the Stdlib that I wish to keep. And then linking the .cmx files without -nopervasives so that the one file can find it. I am however having one issue with lists. Not sure how list literals work in OCaml and where they’re defined but I can use list literals of I give a type annotation for my own List type (List.list), otherwise it thinks it’s of type list and is incompatible with List.list. Could this be something peeking through the original OCaml list implementation?