Linear types in OCaml - ocaml

Rust has a linear type system. Is there any (good) way to simulate this in OCaml? E.g., when using ocaml-lua, I want to make sure some functions are called only when Lua is in a specific state (table on top of stack, etc).
Edit: Here's a recent paper about resource polymorphism relevant to the question: https://arxiv.org/abs/1803.02796
Edit 2: There are also a number of articles about session types in OCaml available, including syntax extensions to provide some syntactic sugar.

As suggested by John Rivers, you can use a monadic style to represent
"effectful" computation in a way that hides the linear constraint in
the effect API. Below is one example where a type ('a, 'st) t is
used to represent computation using a file handle (whose identity is
implicit/unspoken to guarantee that it cannot be duplicated), will
product a result of type 'a and leave the file handle in the state
'st (a phantom type being either "open" or "close"). You have to use
the run of the monad¹ to actually do anything, and its type ensure
that the file handles are correctly closed after use.
module File : sig
type ('a, 'st) t
type open_st = Open
type close_st = Close
val bind : ('a, 's1) t -> ('a -> ('b, 's2) t) -> ('b, 's2) t
val open_ : string -> (unit, open_st) t
val read : (string, open_st) t
val close : (unit, close_st) t
val run : ('a, close_st) t -> 'a
end = struct
type ('a, 'st) t = unit -> 'a
type open_st = Open
type close_st = Close
let run m = m ()
let bind m f = fun () ->
let x = run m in
run (f x)
let close = fun () ->
print_endline "[lib] close"
let read = fun () ->
let result = "toto" in
print_endline ("[lib] read " ^ result);
result
let open_ path = fun () ->
print_endline ("[lib] open " ^ path)
end
let test =
let open File in
let (>>=) = bind in
run begin
open_ "/tmp/foo" >>= fun () ->
read >>= fun content ->
print_endline ("[user] read " ^ content);
close
end
(* starting with OCaml 4.13, you can use binding operators:
( let* ) instead of ( >>= ) *)
let test =
let open File in
let ( let* ) = bind in
run begin
let* () = open_ "/tmp/foo" in
let* content = read in
print_endline ("[user] read " ^ content);
close
end
Of course, this is only meant to give you a taste of the style of
API. For more serious uses, see Oleg's monadic
regions examples.
You may also be interested in the research programming language
Mezzo, which aims to
be a variant of ML with finer-grained control of state (and related
effectful patterns) through a linear typing discipline with separated
resources. Note that it is only a research experiment for now, not
actually aimed at users. ATS is also relevant,
though finally less ML-like. Rust may actually be a reasonable
"practical" counterpart to these experiments.
¹: it is actually not a monad because it has no return/unit combinator, but the point is to force type-controlled sequencing as the monadic bind operator does. It could have a map, though.

Related

Can type variables be used when writing a type signature for a polymorphic variant type?

I would like to constrain a type variable to allow only polymorphic variant types, such that I could use the variable to construct other polymorphic variant types in a signature:
type 'a t
val f : 'a t -> [`Tag | 'a] t
Is there a way to accomplish this in OCaml? Perhaps using classes/objects instead? A naive attempt failed to compile:
type 'a t = { dummy: int } constraint 'a = [>]
let f : 'a t -> ['a | `Tag] t = fun _ -> { dummy = 0 }
^^
The type [> ] does not expand to a polymorphic variant type
Reason for the question:
I want to use the type signature to reflect capabilities of a t statically, to enforce that a t without a given capability can never be used inappropriately.
val do_something_cool : [<`Super_power] t -> unit
val do_something_else : [<`Super_power|`Extra_super_power] t -> unit
val enhance : 'a t -> ['a | `Super_power] t
val plain_t : [`Empty] t
let () = plain_t |> do_something_cool (* fails *)
let () = plain_t |> enhance |> do_something_cool (* succeeds *)
let () = plain_t |> enhance |> do_something_else (* succeeds *)
Obviously there are other ways to achieve this compile-time safety. For example, enhance could just return a [`Super_power] t that could be used in place of plain_t where required. However, I'm really curious whether the first way could succeed. I am writing a DSL which would be a lot more concise if all the capabilities of t could be reflected in its type.
The short answer is no: it is only possible to inline type declarations, not type variables. In other words, this is fine:
type on = [`On]
type off = [`Off]
type any = [ on | off ]
let f: [< any ] -> _ = fun _ -> ()
but not this
let merge: 'a -> 'b -> [ 'a | 'b ] = ...
However, if you only have a closed set of independent capabilities, it might work to switch to an object phantom type where each capacity correspond to a field and each field can be either on or off. For instance,
type +'a t constraint 'a = < super: [< any ]; extra: [< any ]>
Then consumer functions that only require a conjunction of capabilities are relatively easy to write:
val do_something_cool : < super:on; ..> t -> unit
val do_something_extra : < extra:on; ..> t -> unit
val do_something_super_but_not_extra: <super:on; extra:off; .. > t -> unit
but switching a capability on or off is more complex and fixes the set of capabilities:
val enhance : < super: _; extra: 'es > t -> < super: on; extra:'es > t
Beyond those limitations, everything works as expected. For instance, if I have a variable x
val x: <super: off; extra:on > t
This works:
let () = do_something_extra x
whereas
let () = do_something_cool x
fails and finally
let () =
let x = enhance x in
do_something_cool x; do_something_extra x
works fine too.
The main issue is thus writing the enable function. One trick that may help is to
write helper type to manipulate more easily a subset of capabilities.
For instance, if I have a complex type:
type 'a s
constraint 'a = < a: [< any]; b:[< any]; c: [< any ]; d: [< any] >
I can use the following type:
type ('a, 'others) a = < a:'a; b:'b; c:'c; d: 'd>
constraint 'others = 'b * 'c * 'd
to select the capability a, and thus write
val enable_a: (_,'rest) a s -> (on, 'rest) a s
without having to explicit the three type variables hidden in 'rest.

OCaml Hashtbl/0.t and Hashtbl/-1.t

I am quite new to OCaml, so I am not sure what the following error message means (specifically the /0 and the /-1):
Error: This expression has type (string, string) Hashtbl/0.t
but an expression was expected of type ('a, 'b) Hashtbl/-1.t
I am passing a Hashtbl.t into Hashtbl.find and this error shows up. I am unclear as to how the /0 and /-1 came in, and what they actually mean.
Here's a minimal working example to demonstrate my issue:
open Core_kernel.Std
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str))
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
Let's show you an example :
If I write
type t = A;;
let x = A;;
type t = B;;
let y = B;;
x = y;;
Error: This expression has type t/1561 but an expression was expected of type
t/1558
This is because in the interpreter you can declare multiple types with the same name and associate values to these types. But here, as you can see, x and y are not of the same type but both the types are named t so the interpreter tries to tell you the types are both named t but are not the same.
[Compilation]
If I wanted to compile this, I would have to declare
typea.ml
type t = A
let x = A
typeb.ml
type t = B
let y = B
main.ml
open Typea
open Typeb
x = y
If I compile this I will have
Error: This expression has type Typeb.t
but an expression was expected of type Typea.t
What lesson should you learn from this ? Stop interpreting, compile !
Now that I managed to compile your file, I got an error too but much more explicit :
Error: This expression has type (string, string) Hashtbl.t
but an expression was expected of type
('a, 'b) Core_kernel.Std.Hashtbl.t =
('a, 'b) Core_kernel.Core_hashtbl.t
[Explanation and correction]
Since I'm too nice, here is your file corrected :
let file_to_hashtbl filename =
(* open the namespace only where needed *)
let open Core_kernel.Std in
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val =
try
Hashtbl.find ht "t1"
with Not_found -> assert false
let () = print_endline t1_val
Your error was that you opened Core_kernel.Std as a global namespace so when you wrote Hashtbl.find it looked first in Core_kernel.Std and not in the standard library.
What I did is open Core_kernel.Std in the function that needs it, not in the whole file (so it's a local namespace) (a good habit to take).
So, as you can see, the problem was that you had two definition of the type Hashtbl.t (one in Core_kernel.Std and one in the standard library) and OCaml ain't no fool, boy, he knows when you're wrong but he is hard to understand since he only speak for those who can hear. :-D
P.S. : You had an error in your Hashtbl.find because it doesn't return an option but the found value or raise a Not_found exception if no value was found. I corrected it too. ;-)
Apparently, it is just a matter of missing semi-columns, the foloowing code compiles :
open Core_kernel.Std;;
let file_to_hashtbl filename =
let sexp_to_hashtbl_str = Sexplib.Conv.hashtbl_of_sexp
string_of_sexp string_of_sexp
in In_channel.with_file
filename ~f:(fun ch -> (Sexp.input_sexp ch |> sexp_to_hashtbl_str));;
let ht = file_to_hashtbl "test"
let t1_val = match Hashtbl.find ht "t1" with
| Some v -> v
| None -> assert false
let () = print_endline t1_val
But, I do not know how to interpret the error message neither.

Frama-C plugin development: Extract value analysis result as OCaml integers

I have read these two posts:
Getting result of value analysis
and Getting the values of statement. These two posts provide invaluable information on how to print the values of the value analysis.
However, my task requires me to extract the integers stored in the value variable, and then do some math with the integers (I am only concerned with integer values). For example, if the value analysis result for some variable is {1, 2}, I want to get the result as an OCaml list of integers: [1, 2]. This way I can do math with it. If the result involves an interval, I assume I can define a type to handle it. For example,
type point_or_interval =
| Point of int
| Interval of int * int
The type of the value variable is defined as type t = Cvalue.V.t in the documentation. I have not been able to find this module in the source, so I do not know how to manipulate the value and extract the information that I need. How should I do this? A code illustration will be appreciated!
Edit:
I have tried the following code. This code is copied verbatim from Getting result of value analysis, with only some modifications in the pretty_vi function. It is not working with my test input program - Locations.Location_Bytes.find_lonely_key function raises Not_found exception. My input program is also attached.
open Cil_types
(* Prints the value associated to variable [vi] before [stmt]. *)
let pretty_vi fmt stmt vi =
let kinstr = Kstmt stmt in (* make a kinstr from a stmt *)
let lval = (Var vi, NoOffset) in (* make an lval from a varinfo *)
let loc = (* make a location from a kinstr + an lval *)
!Db.Value.lval_to_loc kinstr ~with_alarms:CilE.warn_none_mode lval
in
Db.Value.fold_state_callstack
(fun state () ->
(* for each state in the callstack *)
let value = Db.Value.find state loc in (* obtain value for location *)
let base, offset = Locations.Location_Bytes.find_lonely_key value in
(match offset with
| Ival.Set _ -> ()
| Ival.Float _ -> ()
| Ival.Top (_, _, _, _ )-> ());
Format.fprintf fmt "%a -> %a#." Printer.pp_varinfo vi
Db.Value.pretty value (* print mapping *)
) () ~after:false kinstr
(* Prints the state at statement [stmt] for each local variable in [kf],
and for each global variable. *)
let pretty_local_and_global_vars kf fmt stmt =
let locals = Kernel_function.get_locals kf in
List.iter (fun vi -> pretty_vi fmt stmt vi) locals (*;
Globals.Vars.iter (fun vi _ -> pretty_vi fmt stmt vi) *)
(* Visits each statement in [kf] and prints the result of Value before the
statement. *)
class stmt_val_visitor kf =
object (self)
inherit Visitor.frama_c_inplace
method! vstmt_aux stmt =
(match stmt.skind with
| Instr _ ->
Format.printf "state for all variables before stmt: %a#.%a#."
Printer.pp_stmt stmt (pretty_local_and_global_vars kf) stmt
| _ -> ());
Cil.DoChildren
end
(* usage: frama-c file.c -load-script print_vals.ml *)
let () =
Db.Main.extend (fun () ->
Format.printf "computing value...#.";
!Db.Value.compute ();
let fun_name = "main" in
Format.printf "visiting function: %s#." fun_name;
let kf_vis = new stmt_val_visitor in
let kf = Globals.Functions.find_by_name fun_name in
let fundec = Kernel_function.get_definition kf in
ignore (Visitor.visitFramacFunction (kf_vis kf) fundec);
Format.printf "done!#.")
Test input program:
#include <stdio.h>
int main() {
int a = 1;
return 0;
}
What is the problem with this code? Why is the mapping of the value not found?
General remark: if you are using an editor which supports Merlin, I seriously recommend using it. It makes it easier to find in which module things are defined, which types are synonyms, and, combined with an auto-completion tool, Merlin allows you to find conversion functions much more easily.
In particular, Merlin should help you find out that Cvalue.V.project_ival : V.t -> Ival.t converts a V.t into an Ival.t (assuming the value is convertible, e.g. it is not a pointer).
Ival.t is a sophisticated interval-like value that can represent:
a contiguous interval of floating-point values (Ival.Float);
a small set of integer values (Ival.Set);
or an actual integer interval (Ival.Top, despite the name), with congruence information and optional bounds, e.g. [9..--]1%4 represents {x ∈ ℕ | x ≥ 9 ∧ x mod 4 = 1}.
Function Ival.min_and_max : Ival.t -> Integer.t option * Integer.t option takes an Ival.t and returns (assuming the interval does not contain a floating-point interval) a pair (maybe_min, maybe_max), where maybe_min is None if there is no lower bound (minus infinity), or Some min otherwise, and symmetrically for maybe max. It works both with Ival.Set and Ival.Top.
Note that Integer.t are not machine integers, but an implementation of arbitrary-precision integers.

Efficient input in OCaml

Suppose I am writing an OCaml program and my input will be a large stream of integers separated by spaces i.e.
let string = input_line stdin;;
will return a string which looks like e.g. "2 4 34 765 5 ..." Now, the program itself will take a further two values i and j which specify a small subsequence of this input on which the main procedure will take place (let's say that the main procedure is the find the maximum of this sublist). In other words, the whole stream will be inputted into the program but the program will only end up acting on a small subset of the input.
My question is: what is the best way to translate the relevant part of the input stream into something usable i.e. a string of ints? One option would be to convert the whole input string into a list of ints using
let list = List.map int_of_string(Str.split (Str.regexp_string " ") string;;
and then once the bounds i and j have been entered one easily locates the relevant sublist and its maximum. The problem is that the initial pre-processing of the large stream is immensely time-consuming.
Is there an efficient way of locating the small sublist directly from the large stream i.e. processing the input along with the main procedure?
OCaml's standard library is rather small. It provides necessary and sufficient set of orthogonal features, as should do any good standard library. But, usually, this is not enough for a casual user. That's why there exist libraries, that do the stuff, that is rather common.
I would like to mention two the most prominent libraries: Jane Street's Core library and Batteries included (aka Core and Batteries).
Both libraries provides a bunch of high-level I/O functions, but there exists a little problem. It is not possible or even reasonable to try to address any use case in a library. Otherwise the library's interface wont be terse and comprehensible. And your case is non-standard. There is a convention, a tacit agreement between data engineers, to represent a set of things with a set of lines in a file. And to represent one "thing" (or a feature) with a line. So, if you have a dataset where each element is a scalar, you should represent it as a sequence of scalars separated by a newline. Several elements on a single line is only for multidimensional features.
So, with a proper representation, your problem can be solve as simple as (with Core):
open Core.Std
let () =
let filename = "data" in
let max_number =
let open In_channel in
with_file filename
~f:(fold_lines ~init:0
~f:(fun m s -> Int.(max m ## of_string s))) in
printf "Max number is %s is %d\n" filename max_number
You can compile and run this program with corebuild test.byte -- assuming that code is in a file name test.byte and core library is installed (with opam install core if you're using opam).
Also, there exists an excellent library Lwt, that provides a monadic high-level interface to the I/O. With this library, you can parse a set of scalars in a following way:
open Lwt
let program =
let filename = "data" in
let lines = Lwt_io.lines_of_file filename in
Lwt_stream.fold (fun s m -> max m ## int_of_string s) lines 0 >>=
Lwt_io.printf "Max number is %s is %d\n" filename
let () = Lwt_main.run program
This program can be compiled and run with ocamlbuild -package lwt.unix test.byte --, if lwt library is installed on your system (opam install lwt).
So, that is not to say, that your problem cannot be solved (or is hard to be solved) in OCaml, it is just to mention, that you should start with a proper representation. But, suppose, you do not own the representation, and cannot change it. Let's look, how this can be solved efficiently with OCaml. As previous examples represent, in general your problem can be described as a channel folding, i.e. an consequential application of a function f to each value in a file. So, we can define a function fold_channel, that will read an integer value from a channel and apply a function to it and the previously read value. Of course, this function can be further abstracted, by lifting the format argument, but for the demonstration purpose, I suppose, this will be enough.
let rec fold_channel f init ic =
try Scanf.fscanf ic "%u " (fun s -> fold_channel f (f s init) ic)
with End_of_file -> init
let () =
let max_value = open_in "atad" |> fold_channel max 0 in
Printf.printf "max value is %u\n" max_value
Although, I should note that this implementation is not for a heavy duty work. It is even not tail-recursive. If you need really efficient lexer, you can use ocaml's lexer generator, for example.
Update 1
Since there is a word "efficient" in the title, and everybody likes benchmarks, I've decided to compare this three implementations. Of course, since pure OCaml implementation is not tail-recursive it is not comparable to others. You may wonder, why it is not tail-recursive, as all calls to fold_channel is in a tail position. The problem is with exception handler - on each call to the fold channel, we need to remember the init value, since we're going to return it. This is a common issue with recursion and exceptions, you may google it for more examples and explanations.
So, at first we need to fix the third implementation. We will use a common trick with option value.
let id x = x
let read_int ic =
try Some (Scanf.fscanf ic "%u " id) with End_of_file -> None
let rec fold_channel f init ic =
match read_int ic with
| Some s -> fold_channel f (f s init) ic
| None -> init
let () =
let max_value = open_in "atad" |> fold_channel max 0 in
Printf.printf "max value is %u\n" max_value
So, with a new tail-recursive implementation, let's try them all on a big-data. 100_000_000 numbers is a big data for my 7 years old laptop. I've also added a C implementations as a baseline, and an OCaml clone of the C implementation:
let () =
let m = ref 0 in
try
let ic = open_in "atad" in
while true do
let n = Scanf.fscanf ic "%d " (fun x -> x) in
m := max n !m;
done
with End_of_file ->
Printf.printf "max value is %u\n" !m;
close_in ic
Update 2
Yet another implementation, that uses ocamllex. It consists of two files, a lexer specification lex_int.mll
{}
let digit = ['0'-'9']
let space = [' ' '\t' '\n']*
rule next = parse
| eof {None}
| space {next lexbuf}
| digit+ as n {Some (int_of_string n)}
{}
And the implementation:
let rec fold_channel f init buf =
match Lex_int.next buf with
| Some s -> fold_channel f (f s init) buf
| None -> init
let () =
let max_value = open_in "atad" |>
Lexing.from_channel |>
fold_channel max 0 in
Printf.printf "max value is %u\n" max_value
And here are the results:
implementation time ratio rate (MB/s)
plain C 22 s 1.0 12.5
ocamllex 33 s 1.5 8.4
Core 62 s 2.8 4.5
C-like OCaml 83 s 3.7 3.3
fold_channel 84 s 3.8 3.3
Lwt 143 s 6.5 1.9
P.S. You can see, that in this particular case Lwt is an outlier. This doesn't mean that Lwt is slow, it is just not its granularity. And I would like to assure you, that to my experience Lwt is a well suited tool for a HPC. For example, in one of my programs it processes a 30 MB/s network stream in a real-time.
Update 3
By the way, I've tried to address the problem in an abstract way, and I didn't provide a solution for your particular example (with j and k). Since, folding is a generalization of the iteration, it can be easily solved by extending the state (parameter init) to hold a counter and check whether it is contained in a range, that was specified by a user. But, this leads to an interesting consequence: what to do, when you have outran the range? Of course, you can continue to the end, just ignoring the output. Or you can non-locally exit from a function with an exception, something like raise (Done m). Core library provides such facility with a with_return function, that allows you to break out of your computation at any point.
open Core.Std
let () =
let filename = "data" in
let b1,b2 = Int.(of_string Sys.argv.(1), of_string Sys.argv.(2)) in
let range = Interval.Int.create b1 b2 in
let _,max_number =
let open In_channel in
with_return begin fun call ->
with_file filename
~f:(fold_lines ~init:(0,0)
~f:(fun (i,m) s ->
match Interval.Int.compare_value range i with
| `Below -> i+1,m
| `Within -> i+1, Int.(max m ## of_string s)
| `Above -> call.return (i,m)
| `Interval_is_empty -> failwith "empty interval"))
end in
printf "Max number is %s is %d\n" filename max_number
You may use the Scanf module family of functions. For instance, Scanf.fscanf let you read tokens from a channel according to a string format (which is a special type in OCaml).
Your program can be decomposed in two functions:
one which skip a number i of tokens from the input channel,
one which extract the maximum integer out of a number j from a channel
Let's write these:
let rec skip_tokens c i =
match i with
| i when i > 0 -> Scanf.fscanf c "%s " (fun _ -> skip_tokens c ## pred i)
| _ -> ()
let rec get_max c j m =
match j with
| j when j > 0 -> Scanf.fscanf c "%d " (fun x -> max m x |> get_max c (pred j))
| _ -> m
Note the space after the token format indicator in the string which tells the scanner to also swallow all the spaces and carriage returns in between tokens.
All you need to do now is to combine them. Here's a small program you can run from the CLI which takes the i and j parameters, expects a stream of tokens, and print out the maximum value as wanted:
let _ =
let i = int_of_string Sys.argv.(1)
and j = int_of_string Sys.argv.(2) in
skip_tokens stdin (pred i);
get_max stdin j min_int |> print_int;
print_newline ()
You could probably write more flexible combinators by extracting the recursive part out. I'll leave this as an exercise for the reader.

Simple timing profiler for functional languages

I needed a simple timing profiler to estimate the runtime of some parts of my program (written in OCaml, but I believe this could apply to other functional languages), and I couldn't find a very simple solution, similar to what one would code in an imperative language, using functions such as timer.start/timer.stop. So I tried one using lazy evaluation, and it works quite well for what I need, however I didn't find any references to this method, so I wonder it the approach is flawed or if there is a simpler solution.
So, the question is: do you know about similar implementations for functional languages (especially OCaml)? If so, please indicate them to me, I'd like to borrow some of their ideas to improve my "poorer man's profiler" (I've seen this question but it didn't help me much). From what I've seen, GHC already has a way to collect timing information, so it's probably not an issue for Haskell.
By the way, I tried doing timing profiling as indicated in the OCaml manual (17.4), but it was too "low-level" for what I needed: it gives lots of information at the C function level, which make it harder to evaluate precisely which part of the OCaml code is the culprit.
Below follows my implementation in OCaml (note that I need to add the "lazy" expression everytime I want to measure the time, but at the same time I can finely control how much information I need).
open Unix (* for the timers *)
(** 'timers' associates keys (strings) to time counters,
to allow for multiple simultaneous measurements. *)
let timers : (string, (float * float)) Hashtbl.t = Hashtbl.create 1
(** starts the timer associated with key <name> *)
let timer_start (name : string) : unit =
let now = Unix.times () in
Hashtbl.replace timers name (now.tms_utime, now.tms_stime)
(** Returns time elapsed between the corresponding call to
timer_start and this call *)
let timer_stop (name : string) : float =
try
let now = Unix.times () in
let t = Hashtbl.find timers name in
(now.tms_utime -. fst t) +. (now.tms_stime -. snd t)
with
Not_found -> 0.0
(** Wrapper for the timer function using lazy evaluation *)
let time (s : string) (e : 'a Lazy.t) : 'a =
timer_start s;
let a = Lazy.force e in
let t2 = timer_stop s in
(* outputs timing information *)
Printf.printf "TIMER,%s,%f\n" s t2; a
(** Example *)
let rec fibo n =
match n with
| 0 -> 1
| 1 -> 1
| n' -> fibo (n - 1) + fibo (n - 2)
let main =
let f = time "fibo" (lazy (fibo 42)) in
Printf.printf "f = %d\n" f
Unix.times measures CPU time, not wall-clock time. So this is suitable only for computational code that spends all of its time in CPU. And BTW hashtbl is not needed, even for multiple simultaneous measurements, just return the start time in timer_start and substract it in timer_stop.
Merging the ideas from #Jeffrey_Scofield and #ygrek, the "poorest man's timing profiler" is indeed so simple it would barely require mention at all, which would explain why I hadn't found it. So I've merged their answers and produced a much simpler version:
open Unix (* for the timers *)
(* Wrapper for the timer function using a "unit -> 'a" thunk *)
let time (s : string) (e : unit -> 'a) : 'a =
let tstart = Unix.times () in
let a = e () in
let tend = Unix.times () in
let delta = (tend.tms_utime -. tstart.tms_utime) +.
(tend.tms_stime -. tstart.tms_stime) in
(* outputs timing information *)
Printf.printf "TIMER,%s,%f\n" s delta; a
(* Example *)
let rec fibo n =
match n with
| 0 -> 1
| 1 -> 1
| n' -> fibo (n - 1) + fibo (n - 2)
let main =
let f = time "fibo" (fun () -> fibo 42) in
Printf.printf "f = %d\n" f