How to force only superclass methods to be called, even though they have been overridden (in Ocaml)

Honestly, I know little about the OCaml object system. The following snippet illustrates my problem:

class foo = object (o) method foo1 y = print_endline "foo1"; o#foo2 (y - 1) method foo2 y = print_endline "foo2"; if y > 0 then o#foo2 y else print_endline "end" end class bar = object (o : 'self_type) inherit foo as super method foo2 y = print_endline "bar2"; if y > 0 then super#foo1 y else print_endline "endbar" end let _ = (new bar)#foo2 3 

When executed, the fragment displays the following result:

 bar2 foo1 bar2 foo1 bar2 endbar 

showing that the superclass method foo1 (called through super # foo1) implements the method of overriding foo2 from the subclass. Instead, I expected it to call the foo2 method from the superclass, as it is called via super.

Is it possible to achieve this behavior, i.e. use the superclass method only other superclass methods, even if they are overridden in a subclass?

+4
source share
4 answers

I am not 100% sure, but I am sure you cannot. In OCaml, inheritance and subtyping are two different concepts. A class can be a subtype of another type without inheritance. All inheritance is the pulling of methods from an inherited class.

Polymorphism is achieved through structural input, so your call to foo2 calls a method in bar because bar implements foo2 and NOT, because bar inherits from foo (allegedly C ++, where bar#foo2 is invoked due to a search for a table of virtual functions) .

However, OCaml gives you the ability to distinguish between overridden methods and inherited methods using the inherit...as... syntax. In the bar from your example, o#foo1 and super#foo1 are the same method (since bar does not implement foo1 ), while o#foo2 and super#foo2 are different methods. Despite this, I do not think that in any case the inherited class knows that it is inherited and distinguishes its methods and overridden methods. There may be some syntax for this, but I highly doubt it because inheritance and polymorphism are independent concepts.

+1
source

No no. This is a "late binding", the main idea of ​​OO, not related to OCaml.

I don’t know what you are trying to do, but object-oriented programming may not be the right tool, or at least not used that way. If you want to learn OCaml, you should focus on the non-OO parts, which is interesting enough to get you started.

+4
source

I would say that if you want to write this behavior hard, it is better not to use object-oriented programming. Just implement it as functions that call other functions.

("this behavior", as I understand it: if you call foo2 from inside the code that was called as super#foo1 , then you should call foo2 from the implementation of the superclass, and not from the more "specific" implementations from subclasses)

This is the easiest and cleanest way: software features that do what you want.

Or should you explain to yourself and to us: why do you need OOP? The reason for this is not indicated in the question text. Why use the foo1 and foo2 methods rather than independent functions? (In addition to foo1 and foo2 , there may be some objects and classes and methods in your program).

I wonder if this question arises compared to other lnaguages

If you know another OO language, it is strange that you want “this behavior” from OOP: this is not the behavior expected, for example, in Java or C ++, because they use the concept of a virtual method table that is associated with each object during execution, so when you call a method in your program, it is sent at run time to implement this method, which is actually associated with the object. So, in short: when you use an expression to invoke a method in your program, you agree to adhere to this principle of finding a method implementation ("late binding") , as pointed out by Gasche. Although still cf. differences between OCaml and languages ​​implemented in the table of virtual methods noted by Niki Yoshiuchi.

Formalizing the entire discussion of available and desired behaviors

Although what you want may not be the expected and accessible behavior in many popular OO languages, it is possible and can be implemented in some specific OOP systems, if you have access to the internal functions of OOP.

Say, if in some implementation of super there is a structure containing a table of methods of the superclass (to return to the resolution of the method call for the current object), and methods are functions that should receive the object (method table) as the 1st argument, then execute what you want is to write super.foo1(super, y) .

(Actually, I am wondering if there are OOP implementations whose internals are accessible to the programmer and which allow such a call to be made.)

While normal OOP behavior would be expressed in this system this.foo1(this, y) (where this is the method table for the current object.)

Your call to OCaml super#foo1 y or Java super.foo1(y); converts to this "my" system as super.foo1(this, y) . (Although the differences noted by Nicky Yoshiuchi between OCaml and languages ​​such as Java implemented using the virtual method table still coincide.)

You see the differences between the three cases.

Application. Search for languages ​​that will work this way

Hmm, PHP may be a language where this behavior is possible, but:

  • only on programming at the class level (static methods), and not at the object level;
  • only when you hardcode the weird "late static bindings" when calling the function:
 #!/usr/bin/php <?php class Foo { public static function foo1($y) { echo "foo1\n"; static::foo2($y-1); } public static function foo2($y) { echo "foo2\n"; if ($y > 0) static::foo2($y); else echo "end\n"; } } class Bar extends Foo { public static function foo2($y) { echo "bar2\n"; if ($y > 0) Foo::foo1($y); else echo "endbar\n"; } } class Model extends Foo { public static function foo2($y) { echo "model2\n"; if ($y > 0) static::foo1($y); else echo "endmodel\n"; } } Model::foo2(3); Bar::foo2(3); ?> 

The model behaves in the sense of standard OO objects with virtual methods, and Bar as you would like :

  $ ./test-force-super-binding.php |  head -20
 model2
 foo1
 model2
 foo1
 model2
 foo1
 model2
 endmodel
 bar2
 foo1
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 $ 

(By the way, using parent:: instead of Foo:: will not lead us to your desired behavior.)

I don’t understand what crazy PHP binding specifications, such as static:: , that have some effect only for static methods (like class-level programming).

The C ++ analog example does not, by default, specify the level behavior of an OO object:

 #include<iostream> class Foo { protected: static void foo1(int y) { std::cout << "foo1" << std::endl; foo2(y-1); } public: static void foo2(int y) { std::cout << "foo2" << std::endl; if (y > 0) foo2(y); else std::cout << "end" << std::endl; } }; class Bar: public Foo { public: static void foo2(int y) { std::cout << "bar2" << std::endl; if (y > 0) foo1(y); else std::cout << "endbar" << std::endl; } }; int main() { Bar::foo2(3); return 0; } 

- this gives your desired behavior:

  $ ./a.out |  head -10
 bar2
 foo1
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 foo2
 $ 

even without a special qualifier when calling a function in the code for Bar::foo2() , so we are not interested.

What about Java static methods? .. (are they different from C ++ and give default permission to call functions by default?)

+1
source

As Gasche points out, this is the intended and standard behavior for OO languages.

Calling super#foo1 , since bar does not override foo1 , is exactly equivalent to o#foo1 . The super construct exists only to call the foo2 foo method from methods in bar (otherwise there is no way to refer to this method). In foo1 , called from bar#foo2 , o is actually bar , not foo , so calling foo2 calls the bar foo2 method.

+1
source

Source: https://habr.com/ru/post/1337490/


All Articles