[ANN] ppx_compose 0.0.3

This is the first release of ppx_compose, a ppx rewriter which turns compositions into abstractions and function calls. It is thus merely an optimisation (well, apart from value restriction issues). The transformation was suggested by Alain Frisch in https://github.com/ocaml/ocaml/pull/791, but blame is on me (or better: the issue tracker). The chosen operators are the same as is Batteries, and the ppx will shadow the libary-provided operators whenever the transformation apply.

As you can see from the PL which motivated this, there are mixed opinions on whether a composition operator is a good idea. So, preferably without taking it to the point of obscurity, those of us who use compose operators can now do it efficiently without the need for compiler support.

Do you think that there are performance advantages compared to simply defining them as binary operators? I would expect the compiler to be able to inline them rather effectively. (Does that depend on whether or not -flambda is used?)

There was previous work on inlining monadic bind operators for an exception-like monad (Catch me if you can, David Teller, Arnaud Spiwack and Till Varoquaux, 2008) which did report performance gains, but the operators were longer and thus less reliably inlined by the compiler.

Good question, but it seems not. To be sure… I switched to 4.06.0+trunk+flambda and compiled

(* compose.ml *)
let (%) g f x = g (f x)
let (%>) f g x = g (f x)

(* bench.ml *)
open Compose
let f xs = Array.map (log % float_of_int % succ) xs
let xs = Array.init 10000 (fun _ -> Random.int 1000)
let ys = f xs

separately in the same directory with

ocamlopt -O3 -c -S compose.ml
ocamlopt -O3 -c -S bench.ml

According to bench.s the f calls the compose operator (camlCompose__$25_7@PLT).

Same for 4.04.1, and there the PPX compiled version is half the size of the non-PPX version.

Indeed; it looks like the optimizer is not yet good enough at handling several arities (you wrote the compose iterators as functions of three arguments, but they are in fact only applied to two arguments).

For the record, flambda optimizes correctly with the following definition of the operators (but the non-flambda optimizer does not):

let (%) g f = (); (fun x -> g (f x))
let (%>) f g = (); (fun x -> g (f x))

Nice! Then ppx_compose can be retired in a few versions’ time.