In many situations in functional programming it's handy to be able to produce "specialized" functions by fixing the values of part of the arguments of a more generic one. This is usually referred to as partial application. Here's a simple example in OCaml:
# (+) 1 ;;
- : int -> int = <fun>
# let f = (+) 1 ;;
val f : int -> int = <fun>
# f 2 ;;
- : int = 3
Partial application allows writing more elegant code in some cases:
# List.map ((+) 1) [0; 1; 2; 3; 4] ;;
- : int list = [1; 2; 3; 4; 5]
# let f c a b = c * (a + b) ;;
val f : int -> int -> int -> int = <fun>
# List.map2 (f 2) [0; 1; 2] [1; 2; 3] ;;
- : int list = [2; 6; 10]
List.map (fun x -> 1 + x) [0; 1; 2; 3; 4] ;;
List.map2 (fun x y -> f 2 x y) [0; 1; 2] [1; 2; 3]
But the support for partial application is somewhat limited - there's no syntax in OCaml for fixing an arbitrary subset of arguments, only a number of leftmost arguments (upd: I mean positional arguments here, no keyword arguments). This limitation disallows writing such elegant code for example in following case: we want to substract 5 from all elements of a list (i.e. we want to fix the second argument). The code has to look like this:
# List.map (fun x -> (-) x 5) [1; 2; 3; 4; 5] ;;
- : int list = [-4; -3; -2; -1; 0]
To overturn this limitation the pa_papply syntax extension have been created:
open Camlp4
module Id : Sig.Id = struct
let name = "Partial apply syntax extension"
let version = "0.0.1"
end
module Make (Syn : Sig.Camlp4Syntax) = struct
open Sig
include Syntax
EXTEND Gram
expr: LEVEL "apply" (* LEFTA *)
[ [ e = expr; stubs = LIST1 "_" ->
let il =
let rec enum i = function [] -> [] | _ :: t -> i :: enum (i + 1) t
in enum 0 stubs
in
let xl = List.map (fun i -> "__papply_x_" ^ string_of_int i) il in
let eapplyx =
List.fold_left (fun accu xi -> <:expr< $accu$ $lid:xi$ >>) e xl in
let funx =
List.fold_right
(fun xi rest -> <:expr< fun $lid:xi$ -> $rest$>>)
xl <:expr< $eapplyx$ __papply_z>>
in <:expr< fun __papply_z -> $funx$ >> ] ] ;
END
end
module M = Register.OCamlSyntaxExtension (Id) (Make)
This extension is compiled by running following command:
# ocamlc -I +camlp4 -pp camlp4orf -c pa_papply.ml
And the command:
# ocamlc -pp "camlp4o -I . pa_papply.cmo" -c test.ml
compiles for instance file test.ml written in our extended syntax.
pa_papply enables to rewrite the previous example in the following way:
# List.map ((-) _ 5) [1; 2; 3; 4; 5] ;;
- : int list = [-4; -3; -2; -1; 0]
# let f x y z = (x + y) * z ;;
val f : int -> int -> int -> int = <fun>
# f 1 2 3 ;;
- : int = 9
# f _ 2 1 3 ;;
- : int = 9
# f 1 _ 3 2 ;;
- : int = 9
# f _ _ 3 1 2 ;;
- : int = 9
Comments, suggestions and bug reports are welcome.








bluestormAugust 05, 2008 at 3:03 p.m.
I have written a few months ago a syntax extension, pa_holes, wich is comparable in use.
The idea is to have a handy syntax for quick lambda-expressions. It is possible to write :
(\ \1 - 5)
To express you ((-) _ 5) expression.
Similarly : let flip = (\ \1 \3 \2 ), etc.
See the code at http://bluestorm.info/camlp4/pa_holes.ml.html
The syntax is a bit heavier (i've been considering using _ instead of indexed parameters, but that's much less flexible and i'm not sure how to mix the two right now), but the application are is more general (as you're not restricted to function application) and i suspect the produced code is a bit more readable. However, your extension adress a more specific case and is quite interested in itself (and the implementation is simple, wich is a plus), so i guess they both have value.
As a side note, there has been a bit of work in the background on an "pa_infixop" extension, wich would allow Haskell-like partial application of infix operations. You would simply write : List.map (-5) [...].