Cómo forzar llamar solo los métodos de superclase a pesar de que han sido anulados (en Ocaml)

StackOverflow https://stackoverflow.com/questions/4829597

Pregunta

Para ser honesto, no sé mucho sobre el sistema de objetos de Ocaml. El siguiente fragmento ilustra mi problema:

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

Cuando se ejecuta, el fragmento produce la siguiente salida:

bar2
foo1
bar2
foo1
bar2
endbar

, mostrando que el método de superclase FOO1 (llamado Via Super#foo1) ejecuta el método de anulación FOO2 desde la subclase. En su lugar, hubiera esperado que llamara al método FOO2 desde la superclase, como se llama a través de Super.

¿Es posible lograr este comportamiento, es decir, tener un método de superclase Llame solo a otros métodos de superclase, incluso si están anulados en una subclase?

¿Fue útil?

Solución

No estoy 100% seguro de esto, pero estoy bastante seguro de que no puedes. En Ocaml Heritance and Subtyping hay dos conceptos diferentes. Una clase puede ser un subtipo de otro tipo sin herencia. Todo lo que hace la herencia es extraer los métodos de la clase hereditaria.

El polimorfismo se logra a través de la tipificación estructural, por lo que su llamado a foo2 llama al método en bar porque bar ha implementado foo2 Y no porque bar hereda de foo (como se ha apacionado para decir, C ++, donde bar#foo2 se llama debido a una tabla de funciones virtuales.

Dicho esto, OCAML le proporciona una forma de distinguir entre métodos anulados y métodos heredados utilizando el inherit...as... sintaxis. En bar De tu ejemplo, o#foo1 y super#foo1 son el mismo método (ya que bar no implementa foo1) mientras o#foo2 y super#foo2 son métodos diferentes. A pesar de esto, no creo que haya de todos modos para que la clase hereditaria sepa que ha sido heredada y distinga entre sus métodos y los métodos anulados. Puede haber algo de sintaxis para eso, pero lo dudo mucho debido al hecho de que la herencia y el polimorfismo son conceptos independientes.

Otros consejos

No, no es. Esto es "vinculante tardío", una idea básica de OO, no específica de OCAML.

No sé qué estás tratando de hacer, pero la programación orientada a objetos quizás no sea las herramientas adecuadas, o al menos no se usa de esa manera. Si desea aprender OCAML, probablemente debería concentrarse en las partes no OO, lo cual es lo suficientemente interesante para comenzar.

Como señala Gasche, este es el comportamiento previsto y estándar para los idiomas OO.

los super#foo1 llamar, ya que bar no anula foo1, es exactamente equivalente a o#foo1. los super construir solo existe para poder llamar al foo2 método de foo de métodos en bar (De lo contrario, no hay forma de hacer referencia a ese método). En foo1 como se llama de bar#foo2, o es en realidad un bar, No un foo, así que invocando el foo2 el método invoca el bar foo2 método.

Diría que si desea codificar ese comportamiento, será mejor que no use la programación orientada a objetos. Simplemente impleméntelo como funciones llamando a otras funciones.

("Ese comportamiento" como lo entendió: si llamas foo2 desde el interior del código que se ha llamado como super#foo1, entonces exactamente el foo2 de la implementación de la superclase debe llamarse, no de las implementaciones más "específicas" de las subclases)

Es la forma más simple, más limpia y clara de continuar: las funciones del programa que hacen lo que desea.

O deberías explicarte a ti mismo y a nosotros: ¿por qué necesitas OOP? La razón no se da en el texto de la pregunta. Por qué hacer foo1 y foo2 ¿Métodos en lugar de funciones independientes? (Aparte de foo1 y foo2, puede tener algunos objetos, clases y métodos en su programa, cuando sea apropiado).

Se pregunta si esta pregunta viene de comparación con otros lnaguages

Si conoce otro lenguaje OO, es extraño que desee "ese comportamiento" de OOP: no es el comportamiento esperado en, por ejemplo, Java o C ++, porque emplean el concepto de una tabla de métodos virtuales que está asociado con cada objeto en tiempo de ejecución, por lo que al llamar a un método en su programa, se envía en tiempo de ejecución a la implementación de ese método realmente asociado con el objeto. Entonces, en resumen: Cada vez que utiliza una expresión de llamado de método en su programa, se compromete a este principio de encontrar la implementación del método ("vinculación tardía"), como lo señaló Gasche. Aunque todavía cf. Las diferencias entre OCAML y los idiomas implementados con una tabla de métodos virtuales señalados por Niki Yoshiuchi.

Formalizar toda la discusión de los comportamientos disponibles y deseados

Aunque lo que desea podría no ser el comportamiento esperado y disponible en muchos lenguajes OO populares, es imaginable y podría ser implementable en algunos sistemas OOP específicos, si uno tiene acceso a los internos de implementación de OOP.

Digamos, si en alguna implementación, super es una estructura que contiene la tabla de métodos de la superclase (al retraso a la resolución de una llamada de método para el objeto actual), y los métodos son funciones que deben recibir el objeto (la tabla de métodos) como el primer arg, luego para realizar lo que usted quiero, uno escribiría super.foo1(super, y).

(De hecho, me pregunto si hay implementaciones de OOP cuyas internas están expuestas al programador y que permiten hacer tal llamada).

Mientras que el comportamiento de OOP habitual se expresaría en este sistema por this.foo1(this, y) (dónde this es la tabla de métodos para el objeto actual).

Tu llamada Ocaml super#foo1 y o un Java super.foo1(y); se traduce en este sistema "mi" como super.foo1(this, y). (Aunque todavía cf. las diferencias señaladas por Niki Yoshiuchi entre OCAML y idiomas como Java implementado con una tabla de métodos virtuales).

Ves las diferencias entre los 3 casos.

Apéndice. Buscando idiomas que funcionen de esa manera

Hmm, PHP podría ser un lenguaje donde ese comportamiento sería posible, pero:

  • Solo en programación "a nivel de clase" (métodos estáticos), no a nivel de objeto;
  • Solo cuando te das un poco de extraño "Vinculación estática tardía" en la función llamas:

#!/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);
?>

El modelo se comporta en cierto sentido como los objetos OO estándar con métodos virtuales, y barra como tú querías:

$ ./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
$ 

(Por cierto, usando parent:: en vez de Foo:: no nos llevaría a su comportamiento deseado).

No entiendo la prupose de las especificaciones vinculantes de PHP locos como static::, que tienen algún efecto solo para métodos estáticos (es decir, programación a nivel de clase).

Un ejemplo de C ++ analógico no da un comportamiento de nivel de objeto OO de forma predeterminada:


#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;
}

- Le da a tu comportamiento buscado:

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

Incluso sin ningún calificador especial en la llamada de función en el código para Bar::foo2(), así que no es interesante para nosotros.

¿Qué pasa con los métodos estáticos de Java?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top