syntaxocaml

What does the `and` keyword mean in OCaml?


I'm mystified by the and keyword in OCaml. Looking through this code, I see

type env = {
    (* fields for a local environment described here *)
}

and genv {
    (* fields for a global environment here *)
}

then later,

let rec debug stack env (r, ty) = (* a function definition *)

and debugl stack env x = (* another function definition *)

What's going on here? Does the and keyword just copy the last type, let, or let rec statement? Is there such thing as an and rec statement? Why would I want to use and instead of just typing let or type, making my code less brittle to refactoring? Is there anything else I should know about?


Solution

  • The and keyword is used either to avoid multiple let (first example, I never use it for this but why not) or for mutually recursive definitions of types, functions, modules...

    As you can see in your second example :

    let rec debug stack env (r, ty) =
       ...
       | Tunresolved tyl -> o "intersect("; debugl stack env tyl; o ")"
       ...
     
     and debugl stack env x =
       ...
       | [x] -> debug stack env x
       ...
    

    debug calls debugl and vice versa. So the and is allowing that.

    [EDIT] It bothered me not to give a proper example so here is one example that you'll often see :

    let rec is_even x =
      if x = 0 then true else is_odd (x - 1)
    and is_odd x =
      if x = 0 then false else is_even (x - 1)
    
    (* second version *)
    
    let rec is_even x =
      x = 0 || is_odd (x - 1)
    and is_odd x =
      x <> 0 && is_even (x - 1)
    

    (You can find this example here)

    For mutually recursive types, it's harder to find a configuration but following this wikipedia page we would define trees and forests as follow

     type 'a tree = Empty | Node of 'a * 'a forest
     and 'a forest = Nil | Cons of 'a tree * 'a forest
    

    As an example, a forest composed of the empty tree, the singleton tree labeled a and a two nodes tree with labels b and c would then be represented as :

     let f1 = Cons (Empty, (* Empty tree *)
                 Cons (Node ('a',  (* Singleton tree *)
                             Nil), (* End of the second tree *)
                       Cons (Node ('b', (* Tree composed by 'b'... *)
                                   Cons (Node ('c', (* and 'c' *)
                                               Nil), 
                                         Nil)
                               ),
                             Nil (* End of the third tree *)
                         )
                   )
             );;
      
    

    And the size function (counting the number of nodes in the forest) would be :

    let rec size_tree = function
      | Empty -> 0
      | Node (_, f) -> 1 + size_forest f
    and size_forest = function
      | Nil -> 0
      | Cons (t, f) -> size_tree t + size_forest f
    

    And we get

    # size_forest f1;;
    - : int = 3