I’ve noticed that idiomatic OCaml does not seem to use qualified imports in code very much. Mostly there is the use of open keyword in code or you may have something verbose like Core_kernel.Map.empty (or shorter Map.empty if Core_kernel is open).
In Haskell there are a lot of qualified imports so instead of Core_kernel.Map.empty people might do a qualified import and subsequently refer to things with like M.empty – the M serving as a reminder where the empty function has come from.
You can emulate this in OCaml easily by doing:
module M = struct include Core_kernel.Map end
...
(* possible to use M.empty now *)
M.empty
...
Am I correct in my understanding?
Question
Why aren’t qualified imports used more often? I can see this being useful in tutorials. A lot of tutorial code has a bunch of opens and then its difficult to understand where things are coming from.
It’s a question of style, OCaml gives a lot of freedom there.
Personally I use very rarely open but a lot of qualified imports
(often as single-letter modules).
To my knowledge, the following statement produce a new module space with a copy of the module Core_kernel.Map, which increases the executable size and makes it hard to make some optimizations on the dependency tree:
module M = struct include Core_kernel.Map end
Instead of creating a copy, you should prefer to use a module alias:
why is copying a module possible? when would you need to do that or why?
also, is there any way to import individual functions from a module? if no, why not? (looking at something like Dream, how is it helpful for someone to see the module name Dream on every single line? assuming you write small modules, seeing the function names referenced in imports somewhere near the top would have been just fine… same as most languages? I guess not.)
You would copy a module if you needed to build a new module based on the old one but keep the new and old stuff together ‘flattened’ in a single module for ease of use. Otherwise you’d have to nested modules inside each other every time you wanted to add some new functionality.
You can easily ‘import’ individual functions: let form = Dream.form and so on. I.e. not really a specialized import feature, but then again special syntax is arguably not needed here.
Ease of use is part of it. Another reason is that you might need to satisfy some existing interface, and then you don’t get to pick whether to flatten or not. For example, suppose you have a Map.OrderedType and you want to implement sig include Map.OrderedType val to_string : t -> string end.