[ WWWFun | Software | O'Labl]
Introduction to O'Labl
Objective Label extends Objective Caml in 3 ways.
All these features are sound with respect to type checking, and
integrate smoothly with other parts of O'Caml, like objects and
classes.
Many languages, like Ada, Common Lisp, or SmallTalk already offer such
a kind of feature. It is intended to enhance readability of programs.
However, except for Smalltalk, it seems that this possibility is not
used very much in actual programming. Since in Ada and Common Lisp use
of these labels in function calls is facultatory, programmers will
most often ommit them.
Reflecting their success in the SmallTalk community, labels in O'Labl
are SmallTalk style, with the possibility of providing different call
patterns through optional parameters. Moreover they can be combined
with Currying to allow out-of-order partial applications.
Why use labeled arguments in a functional programming language ?
- One reason is common with other languages: when a function
takes many arguments, it becomes quickly difficult to remember their
order and meaning. This is particularly true in functional programs,
where one is often led to add parameters to function to avoid global
variables.
String.blit : string -> int -> string -> int -> int -> unit
String.blit s1 0 s2 0 (String.length s1)
With labels a function call is more than a sequence of symbols.
String.blit : string -> pos:int ->
to:string -> to_pos:int -> len:int -> unit
String.blit s1 pos:0 to:s2 to_pos:0 len:(String.length s1)
- Another reason, specific to strong typing, is that the label
information is reflected by types. The concept of
type-as-documentation is often used in functional programming, and
labels make it stronger, as you can see in the example above.
A common practice in absence of labels is to define type aliases and
use their names as documentation, but this does not allow for
relative information, that is, what is the role of this
argument in this function.
- A third reason, specific to functional programming, is the use of
functionals. Generally the argument function is taken first, which
means that all other arguments have to be written after it. If you
write the function inline, this leads to unreadable code.
List.fold_left
(fun st x ->
....
....)
a l
With labels, you can freely change the order.
List.fold_left l st:a
fun:(fun x st:st ->
....
....)
- Last, and again specific to functional programming, this allows
for a greater variety of partial applications. You are not retricted
anymore to applying in the definition order. For instance:
# String.blit to:s2 to_pos:0;;
- : string -> pos:int -> len:int -> unit = <fun>
Labeled arguments are a nice feature, and particularly useful with
functional programming, but their success in SmallTalk comes from the
use of call patterns. That is, depending on the labels you
use in your message, different methods will be called.
In O'Labl this is also possible with functions, within the limits of
the type system. That is, the same function may have different call
patterns, but they must all
- return values of the same type.
- take values of the same types on identical labels.
- and all combinations of presence/absence must be allowed
For instance, we can define a powerful string_copy function,
based on the above String.blit function.
# let string_copy s1 to:s2 ?:pos [< 0 >] ?:to_pos [< 0 >]
?:len [< min (String.length s1 - pos) (String.length s2 - to_pos) >]
= String.blit s1 :pos to:s2 :to_pos :len ;;
val string_copy : string -> to:string ->
?pos:int -> ?to_pos:int -> ?len:int -> unit
In fact the above syntax in an abbreviated form for the following
code, where ? denotes optional parameters.
# let string_copy s1 to:s2 ?pos:opos1 ?to_pos:opos2 ?len:olen =
let pos1 = match opos1 with None -> 0 | Some x -> x in
let pos2 = match opos2 with None -> 0 | Some x -> x in
let len = match olen with Some -> x
| None -> min (String.length s1 - pos1) (String.length s2 - pos2)
in String.blit s1 pos:pos1 to:s2 to_pos:pos2 len:len ;;
As you can see, the dispatch is made dynamically. This means that, by
using this last form of definition, one can program almost any pattern
selection. On the other hand, the abbreviated form used above is
powerful enough for most cases.
Calling a function with optional parameters is done exactly the same
way as without optionals.
# let s1 = "12345" and s2 = "12345678";;
val s1 : string = "12345"
val s2 : string = "12345678"
# string_copy s1 to:s2;;
> String.blit s1 to:s2 pos:0 to_pos:0 len:5
- : unit = ()
# string_copy s1 to:s2 len:3;;
> String.blit s1 to:s2 pos:0 to_pos:0 len:3
- : unit = ()
# string_copy s1 to:s2 pos:2;;
> String.blit s1 to:s2 pos:2 to_pos:0 len:3
- : unit = ()
Partial application is still possible.
# string_copy to:s2 to_pos:2;;
- : string -> ?pos:int -> ?len:int -> unit = <fun>
Another new feature, independent from the two previous, is the
availability of polymorphic variants. They are simply sum types that
you don't need to define (their types are inferred).
You will mainly use them in two cases:
- When you want to use the same constructor name in several sums.
- When you use the type for a simple information.
The first case happens when objects take their values in different
ranges.
type number = [int(int) float(float)]
and basic = [int(int) float(float) bool(bool) char(char) string(string)]
One can view number as a subtype of basic. Now if I
define print_basic properly, it will also work with values of
type number.
# let print_basic = function
`int i -> print_int i
| `float x -> print_float x
| `bool b -> print_bool b
| `char c -> print_char c
| `string s -> print_string s ;;
val print_basic : [<int(int) float(float) bool(bool) char(char) string(string)] -> unit
# print_basic (`char 'a');;
a- : unit
# print_basic (`int 1 : number);;
1- : unit
The second case is again useful to make programs more readable and
type safe. For instance I can define a function with type:
val search_string : string -> in:string -> mode:[exact subcase regexp] -> index
It is of course possible to do it by defining explicitly a
search_mode type, but then you always have to refer
explicitly to the module where it was defined, which is a pain when
the information is of communication nature.
This is the most recent addidition to Objective Label. Please refer to
the DVI and postscript version of the manual for how to use them.
For more detailed information, see the manual.
Jacques Garrigue, 96.10.1.