Previous Up Next
B.3 Optional arguments

Labels can also be used to declare default values for some arguments.

Quick overview
Arguments with default values are called optional arguments, and can be omitted in function calls ---the corresponding default values will be used. For instance, one could have declared a function substring as follows
let substring ?pos:(p=0) ~length:l s = String.sub s p l;;
This would allow to call substring with its length argument and an anonymous string, leaving the position to its default value 0. The anonymous string parameter has been moved as the last argument, inverting the convention taken in String.sub, so as to satisfy the requirement than an optional argument must always be followed by an anonymous argument which is used to mark the end optional arguments and replace missing arguments by their default values.

Application to class constructors
In OCaml, objects are created from classes with the new construct. This amounts to having a unique constructor of the same name as the name of the class, with the same arity as that of the class.

In object-oriented languages, it is common and often quite useful to have several ways of building objects of the same class. One common example are is to have default values for some of the parameters. Another situation is to have two (or more) equivalent representations for an object, and to be able to initialized the object using the object either way. For instance, complex points can be defined by giving either cartesian or polar coordinates.

One could think of emulating several constructors by defining different variants of the class obtained by abstraction and application of the original class, each one providing a new class constructor. However, this schema breaks modularity, since classes cannot be simultaneously refined by inheritance.

Fortunately, labeled arguments and variant types can be used together to provide the required flexibility, as it there were several constructors, but with a unique class that can be inherited.

For example, two-dimensional points can be defined as follows:
class point ~x:x0 ?y:(y0=0) () =
  object method getx = x0 + 0 method gety = y0 + 0 end;;
(The extra unit argument is used to mark the end of optional arguments.) Then, the y coordinate may be left implicit, which defaults to 0.
let p1 = new point ~x:1 ();;
let p2 = new point ~x:1 ~y:2 ();;
Conversely, one could define the class so that
class point arg =
  let x0y0 =
   match arg with
   | `Cart (x,y) -> xy
   | `Polar(r,t) -> r *. cos tr *. sin t in
  object method getx = x0 method gety = y0 end;;
Then, points can be build by either passing cartesian or polar coordinates
let p1 = new point (`Cart (1.414, 1.));;
let p2 = new point (`Polar (2., 0.52));;
In this case, one could also choose optional labels for convenience of notation, but at the price of some dynamic detection of ill-formed calls:
class point ?x ?y ?r ?t () =
  let x0y0 =
    match xyrt with
   | Some xSome yNoneNone -> xy
   | NoneNoneSome rSome t -> r *. cos tr *. sin t
   | ____ -> failwith "Cart and Polar coordinates can't be mixed" in
  object method getx = x0 method gety = y0 end;;
let p1 = new point ~x:2. ~y:0.52 ();;
let p2 = new point ~r:1.414 ~t:0.52 ();;

Previous Up Next