generic timer high-order function in OCaml

Go To StackoverFlow.com

7

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:

  • the same arity and types of input parameters , and
  • return type 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.

2012-04-04 21:39
by Marcus Junius Brutus


9

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
2012-04-04 22:01
by pad
If you know that the function doesn't do anything interesting when partially applied (like most functions) then you don't even need the abstraction. It is sufficient to call timer (g x1 x2 ... xN-1) xN - Andreas Rossberg 2012-04-05 14:54
@AndreasRossberg: Thanks, I incorporated your point into my answer - pad 2012-04-05 16:07


7

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.

2012-04-05 13:21
by gasche
my experience seconds thi - ygrek 2012-04-05 14:58
Ads