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

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

Comments

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) [...].


Vadim ShenderAugust 06, 2008 at 4:39 p.m.

Thanks for comment. Your pa_holes extension seems to be very useful.

And the primary goal of my extension is exactly to be a counterpart of Haskell-like partial application with more ocamlish syntax.


AndreyAugust 06, 2008 at 6:56 p.m.

Very nice. It's especially interesting that someone living in Belarus is using OCaml. By the way, are you using it for real project or it's just an experiment/hobby?


ChristopherAugust 07, 2008 at 8:11 a.m.

Saying "there's no syntax in OCaml for fixing an arbitrary subset of arguments" is not completely true, since you can use labelled arguments in arbitrary order. Very nice though.


Vadim ShenderAugust 08, 2008 at 8:33 p.m.

Andrey, Not using it for any commercial project at the moment, but did so in the past and will do it again whenever it'll be suitable for the task at hand.


Vadim ShenderAugust 08, 2008 at 8:35 p.m.

Cristopher, you are right. Of course I implicitly mean positional arguments. Thanks for pointing that out.


: *

: *

: *

 

Copyright © Y-NODE Software 2008.

All rights reserved. Privacy Policy.

E-mail Us Download our Corporate Brochure