Top
This has been processed with:
Hiding administrative data
(code in tests/expr_loc.ml, run with
./ocamlorn tests/stdlib.mli tests/expr_loc.ml)
Let us start with the code for a simple interpreter:
|   type 'a option =
    | Some of 'a
    | None
  
  type expr =
    | Abs of (expr -> expr)
    | App of expr * expr
    | Const of int | 
|   let rec eval e = match e with
    | App (u, v) ->
        begin match eval u with Some (Abs f) -> eval (f v) | _ -> None end
    | _ -> Some(e) | 
We want to add a more precise error reporting, pointing out the location of
an error. We need a type extended with locations, and a result
type to return either a value or the location of the error. 
|   type location = Location of string * int * int
  type expr_loc =
    | Abs' of (expr_loc -> expr_loc) * location
    | App' of expr_loc * expr_loc * location
    | Const' of int * location
  
  type ('a,'err) result =
    | Ok of 'a
    | Error of 'err | 
The type expr_loc is an ornament of expr, and
result of option 
|   type ornament add_loc : expr => expr_loc with
    | Abs f => Abs' (f, _) when f : add_loc -> add_loc
    | App (u, v) => App' (u, v, _) when u v : add_loc
    | Const i => Const' (i, _)
  
  type ornament ('a, 'err) optres : 'a option => ('a, 'err) result with
    | Some a => Ok a
    | None => Error _ | 
We ask for an incomplete lifting:
|   let _eval = lifting eval : add_loc -> (add_loc, location) optres | 
The resulting term has one hole at the point where we have to generate an error.
|   let rec _eval e = match e with
      | (Abs'(_, _) | Const'(_, _)) -> Ok e
      | App'(u, v, _) ->
        begin match _eval u with
          | Ok (Abs'(f, _)) -> _eval (f v)
          | ((Ok (App'(_, _, _)) | Ok (Const'(_, _)))
            | Error _) -> Error #4
        end | 
We re-match on the result of the preivous pattern matchin to check in which of these cases we are.
The result has been automatically named _2: since it is the result of an expression, we
do not want to compute it a second time.
In the case of an error, we propagate the error, while in other
cases we need to return the location of the application.
|   let eval_loc =  lifting eval : add_loc -> (add_loc, location) optres with
    | #4 <-
           begin match _2 with
           | Ok _ -> (match e with App'(_, _, loc) -> loc)
           | Error x -> x
           end | 
We obtain an evaluator that propagates locations. Note that the patterns that were previously grouped
in the error case have been separated:
|   let rec eval_loc e = match e with
      | (Abs'(_, _) | Const'(_, _)) -> Ok e
      | App'(u, v, loc) ->
        begin match eval_loc u with
          | Ok (Abs'(f, _)) -> eval_loc (f v)
          | (Ok (App'(_, _, _)) | Ok (Const'(_, _))) ->
            Error loc
          | Error x -> Error x
        end | 
Alternative representation of locations
Remark that instead of attaching locations directly to the constructor, one
would rather systematically lift expressions to a pair of expressions
and a location:
|   type expr_loc = expr' * location
  and expr' =
    | Abs' of (expr_loc -> expr_loc)
    | App' of expr_loc * expr_loc
    | Const' of int | 
However, our prototype does not implement type abbraviation yet. Hence to
fit with these syntactic restriction, we may equivalently defined:
|   type expr' =
    | Abs' of ((expr' * location) -> (expr' * location))
    | App' of (expr' * location) * (expr' * location)
    | Const' of int | 
and use expr' loc instead of expr_loc:
|   type ornament add_loc : expr => expr' * location with
    | Abs f => (Abs' f, _) when f : add_loc -> add_loc
    | App (u, v) => (App' (u, v), _) when u v : add_loc
    | Const (i) => (Const' (i), _)
  
  let _eval = lifting eval : add_loc -> (add_loc, location) optres | 
We get the following program:
|   let rec _eval e = match e with
      | ((Abs' _, _) | (Const' _, _)) -> Ok e
      | (App'(u, v), _) ->
        begin match _eval u with
          | Ok (Abs' f, _) -> _eval (f v)
          | ((Ok (App'(_, _), _) | Ok (Const' _, _)) | Error _) ->
            Error #4
        end | 
Which we can patch similarly:
|   let eval_loc =  lifting eval : add_loc -> (add_loc, location) optres with
    | #4 <-
           begin match _2 with
           | Ok _ -> (match e with (_,loc) -> loc)
           | Error x -> x
           end | 
|   let rec eval_loc e = match e with
      | ((Abs' _, _) | (Const' _, _)) -> Ok e
      | (App'(u, v), loc) ->
        begin match eval_loc u with
          | Ok (Abs' f, _) -> eval_loc (f v)
          | (Ok (App'(_, _), _) | Ok (Const' _, _)) ->
            Error loc
          | Error x -> Error x
        end |