1. 31

  2. 8

    This is great, and I’ve heard good things about module systems. But how does all this work in practice? What are the applications of modules (or what approaches to programming do they facilitate)? From an external perspective, it seems like a neat gimmick.

    1. 6

      In practice, how it works in OCaml:

      • Every source file automatically becomes a module, e.g. note.ml becomes a module Note in the project
      • You don’t need to import any modules; all modules in the same compilation unit are implicitly available. There are namespacing rules to manage module names globally
      • Syntactic modules (defined by module Foo = struct ... end blocks) are cheap and easy to create, and semantically equal to source file modules, which encourages spinning up nested modules to namespace things properly inside source files
      • It’s customary to prefix values, with their modules, so you’ll see a lot of List.map, Array.map, etc., instead of Haskell-style fmap
      • Interfaces allow managing the visibility of module contents in a very fine-grained way, e.g. which members to display, whether to make types concrete or abstract. They even allow quickly spinning up new types with the same memory representation i.e. no boxing
      • Module-to-module functions (functors) are the way to do programming-in-the-large, i.e. generic programming, dependency injection, and many other techniques

      The module system is, from a high level, quite simple and consistent. This makes it very easy to reason about how it should behave. Modules are the backbone of OCaml programming.

      EDIT: I forgot to mention another benefit that module interfaces buy you: build speed. The technique that OCaml usages to provide that build speed goes back to Modula-2, and I’ve written about it here: https://dev.to/yawaramin/ocaml-interface-files-hero-or-menace-2cib

      1. 5

        It allows for pretty generic libraries to be written. In OCaml that’s exemplified by libraries like:

        • ocamlgraph, a graph library functorized over edges, nodes, labels, etc. with a large collection of algorithms;
        • mirage OS, a library for writing unikernels composed of a collection of functors over concrete hardware, clock implementations, tcp implementations, etc.
        1. 3

          Before reading the article, I thought this was a funny bit of satire.

          After reading the article, I too am curious what parameterized modules buys you. Coming particularly from Rust, where modules are very much a first class part of the language, but not parameterizable, it’s not clear to me what that would buy me.

          1. 5

            If they are not parameterizable, how are they first class? :O

            OCaml Functors are just functions from structures to structures. Here is a more thorough explanation: https://www.cs.cornell.edu/courses/cs3110/2018sp/l/08-functors/notes.html

            What do functions buy you? Why can’t you apply whatever they buy you to modules?

            1. 1

              Ahh, I see now. In Rust and Haskell, such parameterized structs, AKA Generics, are provided by algebraic data types, while the module system enables scoping and hiding. Looks like it’s primarily a different terminology for essentially the same thing.

              1. 3

                No, ADTs usually do not enable this. The Haskell analogue for this is called Backpack, and it was modelled after the OCaml system. There is a thesis documenting it.

          2. 1

            Functors (parameterized modules) are essential in OCaml to implement something like maps or sets over a data type where the implementation needs access to properties like order of the element type. Polymorphism permits to implement a data structure (like lists, pairs) that don’t depend on specific properties of the element type. When an implementation needs them there is no other mechanism than providing them as factor argument. Haskell does not have this problem because type classes can be used to provide access to order, equality, or a show function.

          3. 4

            Is the idea really exotic? Generics in Java, Templates in C++, and others provide essentially the same mechanism, don’t they?

            1. 1

              All successful programming languages are alike; every unsuccessful programming language is unsuccessful in its own way.

              Up to isomorphism, there’s only one happy family.