How ocaml knows where this value is from?

Hello I’m playing with ocamllex. Here’s an ocamllex file

{
    type res = ENDOFFILE | OK
}

rule test = parse
    ['0' - '9']+
        { OK } |
    [' ' '\t']+
        { OK } |
    _
        {OK} |
    eof
        {ENDOFFILE}

And the main file

let scan inp:string =
    let lb = Lexing.from_string inp in
    let rec run (s:string) =
        match Lex.test lb with
            ENDOFFILE -> s ^ "\nend" |
            _ -> run (s ^ " \nfound something at " ^ string_of_int (Lexing.lexeme_start lb))
    in
    run "start\n"

let _ = print_endline (scan "42 24")

I ran ocamllex then I compiled the result with ocamlopt. The code works. I had to specify the module of the “test” function but not the ENDOFFILE value (the ugly name is choosen to be sure it is mine). Is it normal practise or some accident ?

1 Like

This is called type-directed disambiguation (see OCaml - The core language). In contexts where the compiler has enough type information, it can use that information to look up constructor and record label names in the right scope, even if they are not prefixed with the module path where they are defined.

It is convenient and of rather normal usage, I would say.

Cheers,
Nicolas

2 Likes

Incidentally, I found it surprising that the pattern match is not used to infer the correct type in this example on the linked manual page:

 let is_a_or_b x = match x with
    | A -> true (* OCaml infers [x: last_variant] *)
    | B -> true;;
Error: This variant pattern is expected to have type last_variant
       There is no constructor B within type last_variant

Is this more complicated / less well defined to do than using usages of record fields to disambiguate?

Type-directed disambiguation is always local: there are no difference between records and variant here:

type t = { x: int; y : int }
type last = { x : int; z : int }
let f = function
| { x = 0; _ } -> 0
| { y; _ } -> z

errors on the y field too:

Indeed. Even

type middle_record = { x:int; z:int }
type last_record   = { x:int }
let f r = r.x + r.z

cannot infer that r should be middle_record, which surprised me (at least in utop). Naively, one would think that this should be simple to do. But I wouldn’t be surprised to learn that it cannot be made to work in a reasonably general way.

Principled and principal type-directed disambiguation is still an open research question as far as I know.