(* A goal that succeeds if v is one of `terms` *)
let rec amb (terms : term list) (v : term) = match terms with
| [] -> null
| term::ts -> disj (match_goal v term) (amb ts v)
and null (s : state) = Mzero (* a goal that always fails *)
and ...
For those who don't speak OCaml (whose syntax is really quite unintuitive), that's a function named `amb` which takes a parameter called `terms` of type `term list` (in C++ syntax, that would be list<term>). It returns an anonymous function that takes a parameter called `v` of type `term`. This function may call `disj`, or it may return `null`, which is not a keyword, but actually another function I defined; it takes a parameter named `s`, which is of type `state`, and returns Mzero, which is at long last an honest-to-God singleton constant. (Of the parametric type `α stream`, as it happens, but let's not get distracted with that.)So `amb` is, in object-oriented parlance, a state stream factory factory factory. Upon being passed a `term list`, it returns a factory, which, upon being passed a `term`, returns a factory, which, upon being passed a `state`, returns a `stream`. (I only traced the `null` path in this comment, but the `disj` path also returns a stream, specifically a `state stream`—OCaml's static type checking guarantees that both return paths return compatible types.)
You might think that isn't the way it's actually used, but my example code uses it in precisely that way:
dump_stream (call_fresh (amb [Const 17; Pair(Const 20, Const 20); Const 11]) empty_state) ;;
Here `amb` is being invoked with just a `term list`. (OCaml separates list items with semicolons, so that commas always make tuples.)So I think factory factory factories are very useful, but also I think they're really hard to keep under mental control without a functional language with a strong static type system. How on earth would you translate those four lines of code above into Java? With four inner classes, maybe, nested eight levels deep with {{}}?
If you're interested, I was translating μKanren, a Prolog-like logic-programming language originally written in 39 lines of Scheme, into OCaml: http://canonical.org/~kragen/sw/dev3/mukanren.ml