I find myself missing some functions from some modules in the Stdlib. Is it fine to simply overwrite them by writing new files, say, stack.mli and stack.ml in my /lib directory (in a project generated using dune init proj project_name) and then calling, say, Stack.mem, etc ? Or are there downsides and preferred solutions, maybe using different names for the extended modules, or functors ?
(* stack.mli *)
include module type of Stack
val mem : 'a -> 'a Stack.t -> bool
(** Check if the argument is a member of the stack. *)
val create_sng : 'a -> 'a Stack.t
(** Return a stack with the argument as single element. *)
val junk : 'a Stack.t -> unit
(** Remove the top element of a stack, throwing an exception in the case of
an empty stack. *)
(* stack.ml *)
include Stack
let mem x t =
let f b y = b || y = x in
Stack.fold f false t
let create_sng x =
let res = create () in
Stack.push x res;
res
let junk t = ignore (Stack.pop t)
Thanks. I see that my method is discouraged, but it is not said in favor of which other method. It looks like I should rename my files stack1.mli / stack1.ml and should not use include. Then, in my main file, I should call either Stack.xxx for the functions of the Stdlib or Stack1.yyy for my new functions.
Since I also want to add functions for lists, maybe it’s better to have files util.mli / util.ml where I put my stack functions and my list functions together, and then refer to them as Util.xxx in my main file ?
I didn’t get the sense of a consensus that it’s discouraged from those threads. In your shoes I’d probably use the module type of struct include Stack end form.
Another pragmatic option would be instead of writing an somewhat incomplete extended standard library, use one of the existing ones like Base or Containers, which come with a number of frequently reimplemented functions from the get-go.
Depends on your kind of pragmatism. If you just need a function here and there, simply adding them to your project is very pragmatic from a maintenance perpsective.
I don’t know Containers, I’m going to look at it, thanks ! As for Base, it actually overwrites some functions of the Stdlib (even some very common ones) and gives them a different meaning, which is a big NO.
By the way: I think it should be a big NO for everyone. What happens when a third person will want to rewrite a third “standard library” ? And a fourth ? We’ll end up with various OCaml-looking languages and maintenance (and learning) will be harder for everyone. It will be like the XKCD “standards” thing. Why couldn’t they join forces instead of competing ? Or if they want to enrich the library with other features, fine, but not rewriting the existing ones with a different meaning. And if they think of better design choices, they could push them to Stdlib, and everyone would benefit from it. Collaboration, not competition !
I think this is actually a big advantage because it makes the Stdlib look like it was designed in 2015, that means it incorporates a lot of learnings that we got from the original standard library. For good reasons the OCaml stdlib can’t really change but I do enjoy that I can just point people to use (Base.)List.find instead of always knowing not to use Stdlib.List.find but use the slightly more obscure Stdlib.List.find_opt, etc, etc.
In fact we already have a lot of “standard libraries” because often every project comes with its own implementation of things that were not in the standard library or weren’t there when the project was written or are not in the standard library of the minimum version of OCaml that they support, so for every project one has to look up how to achieve pretty simple things like “What do Maps support here, how can I get a Set, where are the prettyprinters, how do I deal with result, is there let* syntax” etc and the situation is pretty much what you describe.
It doesn’t help that some of the standard extensions have pretty bad APIs, e.g. a Map.update that can’t delete keys because the function does not accept 'a option as a return value, so you end up having to build your own version of Map.update on top of it.
Thanks @Leonidas for the explanations. Obviously I cannot tell which one is better, but I was simply frustrated to see two different and incompatible “standard” libraries.
As @dbuenzli advises, I will continue with Stdlib, which seems sufficient for now (and hope that I won’t discover, when my program is twice larger, that I have to switch to Base).