I am trying to implement a generic timer function in OCaml which will take as input a function of arbitrary arity and return type 'r and returns a function with:
float * 'r
where the float would be a metric of the time spent in the function (e.g. reported by Sys.time()
)The problem is I can't implement it in such a way that it can handle functions of any arity. E.g. the following code:
let timer f = let timerf x y = let t0 = Sys.time () in let result = f x y in let diff = Sys.time() -. t0 in diff, result in timerf
works only with functions of input arity 2. It is not obvious to me how to generalize it to handle functions of any arity. I was hoping the partial function applications would somehow magically solve the conundrum but I can't get it to work.
I understand your intention of making a timer function with arbitrary arity. But you cannot do it in an easy way in OCaml.
Moreover, a timer function with only one param is enough for use in practice:
let timer f x =
let t0 = Sys.time()
in let result = f x
in let diff = Sys.time() -. t0
in diff, result
Since any function g
with any arity can be passed to timer
easily by:
let diff, result = timer (fun () -> g x1 x2 x3 ... xN) ()
or better by using partial application (as suggested by @Andreas):
let diff, result = timer (g x1 x2 x3 ... xN-1) xN
A remark on pad's solution that was too verbose to fit a comment.
In practice I found it was a better design to enforce f : unit -> 'a
by passing ()
instead of a delayed argument.
let timer f =
let t0 = Sys.time() in
let result = f () in
let t1 = Sys.time() in
t1 -. t0, result
The reason why is that I tend to use the following pattern quite often:
let fun_to_test = match some_configuration with ... in
timer fun_to_test
pad's design, which is more appealing at first because more general, encourages you to instead write:
let fun_to_test, arg = match some_configuration with .... in
timer fun_to_test arg
The problem with this choice is that it seems fine at first, and after adding a few options you encounter a case where the arguments to the different functions to test are not of the same type. And you have a type error. Example of wrong code:
let fun_to_test, arg =
if Random.bool ()
then (foo, 3)
else (bar, 3.2)
in timer fun_to_test arg
By forcing a closure with parameters pre-passed, I get "an existential type" for free here: the type of the last function argument does not appear in the type of timer
application. I found this to be better in practice.
Of course, you can also delay the full call and use ()
as argument in pad's design. But I prefer a choice that forces me to do this, because otherwise I'm too tempted not to do it, and I pay it later.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With