ventajas sirve que programacion para orientado orientada objetos lenguajes lenguaje ejemplos caracteristicas oop scala haskell f# functional-programming

oop - sirve - programacion orientada a objetos java



Programación orientada a objetos en un contexto de programación puramente funcional? (3)

¿Hay alguna ventaja en el uso de programación orientada a objetos (OOP) en un contexto de programación funcional (FP)?

He estado usando F# durante un tiempo, y noté que cuanto más mis funciones son sin estado, menos necesito tenerlas como métodos de objetos. En particular, existen ventajas al confiar en la inferencia de tipo para poder utilizarlas en la mayor cantidad de situaciones posible.

Esto no excluye la necesidad de espacios de nombres de alguna forma, que es ortogonal a ser OOP. Tampoco se desaconseja el uso de estructuras de datos. De hecho, el uso real de los lenguajes de PF depende en gran medida de las estructuras de datos. Si observa la pila F # implementada en F Sharp Programming / Advanced Data Structures , encontrará que no está orientada a objetos.

En mi opinión, OOP está fuertemente asociado con tener métodos que actúan sobre el estado del objeto principalmente para mutar el objeto. En un contexto de FP puro que no es necesario ni deseado.

Una razón práctica puede ser poder interactuar con el código OOP, de la misma manera en que F # funciona con .NET . Aparte de eso, ¿hay alguna razón? ¿Y cuál es la experiencia en el mundo Haskell, donde la programación es más pura FP?

Apreciaré cualquier referencia a documentos o ejemplos contrafácticos del mundo real sobre el tema.


En cuanto a Haskell, las clases son menos útiles allí porque algunas características OO se logran más fácilmente de otras maneras.

La encapsulación o "ocultación de datos" se realiza frecuentemente a través de cierres de funciones o tipos existenciales, en lugar de miembros privados. Por ejemplo, aquí hay un tipo de datos de generador de números aleatorios con estado encapsulado. El RNG contiene un método para generar valores y un valor de inicialización. Como el tipo ''semilla'' está encapsulado, lo único que puede hacer con él es pasárselo al método.

data RNG a where RNG :: (seed -> (a, seed)) -> seed -> RNG a

El envío dinámico de métodos en el contexto del polimorfismo paramétrico o "programación genérica" ​​es proporcionado por clases de tipos (que no son clases OO). Una clase de tipo es como la tabla de métodos virtuales de una clase OO. Sin embargo, no hay datos escondidos. Las clases de tipo no "pertenecen" a un tipo de datos como lo hacen los métodos de clase.

data Coordinate = C Int Int instance Eq Coordinate where C a b == C d e = a == b && d == e

El envío de métodos dinámicos en el contexto del polimorfismo de subtipado o "subclases" es casi una traducción del patrón de clases en Haskell usando registros y funciones.

-- An "abstract base class" with two "virtual methods" data Object = Object { draw :: Image -> IO () , translate :: Coord -> Object } -- A "subclass constructor" circle center radius = Object draw_circle translate_circle where -- the "subclass methods" translate_circle center radius offset = circle (center + offset) radius draw_circle center radius image = ...


La desconexión que ves no es de FP vs. OOP. Se trata principalmente de inmutabilidad y formalismos matemáticos vs. mutabilidad y enfoques informales.

En primer lugar, prescindamos del problema de la mutabilidad: puede tener FP con mutabilidad y OOP con inmutabilidad muy bien. Aún más funcional que Haskell te permite jugar con datos mutables todo lo que quieras, solo tienes que ser explícito sobre qué es mutable y el orden en que suceden las cosas; y dejando a un lado las preocupaciones sobre la eficiencia, casi cualquier objeto mutable podría construir y devolver una nueva instancia "actualizada" en lugar de cambiar su propio estado interno.

El problema más grande aquí son los formalismos matemáticos, en particular el uso intensivo de tipos de datos algebraicos en un lenguaje poco alejado del cálculo lambda. Has etiquetado esto con Haskell y F #, pero te das cuenta de que es solo la mitad del universo de programación funcional; la familia Lisp tiene un carácter muy diferente, mucho más libre en comparación con los lenguajes de estilo ML. La mayoría de los sistemas OO de amplio uso en la actualidad son de naturaleza muy informal: existen formalismos para OO, pero no se mencionan explícitamente de la misma forma que los formalismos de FP en los lenguajes de estilo ML.

Muchos de los conflictos aparentes simplemente desaparecen si eliminas el desajuste de formalismo. ¿Desea construir un sistema OO flexible, dinámico y ad-hoc sobre un Lisp? Adelante, funcionará bien. ¿Desea agregar un sistema OO formal e inmutable a un lenguaje ML-style? No hay problema, simplemente no esperes que funcione bien con .NET o Java.

