There’s been this idea I’ve been floating in my mind for a while on improving the debugging experience for OCaml, although I haven’t yet got round to working out the details to see if it is even possible at all.
The idea is to improve debugging output by working out the concrete types for the variables in scope, to allow for pretty-printing, evaluation etc.
Of course, OCaml erases types at run-time, so a naieve approach to do this would require changing internals of the compiler.
The idea that I’ve been playing with is to somehow use the call-stack + source code to essentially recover the concrete type information for any variables in scope at runtime - the potential benefits of this, if it were possible, would be that it wouldn’t require major (if any at all) changes to the compiler, and could provide more detailed information that just looking at the type at compile time (because we’d be observing concrete types, not abstract ones).
For example, consider map for lists:
(* in List.ml at compile time *)
let rec map (f: 'a -> 'b) (ls: 'a list) = match ls with
| [] -> []
| (h: 'a) :: (t: 'a list) -> (f h: 'b) :: (map f t: 'b list)
If our main program were something like:
(* main.ml *)
let () =
let (vl: int list) = [1;2;3;4] in
let (vl': float list) = List.map (fun vl -> float_of_int vl) vl in
List.iter (fun (vl: float) ->
print_float vl
) vl
Now, if we put a breakpoint in list.ml
at lin 4, when the breakpoint is hit, our call-stack could abstractly be mapped back to our program as:
main.ml:4
list.ml:4
------------
breakpoint
So using this, could we work out that in our current call to map
, 'a
has been instantiated with int
and 'b
has been instantiated with float
:
let rec map (f: int -> float) (ls: int list) = match ls with
| [] -> []
| (h: int) :: (t: int list) -> (f h: float) :: (map f t: float list)
I haven’t put much more thought into this beyond the idea, so before I try out anything more involved, I thought I’d confer with the community to get their opinions.
What do you think? Is this possible? Has this direction been explored before? Any obvious counter-examples on which this would fail?