|Benefits of OCaml|
Benefits of OCaml
Most languages provide a construct that allows the flow of execution to take one of several paths depending on the value of an expression. OCaml provides a construct known as pattern matching that is more powerful for two main reasons:
Pattern matching is a large subject. Let us examine some of the pattern matching capabilities offered by OCaml.
The OCaml compilers check pattern matches at compile-time to ensure that they are exhaustive (all possible inputs match at least one pattern, i.e. no values "escape" the pattern match) and contain no redundant patterns. Any inexhaustive or redundant patterns incur a warning (rather than an error).
For example, the following function tests for the empty list but does not provide an alternative for non-empty lists:
# let is_empty = function |  -> true;; Warning: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: _::_ val is_empty : 'a list -> bool = <fun>
Note that the compiler provides an example
The following function tests for an empty list, returning
# let is_empty = function |  -> true | _::_ -> false | _ -> false;; Warning: this match case is unused. val is_empty : 'a list -> bool = <fun>
As the first two patterns match all possible lists, the third pattern is redundant and the compiler emits a warning about it.
Pattern matching is not only used to perform an action based upon the structure of a value, such as the emptiness of a list, it can also be used to dissect data structures by binding variables to parts of the data structure.
For example, the following function sums the integers in a list by adding the head to the sum of the tail:
# let rec sum list = match list with |  -> 0 | head::tail -> head + sum tail;; sum : int list -> int = <fun> # sum [1; 2; 3];; - : int = 6
The second pattern matches any list with at least one element, binding the variable
Parallel pattern matching
The ability to dissect multiple data structures simultaneously can be of great benefit. This is known as parallel pattern matching and is implemented by simply matching over a tuple of the data structures.
For example, the following recursive function tests if every pair of elements in a pair of lists sum to zero:
# let rec f list1 list2 = match list1, list2 with | h1::t1, h2::t2 -> h1+h2=0 && f t1 t2 | _, _ -> true;; val f : int list -> int list -> bool = <fun>
Parallel pattern matching is one of the more powerful features of pattern matching and allows related data structures to be decomposed simultaneously. Moreover, parallel pattern matches are also compiled into efficient code.
Multiple patterns may invoke the same expression. For example, the following function returns
# let f = function |  | [_] -> true | _::_::_ -> false;; val f : 'a list -> bool = <fun> # f ;; - : bool = true
OCaml extends this core functionality to allow or-patterns in subpatterns. For example, the following function counts the number of 0s and 1s in the given list:
# let rec count = function |  -> 0 | (0|1)::t -> 1 + count t | _::t -> count t;; val count : int list -> int = <fun> # count [1; 2; 3; 0; 1; 2; 3];; - : int = 3
In OCaml, patterns may have arbitrary boolean guard expressions associated with them, such that they can only match when the guard expression evaluates to
For example, the following function counts the number of 0s and 1s in the given list using a guard expression:
# let rec count = function |  -> 0 | h::t when h=0 || h=1 -> 1 + count t | _::t -> count t;; val count : int list -> int = <fun> # count [1; 2; 3; 0; 1; 2; 3];; - : int = 3
Pattern matching is one of the most useful features of OCaml. In addition to providing an elegant and efficient means to dispatch computation, pattern matches are subjected to static checks beyond static type checking. Thus, pattern matching accelerates development and improves both reliability and performance.
|© Flying Frog Consultancy Ltd., 2007||Contact the webmaster|