I'd like to understand the reason for this behavior of OCAML objects. Suppose I have a class A
that calls methods of an object of another class B
. Schematically, A#f calls B#g and B#h. The normal practice in OOP is that I would like to avoid using B as a fixed concrete class, but instead declare only an interface for B. What is the best way to do this in OCAML? I tried several options, and I do not quite understand the reason why some of them work while others don't. Here are the code samples.
Version 1:
# class classA = object
method f b = b#g + b#h
end ;;
Error: Some type variables are unbound in this type:
class a : object method f : < g : int; h : int; .. > -> int end
The method f has type (< g : int; h : int; .. > as 'a) -> int where 'a
is unbound
This behavior is well-known: OCAML correctly infers that b
has the open object type <g:int;h:int;..>
but then complains that my class does not declare any type variables. So it seems that classA
is required to have type variables; I then introduced a type variable explicitly.
Version 2:
# class ['a] classA2 = object
method f (b:'a) = b#g + b#h
end ;;
class ['a] classA2 :
object constraint 'a = < g : int; h : int; .. > method f : 'a -> int end
This works, but the class is now explicitly polymorphic with a type constraint, as OCAML shows. It is also confusing that the class type contains a type variable 'a
, and yet I can still say let x = new classA2
without specifying a type value for 'a
. Why is that possible?
Another drawback of classA2
is that an explicit type constraint (b:'a)
contains a type variable. After all, I know that b
must conform to a fixed interface rather than to an unknown type 'a
. I want OCAML to verify that this interface is indeed correct.
So in version 3 I first declared an interface classB
as a class type and then declared that b
must be of this type:
# class type classB = object method g:int method h:int end;;
class type classB = object method g : int method h : int end
# class classA3 = object method f (b:classB) = b#g + b#h end;;
class classA3 : object method f : classB -> int end
This works too, but my puzzlement remains: why doesn't classA3
require explicit polymorphism any more?
Summary of questions:
- Why is it possible to use
new classA2
without specifying a type for'a
even thoughclassA2
is declared with a type variable'a
? - Why does
classA3
accept a type constraint(b:classB)
and does not require a bound type variable any more? - Is the functionality of
classA2
andclassA3
different in some subtle way, and if yes, how?