Ahora, usted se estará preguntando, ¿cuál es un formalismo apropiado para OOP? Bueno, aquí está la línea de golpe: en muchos sentidos, ¡está más centrada en las funciones que la FP de estilo ML! Me referiré a uno de mis artículos favoritos para lo que parece ser la distinción clave: los datos estructurados como los tipos de datos algebraicos en los lenguajes de estilo ML proporcionan una representación concreta de los datos y la capacidad de definir operaciones en ellos; los objetos proporcionan una abstracción de recuadro negro sobre el comportamiento y la capacidad de reemplazar fácilmente los componentes.

Aquí hay una dualidad que va más allá de solo FP vs. OOP: está estrechamente relacionada con lo que algunos teóricos del lenguaje de programación llaman el problema de expresión : con datos concretos, puede agregar fácilmente nuevas operaciones que funcionan con él, pero cambiar la estructura de los datos es más difícil. Con los objetos puede agregar fácilmente nuevos datos (p. Ej., Nuevas subclases) pero es difícil agregar nuevas operaciones (piense en agregar un nuevo método abstracto a una clase base con muchos descendientes).

La razón por la que digo que la POO está más centrada en las funciones es que las mismas funciones representan una forma de abstracción conductual. De hecho, puede simular la estructura de estilo OO en algo como Haskell utilizando registros que contienen un conjunto de funciones como objetos, permitiendo que el tipo de registro sea una "interfaz" o una "clase base abstracta" y que las funciones que crean registros reemplacen constructores de clase. Entonces, en ese sentido, los lenguajes OO usan funciones de orden superior mucho, mucho más a menudo que, por ejemplo, Haskell.

Para un ejemplo de algo así como este tipo de diseño muy bien utilizado en Haskell, lea la fuente del paquete graphics-drawingcombinators , en particular la forma en que utiliza un tipo de registro opaco que contiene funciones y combina cosas solo en términos de su comportamiento.

EDITAR: Algunas cosas finales que olvidé mencionar arriba.

Si OO hace un uso extensivo de las funciones de orden superior, al principio podría parecer que debería encajar de forma muy natural en un lenguaje funcional como Haskell. Lamentablemente, este no es el caso. Es cierto que los objetos tal como los describí (véase el artículo mencionado en el enlace LtU) encajan perfectamente. de hecho, el resultado es un estilo OO más puro que la mayoría de los lenguajes OO, porque los "miembros privados" están representados por valores ocultos por el cierre utilizado para construir el "objeto" y son inaccesibles a cualquier otra cosa que no sea la instancia específica. ¡No se obtiene mucho más privado que eso!

Lo que no funciona muy bien en Haskell es la subtipificación . Y, aunque creo que la herencia y la subtipificación se usan con demasiada frecuencia en los lenguajes OO, alguna forma de subtipado es bastante útil para poder combinar objetos de manera flexible. Haskell carece de una noción inherente de subtipado, y los reemplazos hechos a mano tienden a ser excesivamente torpes para trabajar con ellos.

Por otro lado, la mayoría de los lenguajes de OO con sistemas de tipo estático también hacen un hash completo de subtipado al ser demasiado flojos con la posibilidad de sustitución y no proporcionar el soporte adecuado para la variación en las firmas de métodos. De hecho, creo que el único lenguaje completo de OO que no lo ha estropeado completamente, al menos que yo sepa, es Scala (F # parecía hacer demasiadas concesiones a .NET, aunque al menos no creo comete nuevos errores). Sin embargo, tengo una experiencia limitada con muchos de esos idiomas, así que definitivamente podría estar equivocado aquí.

En una nota específica de Haskell, sus "clases de tipos" a menudo parecen tentadores para los programadores OO, a lo que les digo: "No vayan allí". Tratar de implementar OOP de esa manera solo terminará en lágrimas. Piense en clases de tipos como reemplazo de funciones / operadores sobrecargados, no OOP.


Creo que hay varias formas de entender lo que significa OOP. Para mí, no se trata de encapsular el estado mutable , sino más bien de organizar y estructurar programas. Este aspecto de OOP se puede usar perfectamente bien junto con los conceptos de FP.

Creo que mezclar los dos conceptos en F # es un enfoque muy útil: puede asociar el estado inmutable con las operaciones que funcionan en ese estado. Obtendrás las bonitas características de la compleción de ''punto'' para los identificadores, la capacidad de usar fácilmente el código F # de C #, etc., pero aún puedes hacer que tu código funcione perfectamente. Por ejemplo, puedes escribir algo como:

type GameWorld(characters) = let calculateSomething character = // ... member x.Tick() = let newCharacters = characters |> Seq.map calculateSomething GameWorld(newCharacters)

Al principio, las personas generalmente no declaran tipos en F # - puede comenzar simplemente escribiendo funciones y luego desarrollar su código para usarlas (cuando comprenda mejor el dominio y sepa cuál es la mejor manera de estructurar el código). El ejemplo de arriba:

  • Sigue siendo puramente funcional (el estado es una lista de caracteres y no está mutado)
  • Está orientado a objetos: lo único inusual es que todos los métodos devuelven una nueva instancia de "el mundo"