• Aucun résultat trouvé

Using coercions

Dans le document The Objective Caml system release 3.12 (Page 52-56)

Objects in Caml

3.12 Using coercions

Subtyping is never implicit. There are, however, two ways to perform subtyping. The most general construction is fully explicit: both the domain and the codomain of the type coercion must be

given.

We have seen that points and colored points have incompatible types. For instance, they cannot be mixed in the same list. However, a colored point can be coerced to a point, hiding its color method:

# let colored_point_to_point cp = (cp : colored_point :> point);;

val colored_point_to_point : colored_point -> point = <fun>

# let p = new point 3 and q = new colored_point 4 "blue";;

val p : point = <obj>

val q : colored_point = <obj>

# let l = [p; (colored_point_to_point q)];;

val l : point list = [<obj>; <obj>]

An object of typetcan be seen as an object of typet’ only iftis a subtype of t’. For instance, a point cannot be seen as a colored point.

# (p : point :> colored_point);;

Error: Type point = < get_offset : int; get_x : int; move : int -> unit >

is not a subtype of colored_point =

< color : string; get_offset : int; get_x : int;

move : int -> unit >

Indeed, narrowing coercions without runtime checks would be unsafe. Runtime type checks might raise exceptions, and they would require the presence of type information at runtime, which is not the case in the Objective Caml system. For these reasons, there is no such operation available in the language.

Be aware that subtyping and inheritance are not related. Inheritance is a syntactic relation between classes while subtyping is a semantic relation between types. For instance, the class of colored points could have been defined directly, without inheriting from the class of points; the type of colored points would remain unchanged and thus still be a subtype of points.

The domain of a coercion can often be omitted. For instance, one can define:

# let to_point cp = (cp :> point);;

val to_point : #point -> point = <fun>

In this case, the functioncolored_point_to_pointis an instance of the functionto_point. This is not always true, however. The fully explicit coercion is more precise and is sometimes unavoidable.

Consider, for example, the following class:

# class c0 = object method m = {< >} method n = 0 end;;

class c0 : object (’a) method m : ’a method n : int end

The object type c0 is an abbreviation for <m : ’a; n : int> as ’a. Consider now the type declaration:

# class type c1 = object method m : c1 end;;

class type c1 = object method m : c1 end

The object type c1 is an abbreviation for the type<m : ’a> as ’a. The coercion from an object of typec0 to an object of type c1is correct:

# fun (x:c0) -> (x : c0 :> c1);;

- : c0 -> c1 = <fun>

However, the domain of the coercion cannot be omitted here:

# fun (x:c0) -> (x :> c1);;

- : c0 -> c1 = <fun>

The solution is to use the explicit form. Sometimes, a change in the class-type definition can also solve the problem

# class type c2 = object (’a) method m : ’a end;;

class type c2 = object (’a) method m : ’a end

# fun (x:c0) -> (x :> c2);;

- : c0 -> c2 = <fun>

While class typesc1 and c2 are different, both object typesc1and c2 expand to the same object type (same method names and types). Yet, when the domain of a coercion is left implicit and its co-domain is an abbreviation of a known class type, then the class type, rather than the object type, is used to derive the coercion function. This allows to leave the domain implicit in most cases when coercing form a subclass to its superclass. The type of a coercion can always be seen as below:

# let to_c1 x = (x :> c1);;

val to_c1 : < m : #c1; .. > -> c1 = <fun>

# let to_c2 x = (x :> c2);;

val to_c2 : #c2 -> c2 = <fun>

Note the difference between the two coercions: in the second case, the type#c2 = < m : ’a; ..

> as ’a is polymorphically recursive (according to the explicit recursion in the class type of c2);

hence the success of applying this coercion to an object of classc0. On the other hand, in the first case,c1was only expanded and unrolled twice to obtain< m : < m : c1; .. >; .. >(remember

#c1 = < m : c1; .. >), without introducing recursion. You may also note that the type ofto_c2 is #c2 -> c2 while the type of to_c1 is more general than #c1 -> c1. This is not always true, since there are class types for which some instances of #c are not subtypes of c, as explained in section 3.16. Yet, for parameterless classes the coercion (_ :> c)is always more general than (_

: #c :> c).

A common problem may occur when one tries to define a coercion to a class cwhile defining class c. The problem is due to the type abbreviation not being completely defined yet, and so its subtypes are not clearly known. Then, a coercion (_ :> c) or(_ : #c :> c)is taken to be the identity function, as in

# function x -> (x :> ’a);;

- : ’a -> ’a = <fun>

As a consequence, if the coercion is applied toself, as in the following example, the type ofselfis unified with the closed typec(a closed object type is an object type without ellipsis). This would constrain the type of self be closed and is thus rejected. Indeed, the type of self cannot be closed:

this would prevent any further extension of the class. Therefore, a type error is generated when the unification of this type with another type would result in a closed object type.

# class c = object method m = 1 end

# and d = object (self)

# inherit c

# method n = 2

# method as_c = (self :> c)

# end;;

Error: This expression cannot be coerced to type c = < m : int >; it has type

< as_c : c; m : int; n : int; .. >

but is here used with type c

Self type cannot be unified with a closed object type

However, the most common instance of this problem, coercing self to its current class, is detected as a special case by the type checker, and properly typed.

# class c = object (self) method m = (self :> c) end;;

class c : object method m : c end

This allows the following idiom, keeping a list of all objects belonging to a class or its subclasses:

# let all_c = ref [];;

val all_c : ’_a list ref = {contents = []}

# class c (m : int) =

# object (self)

# method m = m

# initializer all_c := (self :> c) :: !all_c

# end;;

class c : int -> object method m : int end

This idiom can in turn be used to retrieve an object whose type has been weakened:

# let rec lookup_obj obj = function [] -> raise Not_found

# | obj’ :: l ->

# if (obj :> < >) = (obj’ :> < >) then obj’ else lookup_obj obj l ;;

val lookup_obj : < .. > -> (< .. > as ’a) list -> ’a = <fun>

# let lookup_c obj = lookup_obj obj !all_c;;

val lookup_c : < .. > -> < m : int > = <fun>

The type< m : int >we see here is just the expansion ofc, due to the use of a reference; we have succeeded in getting back an object of typec.

The previous coercion problem can often be avoided by first defining the abbreviation, using a class type:

# class type c’ = object method m : int end;;

class type c’ = object method m : int end

# class c : c’ = object method m = 1 end

# and d = object (self)

# inherit c

# method n = 2

# method as_c = (self :> c’)

# end;;

class c : c’

and d : object method as_c : c’ method m : int method n : int end

It is also possible to use a virtual class. Inheriting from this class simultaneously allows to enforce all methods ofcto have the same type as the methods of c’.

# class virtual c’ = object method virtual m : int end;;

class virtual c’ : object method virtual m : int end

# class c = object (self) inherit c’ method m = 1 end;;

class c : object method m : int end

One could think of defining the type abbreviation directly:

# type c’ = <m : int>;;

However, the abbreviation#c’cannot be defined directly in a similar way. It can only be defined by a class or a class-type definition. This is because#sharp abbreviations carry an implicit anonymous variable.. that cannot be explicitly named. The closer you get to it is:

# type ’a c’_class = ’a constraint ’a = < m : int; .. >;;

with an extra type variable capturing the open object type.

Dans le document The Objective Caml system release 3.12 (Page 52-56)