Y-NODE Software Y-NODE Corporate Blog main page Y-NODE website

July archive

OCaml partial application syntax enhancement

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]
instead of
  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]
It's possible to skip any number of leftmost arguments before partial application:
  # 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.

Posted by Vadim Shender on 2008-07-09 14:09:55
Tags: ocaml, functional programming, camlp4

Copyright © Y-NODE Software 2008.

All rights reserved. Privacy Policy.

E-mail Us Download our Corporate Brochure