How can I subclass Ocaml with additional methods?

In Ocaml, I struggle with subclass and types:

class super = object (self) method doIt = ... end; class sub = object (self) inherit super method doIt = ... self#somethingElse ... method somethingElse = ... end; let myFunction (s:super) = ... myFunction new sub 

Apparently, in Ocaml, the sub class is not a "subtype" of the super class, because the sub#doIt method calls the method in sub , which is not in super >. However, this seems like a fairly common use case for OO programming. What is the recommended way to accomplish this?

+8
object inheritance types class ocaml
source share
3 answers

As Rémi mentioned, the problem with your code is that a system like OCaml supports only one type for an expression: an expression of type sub not of type super . In your example, myFunction expects an argument of type super , and the expression new sub is of type sub , so the problem.

Upcasting is essential for object-oriented programming, and OCaml supports it with two different constructs.

The first type is the type of coercion . If super is a supertype of sub (which means that semantically values ​​of type sub behave like super values) and x : sub , then (x :> super) : super . A type operator :> makes the conversion explicit - the equivalent of popular object-oriented languages ​​implicitly when you use an avalue of type sub , where super is expected.

The second is supertype restrictions : a variable of a given type is required to be a subtype of this type. This is indicated as #super or (#super as 'a) if you want to specify a type variable inside. The supertype constraints do not actually change the type of the expression, for example, type coercion, they just check if the type is a valid subtype of the required type.

To learn more about the difference, consider the following example:

 class with_size ~size = object val size = size : int method size = size end class person ~name ~size = object inherit with_size ~size val name = name : string method name = name end let pick_smallest_coerce (a : with_size) (b : with_size) = if a # size < b # size then a else b let pick_smallest_subtype (a : #with_size) (b : #with_size) = if a # size < b # size then a else b 

The pic_smallest_coerce type is equal to with_size -> with_size -> with_size : even if you passed two instances of person , the return value will be of type with_size and you cannot call its name method.

The pic_smallest_subtype type is (#with_size as 'a) -> 'a -> 'a : if you pass two instances of person , the type system will determine that 'a = person and correctly identify the return value as the person type (which allows the use of the name method) .

In short, supertype limitations simply guarantee that your code will work without losing any type information - the variable retains its original type. Typical coercion actually loses information about the type (which in the absence of step-down casting is a very unpleasant thing), so it should be used only as a last resort in two situations:

1. You cannot have a polymorphic function. #super limitations rely on #super as a free type variable, so if you cannot afford to have a free type variable in your code, you will have to do without it.

2. You need to store values ​​of different actual types in one container. A list or link that may contain instances of person or box will use with_size and coercion:

 let things = [ my_person :> with_size ; my_box :> with_size ] 

Please note that the type determination algorithm detects its own supertype limitations (it will not determine which type of class or class you are going to use, but it will build a literal type):

 let pick_smallest_infer ab = if a # size < b # size then a else b val pick_smallest_infer : (< size : 'a ; .. > as 'b) -> 'b -> 'b 

As such, with rare exceptions, annotating the actual limitations of a supertype is a useful exercise only when documenting your code.

+7
source share

sub is probably a subtype of super. But in ocaml there is no implicit type conversion. So your function does not accept super subtype. You must explicitly force the:

 let myFunction (s:super) = ... myFunction (new sub :> super) 

Or it is advisable to accept the super subtype:

 let myFunction (s:#super) = ... myFunction new sub 
+8
source share

If you want myFunction accept any super subtype as an argument, you must define it as follows:

 let myFunction s = let s = (s :> super) in ... 

... or equivalent ...

 let myFunction (s :> super) = ... 

As for your comment, that sub not a subtype of super in your example, because the doIt method in sub is what is dispatched when the value of the object to the left of the # operator is of type class sub , well, I think you're wrong. This is exactly what you should expect.

To allow sub class methods to call the doIt method in the super class, you must define them like this:

 class super = object (self) method doIt = ... end; class sub = object (self) inherit super as parent method doIt = ... let _ = parent#doIt in ... self#somethingElse ... method somethingElse = ... end; 
0
source share

All Articles