What does it mean to use a variable/argument/parameter using a dot with a module?

let test lexbuf =
  let pos = lexbuf.Lexing.lex_curr_p in

This doesn’t quite make sense to me,

What is lexbuf.Lexing.lex_curr_p? Why is there Lexing in between the dots/periods if it is a module? I thought periods/dots are used for like records.

Record field names are namespaced by module, so this is telling the compiler to access the lex_curr_p field of the lexbuf record value, as defined in the Lexing module. This tells the compiler where to find the record type definition and thus the correct type of the expression.

More details in Records - Real World OCaml

the syntax x.Foo.y means accessing record field Foo.y of value x.
It’s a qualified field name :-). Here, the field name is
Lexing.lex_curr_p.

In “recent” versions of OCaml, we can use records with constructors, like this:

type alfa = Alfa of { field1: string; field2: int }

…which you normally reference by pattern matching…

let Alfa r = ... in r.field1 r.field2

…where the compiler prevents leaking the variable r from its scope because it doesn’t have a type that can be universally quantified.

Older versions of OCaml did not allow constructors for records, and this older form is used in your example, when the names of the fields in every record of a module are all in the module namespace. Hence, in the standard Lexing module all the fields in the lexbuf record type have names that begin with lex_ to disambiguate them from the fields in the other record types, e.g. the position type.

Accordingly, the way you read the expression buf.Lexing.lex_curr_p is that buf is a variable with the type of the record defined in the Lexing module with a field named lex_curr_p.

If this syntax feels weird and unwieldy, then you’re not alone. I try not to use it anymore, because the newer record types with constructors have fields with their own namespace, which feels to me a lot more like other programming languages. However, the standard Lexing module is from an earlier age, when we still had a Republic and the Jedi still policed the galaxy.

I actually find namespacing record fields by modules quite elegant and very consistent with the rest of the language, where almost everything else (including variant constructors) is namespaced by module.

I’m not sure when the module-qualified record field access was introduced, but I don’t think using or not using it has anything to do with the introduction of nested record types.

Since OCaml 4.01, you may also use type-based disambiguation if you prefer not to use qualified field names: the field lex_curr_p is available as soon as the value is known to be of type Lexing.lexbuf, and you may add a type annotation to ensure that.

let test (lexbuf : Lexing.lexbuf) =
  let pos = lexbuf.lex_curr_p in
4 Likes

There are good replies here. I just want to add a note that it might help to think of this as a “path” – a way to uniquely specify a field, which can involve specifying the module where the record (and field) are defined.

1 Like