Is there a reason not to use Caml standard library modules together with Base or Core?

I’m using Base for a project, but I’m also using Caml.Seq because I find it a bit easier to work with than Sequence, I use Caml.Str for regex, and I also used some of the I/O functions. I get the impression this is frowned upon, but I’m not sure why.

1 Like

Independently from the use of Base, using the Str module from the stdlib is definitely frowned upon because its API is very imperative and error-prone.
I think people usually recommend using https://github.com/ocaml/ocaml-re as an better behaved alternative.

4 Likes

Thanks for link. I’ll try to use it. Side rant: way too many 3rd-party OCaml libraries, including this one lack documentation with examples (or anything outside of .mli files).

5 Likes

That’s true. But you know how it goes: don’t hesitate to contribute such examples – I’m sure pull requests adding more documentation are welcome :slight_smile:.

1 Like

With Base open, the standard library modules are deliberately shadowed so that you can only refer to, e.g., Str by the name Caml.Str. This is to make it clear at each call site which library you are using, because otherwise it might not be obvious which String module you’re referring to. Some of the functions that are in both Stdlib and Base behave slightly differently—e.g., List.map is tail-recursive in Base, but not in Stdlib.

For the modules of the standard library where Base does not have an exact replacement (e.g., Caml.Seq, for which Sequence is analogous but a different type), it is encouraged to use the unambiguous name to refer to them if you need that particular type.

For I/O functions, see Stdio as an alternative to the Stdlib that follows Base conventions.

2 Likes

OK, that’s more or less what I’ve been doing. I’ll start using Stdio (I think it’s in Core, though, not in Base, but I guess I could “upgrade” to Core).

I was looking at the implementation of this the other day, and I was actually pretty surprised. It’s tail-recursive after the first five-thousand elements . Base’s implementation of List.map is interesting reading.

Assuming I figure it out from trial and error with enough confidence to write any documentation, perhaps I shall.

You can use Stdio without Core, if you want. Just add stdio to your dune file (assuming you’re using dune for your build system). Base and Stdio are both included in Core_kernel, which is included in Core.

1 Like

It’s just about reducing complexity. JS libraries have very uniform APIs following standard conventions (e.g., foo vs foo_exn, functions being passed as ~f, etc…). This uniformity makes it easier to read and maintain your code. You lose that benefit if you mix in the standard library, so it’s best avoided unless you have a very good reason.

1 Like

I see what you mean, but at the same time, I’m going to be using third-party libraries which don’t follow Jane Street conventions. (Uutf and now Re). I suppose I could wrap Seq in a way that is more consistent with JS style, but there will always be compromises when using with non-JS libraries, which I assume most projects do.

I’ll do that.

Out of curiosity, does it make a difference in the output if I switch from Base to Core? Is there a reason not to? I started using it because it was smaller, but I’m not actually sure if it makes any difference at the end of the day.

The recommendation is just to use Caml.Seq if you need that type, then. It is not terribly difficult to convert between 'a Seq.t and 'a Sequence.t, but we don’t have such a function in Base yet because Seq was only added in 4.07, and the latest stable version of Base currently supports OCaml >= 4.05.

If by output you mean the results of your program, probably not. If by output you mean the size of the generated binary, then probably, although I would test to make sure. A reason to use Base over Core might be to avoid pulling in dependencies you don’t need. Base is much quicker to install and compile compared to Core_kernel/Core, for example.

1 Like

Adding to @bcc32 answer, Base can be compiled to JavaScript whereas Core cannot.

2 Likes

This is what I was thinking about. I know you pay for everything you statically link in C (or… that’s the impression I have), but since OCaml must do a lot of code generation based on inference of generic types, I thought it might be more of a “pay for what you use” scenario—but I haven’t checked.

Thanks for mentioning that. I’m not targeting JS at the moment, but it’s not beyond the realm of possibility. If anything, it’s a reason to keep the I/O stuff separate from the main library.

I think this depends on what ends up getting inlined, although TBH I’m no compiler expert so I can’t really say too much in detail about the general case. Generally, you don’t pay for modules you don’t reference at all, but even a reference to a single value in a module brings in the whole module.

One property of Core which makes its code size so big is its extensive use of functors, e.g., Comparable.Make (which provides such modules as String.Map). I think the dead code elimination for functors is pretty naive/non-existent at the moment (though I am eager to be corrected on this point). That means that a lot of large generated modules are necessarily included in the final executable.

1 Like

When I thought about it again this morning, I realized that my reasoning about paying for what you use was flawed because OCaml is not like C++ —generating different function code for type specialization), since everything is boxed.

By the way, the next release of Base will only support OCaml 4.07 and newer, so we plan to add a function to convert between Seq.t and Sequence.t.

4 Likes

Why not depend on the seq package and support older versions of OCaml,
like other stdlib overlays do? It’s similar to result or bytes in
the past.

2 Likes

My impression from working with opam has been it’s not difficult for people to use the latest version of OCaml. Is that not the case?

I wasn’t aware of the seq package, that’s good to know about. Unlike result, Jane Street doesn’t really use the Seq.t type at all, so we probably didn’t look too deeply into a compatibility layer for it, but this would probably have fit the bill.

The reason Base no longer supports older versions of OCaml isn’t to pick up the addition of Seq.t, by the way. Roughly, Base only supports the four most recent major releases of OCaml, because we want to eagerly take advantage of new language features, such as empty variant types and include shadowing. Those are harder to write compatibility packages for.

1 Like