Styles in defining functions

I noticed a style of defining top functions without its arguments, as in:

let f =
  let aux1 = ... in
  let aux2 ... = ... in
  (* now really define the function *)
  let f x y z = ... in
  f 

This way aux values are defined within the local scope but evaluated once. I wonder if there is any performance advantage writing in this style when compared to the more “conventional”(?) style:

let f x y z =
  let aux1 = ... in
  let aux2 ... = ... in
  ...
2 Likes

There could be, depends on if aux1/aux2 are expensive to calculate. Some people just don’t like doing extra work in functions if it can be avoided especially if the function is in a hot path. Some functions are called only once so it doesn’t really matter.

It can also be useful for controlling and scoping side-effects. Consider this id generation function, for example:

let generate_id =
  let last_id = ref 0 in
  fun () -> 
    incr last_id; 
    !last_id
4 Likes

As an example, this style is often used with the re library. There’s a Re.t type which describes regexps (a sort of AST I suppose) and a Re.re type which is for compiled regexps (a sort of DFA maybe). There’s a function Re.compile to turn Re.t into Re.re, and the rest of the library expects Re.re values. Since Re.compile can be expensive, this patterns allows the compiled regexp to have local scope but a global lifetime:

let parse_line =
  let re = Re.compile (Re.seq [Re.alt [...]]) in
  fun line ->
  match Re.exec re line with
  ...
4 Likes