Author: Christian.Queinnec@lip6.fr

## Other Aspects of the Object Extension

In this section we describe the declaration of ``object'' types and local declarations in classes. The latter can be used for class variables by making constructors that reference the local environment.

### Interfaces

Class interfaces are generally infered by the type system, but they can also be defined by a type declaration. Only public methods appear in this type.

### Syntax

class type name =
object
:
val namei : typei
:
method namej : typej
:
end

Thus we can define the class `point` interface:

    # class type interf_point  =
     object
       method get_x : int
       method get_y : int
       method moveto : (int * int ) -> unit
       method rmoveto : (int * int )  -> unit
       method to_string : unit -> string
       method distance : unit -> float
     end ;;

This declaration is useful because the defined type can be used as a type constraint.

    # let seg_length (p1:interf_point) (p2:interf_point) =
      let x = float_of_int (p2#get_x - p1#get_x)
      and y = float_of_int (p2#get_y - p1#get_y) in
       sqrt ((x*.x) +. (y*.y)) ;;
    val seg_length : interf_point -> interf_point -> float = <fun>

Interfaces can only mask fields of instance variables and private methods. They cannot mask abstract or public methods.
This is a restriction in their use, as shown by the following example:

    # let p = ( new point_m1 (2,3) : interf_point);;
    Characters 11-29:
    This expression has type
      point_m1 =
        < distance : unit -> float; get_x : int; get_y : int;
          moveto : int * int -> unit; rmoveto : int * int -> unit;
          to_string : unit -> string; undo : unit -> unit >
    but is here used with type
      interf_point =
        < distance : unit -> float; get_x : int; get_y : int;
          moveto : int * int -> unit; rmoveto : int * int -> unit;
          to_string : unit -> string >
    Only the first object type has a method undo

Nevertheless, interfaces may use inheritance. Interfaces are especially useful in combination with modules: it is possible to build the signature of a module using object types, while only making available the description of class interfaces.

### Local Declarations in Classes

A class declaration produces a type and a constructor. In order to make this chapter easier to read, we have been presenting constructors as functions without an environment. In fact, it is possible to define constructors which do not need initial values to create an instance: that means that they are no longer functional. Furthermore one can use local declarations in the class. Local variables captured by the constructor are shared and can be treated as class variables.

#### Constant Constructors

A class declaration does not need to use initial values passed to the constructor. For example, in the following class:

    # class example1 =
       object
         method print () = ()
       end ;;
    class example1 : object method print : unit -> unit end
    # let p = new example1 ;;
    val p : example1 = <obj>

The instance constructor is constant. The allocation does not require an initial value for the instance variables. As a rule, it is better to use an initial value such as `()`, in order to preserve the functional nature of the constructor.

#### Local Declarations for Constructors

A local declaration can be written directly with abstraction.

    # class example2 =
       fun a ->
         object
           val mutable r = a
           method get_r = r
           method plus x = r <- r + x
        end;;
    class example2 :
      int ->
      object val mutable r : int method get_r : int method plus : int -> unit end

Here it is easier to see the functional nature of the constructor. The constructor is a closure which may have an environment that binds free variables to an environment of declarations. The syntax for class declarations allows local declarations in this functional expression.

#### Class Variables

Class variables are declarations which are known at class level and therefore shared by all instances of the class. Usually these class variables can be used outside of any instance creation. In Objective CAML, thanks to the functional nature of a constructor with a non-empty environment, we can make these values (particularly the modifiable ones) shared by all instances of a class.
We illustrate this facility with the following example, which allows us to keep a register of the number of instances of a class. To do this we define a parameterized abstract class 'a om.

    # class virtual  ['a] om =
       object
         method  finalize ()  = ()
         method virtual destroy : unit -> unit
         method virtual to_string : unit -> string
         method virtual all : 'a list
       end;;

Then we declare class `'a lo`, whose constructor contains local declarations for `n`, which associates a unique number with each instance, and for `l`, which contains the list of pairs (number, instance) of still active instances.

    # class ['a] lo =
       let l = ref []
       and n = ref 0 in
         fun s ->
           object(self:'b )
             inherit ['a] om
             val mutable num = 0
             val name = s
             method to_string () = s
             method print () = print_string s
             method print_all () =
               List.iter (function (a,b) ->
                            Printf.printf "(%d,%s) " a (b#to_string())) !l
             method destroy () = self#finalize();
                                 l:= List.filter (function (a,b) -> a <> num) !l; ()
             method all = List.map snd !l
             initializer incr n; num <- !n;  l:= (num, (self :> 'a  om) ) :: !l ; ()
           end;;
    class ['a] lo :
      string ->
      object
        constraint 'a = 'a om
        val name : string
        val mutable num : int
        method all : 'a list
        method destroy : unit -> unit
        method finalize : unit -> unit
        method print : unit -> unit
        method print_all : unit -> unit
        method to_string : unit -> string
      end

At each creation of an instance of class lo, the initializer increments the reference `n` and adds the pair (number, `self`) to the list `l`. Methods `print` and `print_all` display respectively the receiving instance and all the instances containing in `l`.

    # let m1 = new lo "start";;
    val m1 : ('a om as 'a) lo = <obj>
    # let m2 = new lo "between";;
    val m2 : ('a om as 'a) lo = <obj>
    # let m3 = new lo "end";;
    val m3 : ('a om as 'a) lo = <obj>
    # m2#print_all();;
    (3,end) (2,between) (1,start) - : unit = ()
    # m2#all;;
    - : ('a om as 'a) list = [<obj>; <obj>; <obj>]

Method `destroy` removes an instance from the list of instances, and calls method `finalize` to perform a last action on this instance before it disappears from the list. Method `all` returns all the instances of a class created with new.

    # m2#destroy();;
    - : unit = ()
    # m1#print_all();;
    (3,end) (1,start) - : unit = ()
    # m3#all;;
    - : ('a om as 'a) list = [<obj>; <obj>]

We should note that instances of subclasses are also kept in this list. Nothing prevents you from using the same technique by specializing some of these subclasses. On the other hand, the instances obtained by a copy (`Oo.copy` or {< >}) are not tracked.