oop - programacion - Logrando polimorfismo en programación funcional
polimorfismo poo (5)
Poner funciones dentro de estructuras de datos puras de forma prototipo: parece que funciona, pero ¿no viola también la idea de definir funciones puras separadamente de los datos?
Si el envío de métodos virtuales es la forma en que desea abordar el problema, este es un enfoque perfectamente razonable. En cuanto a separar las funciones de los datos, es una noción claramente no funcional para empezar. Considero que el principio fundamental de la programación funcional es que las funciones SON datos. Y en cuanto a su sensación de que está simulando una función virtual, yo diría que no es una simulación en absoluto. ES una tabla de funciones virtuales, y eso está perfectamente bien.
El hecho de que el idioma no tenga el soporte de OOP integrado no significa que no sea razonable aplicar los mismos principios de diseño, solo significa que tendrá que escribir más de la maquinaria que otros lenguajes incorporan, porque usted '' Re luchar contra el espíritu natural del idioma que estás usando. Los lenguajes funcionales modernos tienen un soporte muy profundo para el polimorfismo, pero es un enfoque muy diferente al polimorfismo.
El polimorfismo en OOP es muy parecido a la "cuantificación existencial" en lógica: un valor polimórfico tiene ALGUN tipo de tiempo de ejecución pero no se sabe qué es. En muchos lenguajes de programación funcionales, el polimorfismo es más parecido a la "cuantificación universal": un valor polimórfico se puede instanciar a CUALQUIER tipo compatible que su usuario desee. Son dos caras de la misma moneda (en particular, cambian de lugar dependiendo de si mira una función desde "adentro" o "afuera"), pero resulta extremadamente difícil cuando se diseña una lenguaje para "hacer que la moneda sea justa", especialmente en presencia de otras características del lenguaje, como la subtipificación o polimorfismo de alto grado (polimorfismo sobre tipos polimórficos).
Si ayuda, puede pensar en el polimorfismo en los lenguajes funcionales como algo muy parecido a los "genéricos" en C # o Java, porque ese es exactamente el tipo de polimorfismo que, por ejemplo, ML y Haskell, favorecen.
Actualmente estoy disfrutando la transición de un lenguaje orientado a objetos a un lenguaje funcional. Es un soplo de aire fresco, y me estoy encontrando mucho más productivo que antes.
Sin embargo, hay un aspecto de OOP que todavía no he visto una respuesta satisfactoria en el lado de FP, y eso es polimorfismo . es decir, tengo una gran colección de elementos de datos, que deben procesarse de formas bastante diferentes cuando se pasan a ciertas funciones. En aras de la argumentación, digamos que existen múltiples factores que impulsan el comportamiento polimórfico de forma potencialmente exponencial, muchas combinaciones de comportamiento diferentes.
En OOP que puede manejarse relativamente bien usando polimorfismo: ya sea a través de composición + herencia o un enfoque basado en prototipos.
En FP estoy un poco atrapado entre:
- Escribir o componer funciones puras que implementen de manera efectiva los comportamientos polimórficos ramificándose en el valor de cada ítem de datos, se siente como ensamblar una enorme tabla de métodos condicional o incluso simular una tabla de métodos virtuales.
- Poner funciones dentro de estructuras de datos puras de forma prototipo: parece que funciona, pero ¿no viola también la idea de definir funciones puras separadamente de los datos?
¿Cuáles son los enfoques funcionales recomendados para este tipo de situación? ¿Hay otras buenas alternativas?
Mike, ambos enfoques son perfectamente aceptables, y los pros y los contras de cada uno se discuten, como dice Doc Brown, en el Capítulo 2 del SICP. El primero sufre de tener una mesa de tipo grande en alguna parte, que necesita ser mantenida. El segundo es solo tablas tradicionales de polimorfismo / virtual de un solo despacho.
La razón por la cual el esquema no tiene un sistema incorporado es que usar el sistema de objetos incorrecto para el problema conduce a todo tipo de problemas, por lo que si usted es el diseñador de idiomas, ¿cuál elegir? La herencia única de envío único no se ocupará bien de "múltiples factores que impulsan el comportamiento polimórfico de manera exponencialmente muchas combinaciones de comportamiento diferentes".
Para sintetizar, hay muchas maneras de construir objetos, y el esquema, el lenguaje que se analiza en SICP, solo le brinda un conjunto de herramientas básicas desde las cuales puede construir el que necesita.
En un programa de esquema real, construiría su sistema de objetos a mano y luego escondería la plantilla repetitiva asociada con macros.
En Clojure, en realidad tiene un sistema de objeto / despacho precompilado integrado con multimétodos, y una de sus ventajas sobre el enfoque tradicional es que puede despachar en los tipos de todos los argumentos. También puede (aparentemente) usar el sistema de jerarquía para darle características similares a las de la herencia, aunque nunca lo he usado, por lo que debería tomar ese cum grano salis .
Pero si necesita algo diferente del esquema de objetos elegido por el diseñador del lenguaje, puede hacer uno (o varios) que le convenga.
Eso es efectivamente lo que estás proponiendo arriba.
Cree lo que necesita, haga que todo funcione, oculte los detalles con macros.
El argumento entre FP y OO no se trata de si la abstracción de datos es mala, se trata de si el sistema de abstracción de datos es el lugar para rellenar todas las preocupaciones por separado del programa.
"Creo que un lenguaje de programación debería permitir definir nuevos tipos de datos. No creo que un programa deba consistir únicamente en definiciones de nuevos tipos de datos".
Quien dijo
definir funciones puras por separado de los datos
es la mejor práctica?
Si quieres objetos polimórficos, necesitas objetos. En un lenguaje funcional, los objetos se pueden construir al unir un conjunto de "datos puros" con un conjunto de "funciones puras" que operan sobre esos datos. Esto funciona incluso sin el concepto de una clase. En este sentido, una clase no es más que una pieza de código que construye objetos con el mismo conjunto de "funciones puras" asociadas.
Y los objetos polimórficos se construyen reemplazando algunas de esas funciones de un objeto por diferentes funciones con la misma firma.
Si desea obtener más información sobre cómo implementar objetos en un lenguaje funcional (como Scheme), eche un vistazo a este libro:
Abelson / Sussman: "Estructura e Interpración de programas de Computadora"
http://www.haskell.org/haskellwiki/OOP_vs_type_classes#Everything_is_an_object.3F discute muy bien algunas soluciones.
Bueno, en Haskell siempre puedes hacer una clase de tipo para lograr un tipo de polimorfismo. Básicamente, define funciones que se procesan para diferentes tipos. Ejemplos son las clases Eq
y Show
:
data Foo = Bar | Baz
instance Show Foo where
show Bar = ''bar''
show Baz = ''baz''
main = putStrLn $ show Bar
La función show :: (Show a) => a -> String
se define para cada tipo de datos que instancia la clase de tipo Show
. El compilador encuentra la función correcta para usted, dependiendo del tipo.
Esto permite definir funciones de manera más general, por ejemplo:
compare a b = a < b
funcionará con cualquier tipo de clase de letra Ord
. Esto no es exactamente como OOP, pero incluso puedes heredar typeclasses de la siguiente manera:
class (Show a) => Combinator a where
combine :: a -> a -> String
Depende de la instancia definir la función real, usted solo define el tipo, similar a las funciones virtuales.
Esto no está completo y, hasta donde yo sé, muchos lenguajes de FP no tienen clases de tipos. OCaml no lo hace, lo pasa a su parte OOP. Y Scheme no tiene ningún tipo. Pero en Haskell es una forma poderosa de lograr un tipo de polimorfismo, dentro de ciertos límites.
Para ir aún más lejos, las extensiones más recientes de la norma 2010 permiten familias tipo y similares.
Espero que esto te haya ayudado un poco.