Should I prefer Core_kernel or Base's APIs where they differ?

Though usually Core_kernel is a superset of Base (and its related libraries)'s functionality, I’ve noticed there are some places where the exposed API surface is a little different: for instance, Base_quickcheck (which expects modules as input to its tester) vs Core_kernel's Quickcheck (which expects functions), and the fixed-comparator Set/Map instances (which come as standard through Core_kernel's comparable functors, but need something like Set.M(Module) in Base, and generally seem to be less supported than passing comparator witnesses around directly).

Is it as clear-cut as the Core_kernel versions being old/deprecated/not recommended and the Base ones being new/cleaner/recommended/etc? Should I prefer using the Base-flavour API over the Core_kernel one? Or is it a bit more nuanced?

More generally, should I be trying to avoid using Core_kernel where possible (and only opening it when I actually need its extensions over Base), or is it still a generally useful Stdlib replacement? At the time I started using Base/Core_kernel/Core the latter was the feeling I got, so it (and Core!) have pervaded my OCaml code quite a lot :slight_smile:

I figure this touches on something I asked a while back, which was whether Format was deprecated—the fact that Core_kernel still carries it but Base doesn’t makes me wonder if Base is the ‘new’, less baggage-y way to do things.

I hope that the Core developers will drop by and provide a more authoritative answer, but while we’re waiting for them, here is my two cents.

My personal modus of operandi is, if you can, then use the Base interface. Mostly because of two reasons, the Base interface is usually more stable, and if there are any discrepancies between Base/Core/Core_kernel/… is that because the latter was not updated, where the Set.M, etc are good examples. This is a new idea that didn’t yet propagate to the full extent of the JS family.

More generally, I wouldn’t advise against opening Core_kernel or Core. In general, you shall try to find the comfort zone, where your package doesn’t have too many dependencies, but you’re still not reimplementing too much stuff. This comfort will vary depending on a package, its target auditory, etc. For example, when I’m developing personal (or in-house) tools, I bring all the power of Core and Core_extended to my hands, even if it takes hundreds of packages. But when I develop an abstract library which may attract many different users, I’m trying to minimize the dependency list, even if it will end up in working with List.fold_left and maps from the standard library.

I think Ivan’s summary is right on the nose. Set.M is definitely an example of an idiom where Base has the better behavior, and we haven’t fully propagated that change through Core_kernel and Core yet.

The Format module is an interesting case, where we’re not excited to commit to supporting it long-term in Base, but we don’t necessarily have a view as to what the right alternative is either.