Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

generic timer high-order function in OCaml

Tags:

generics

ocaml

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.

like image 833
Marcus Junius Brutus Avatar asked Apr 04 '12 21:04

Marcus Junius Brutus


2 Answers

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
like image 71
pad Avatar answered Nov 01 '22 09:11

pad


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.

like image 32
gasche Avatar answered Nov 01 '22 08:11

gasche