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

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

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; 

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 type which is for compiled regexps (a sort of DFA maybe). There’s a function Re.compile to turn Re.t into, and the rest of the library expects 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