A `use`-like syntax

Hello, I came from Rust and found out that OCaml is a nice language.

I also found that in OCaml, module can be imported through linking and the namespace can be shortened using open syntax. In which, Rust equivalent of open Module syntax is like use module::*, shortening all entities under the module.

In OCaml, any specific syntax for picking some entity instead?
I think of let-ing the namespace, but this kind of verbose, i.e. let Sub = Module.Sub.

1 Like

There is no way to “partially open” a module in OCaml, but a common idiome is to rename Module to a shorter name with module M = Module. Then you write M.something instead of Module.something.

1 Like

Hey, this one is compile-able! (the let-binding I thought before wasn’t compile)

But, is it commonly used?

module type Sig = sig 
  val a : int 
  module Nested : sig 
    val b : int 
  end 
end
;;

module Mod : Sig = struct
  let a = 1
  module Nested = struct 
    let b = 2 
  end
end
;;

module Nested = Mod.Nested ;;

print_int Nested.b ;;

One can also open a module locally, so that all the names are visible only within a given let open, or within a given Module.( ... ) construct.

1 Like

Absolutely, opening nested modules is used.

Sometimes I create submodules specifically for convenient inclusion of parts. Like Vec3.Ops, which has infix operators working on the Vec3.t type. I generally avoid full open, prefering local opens for most code, as perry mentions. This way it’s immediately clear where most functions are from, even if there is mixed use. Vec3.ortho_proj v_desired (v /| dist) – here, the vector-division-by-scalar is from the opened Vec3.Ops, while I keep the path to ortho_proj explicit.

It’s also often convenient that the local open syntax works for any bracketed group, like lists or arrays, as well as general parenthesized blocks. One of my favorites is that you can scope function arguments Glt.Tx.(Named.rgba, load byte4 dim rgba) – here, the Glt (OpenGL tools) Tx (Texture) submodule has Named.rgba, load, and byte4 accessed from its scope (dim and rgba are local to the calling function). This keeps expression terse (ie. without prefixing everything with Glt.Tx), but still reasonably explicit, unlike opening several namespaces (where did that come from!?). This issue bugs me a lot when I deal with C++ code, where it would be nice to have class-local definitions/enums, but they impose repeated prefixing for each one.

I don’t know about commonly, but in our codebase we use it all the time, just like you’d have from foo import bar in Python. It is a bit unwieldy if you want to alias multiple things but oh well.

1 Like

This is something I was looking for today! I looked in the manual and tutorials etc. and was surprised that it wasn’t a feature. My original question on discord was:

In OCaml, is there a way to import only specific thing from a module, as opposed to opening the entire module? Kind of like in Rust:

use foo::{bar, baz};

Or Elm:

import Html exposing (HTML, text)

Hopefully if others search for similar things might now be able to find this discussion (it took me a bit to find personally).

Somebody also posted a ppx that implements something similar, which might be of use for some people (although not as nice as it being built-in): GitHub - johnyob/ppx-open: A ppx rewriter that provides idiomatic selective `open`s in OCaml.

It’s too heavy, but the following technically works:

open (struct open List let map = map and filter = filter end);;

(parentheses just “for clarity”)

2 Likes

I think someone mentioned earlier that this is not really idiomatic in OCaml, it’s more common practice to alias the module to a convenient short name and use items with their module prefix. But, it’s still fairly easy to do if you really need it:

let (filter, map) = List.(filter, map)

Or even just:

let filter = List.filter
let map = List.map
2 Likes

If you don’t mind writing out the types, there’s also

open (List : sig val hd : 'a list -> 'a end)

There are also ways of removing/overriding things from a module before importing, including submodules, but it’s syntactically a little heavy. See this excellent post by @gasche.

1 Like

ppx-open uses something like this internally. Namely:

[%%open: {| List.(map, filter) |}];;

is re-written to

open (struct let map = List.map let filter = List.filter end);;
2 Likes