Algunos excelentes respuestas en este (la verdad de edad) pregunta, pero me siento que tengo que tirar mis pocos centavos en.
No hay manera de que de alguna manera puedo añadir otra opción para este tipo después sin modificando esta declaración. ¿Cuáles son los beneficios de este sistema? Parece que el modo OO sería mucho más extensible.
La respuesta a esto, creo, es que el tipo de extensibilidad que las cantidades abiertas dan es no siempre es un plus, y que, en consecuencia, el hecho de que OO fuerzas esto en usted es una debilidad.
La ventaja de uniones cerradas es su exhaustividad: si ha arreglado todas las alternativas en tiempo de compilación, entonces puede estar seguro de que no habrá casos imprevistos que su código no pueda manejar. Esta es una propiedad valiosa en muchos dominios problemáticos, por ejemplo, en árboles sintácticos abstractos para idiomas. Si está escribiendo un compilador, las expresiones del lenguaje caen en un conjunto predefinido y cerrado de subcajas; usted hace no ¡quiera que las personas puedan agregar nuevas subcajas en tiempo de ejecución que su compilador no comprenda!
De hecho, los AST del compilador son uno de los ejemplos motivadores de la Banda de los Cuatro clásicos para Visitor Pattern, que es la contraparte de OOP a sumas cerradas y concordancia exhaustiva de patrones. Es instructivo reflexionar sobre el hecho de que los programadores de OO terminaron inventando un patrón para recuperar sumas cerradas.
Del mismo modo, los programadores procedurales y funcionales han inventado patrones para obtener el efecto de las sumas. El más simple es la codificación "registro de funciones", que corresponde a las interfaces OO. Un registro de funciones es, efectivamente, una tabla de envío . (¡Nótese que los programadores C han estado usando esta técnica durante siglos!) El truco es que a menudo hay una gran cantidad de funciones posibles de un tipo dado, a menudo infinitamente muchas. Entonces, si tiene un tipo de registro cuyos campos son funciones, entonces eso puede respaldar fácilmente un conjunto de alternativas astronómicamente grande o infinito. Y lo que es más, dado que los registros se crean en tiempo de ejecución y se pueden realizar de forma flexible en función de las condiciones de tiempo de ejecución, las alternativas son con límite de tiempo.
El comentario final me gustaría hacer es que, en mi mente, OO ha hecho demasiadas personas creen que la extensibilidad es sinónimo de el enlace en tiempo (por ejemplo, la posibilidad de añadir nuevos sub-casos a un tipo en tiempo de ejecución), cuando esto simplemente no es verdad en general. Encuadernación tardía es una técnica para la extensibilidad. Otra técnica es composición -construir objetos complejos a partir de un vocabulario fijo de bloques de construcción y reglas para ensamblarlos juntos. El vocabulario y las reglas son idealmente pequeños, pero diseñados para que tengan interacciones ricas que te permitan construir cosas muy complejas.
La programación funcional -y los sabores estáticos ML/Haskell en particular- han enfatizado durante mucho tiempo la composición sobre el encuadernado tardío. Pero en realidad, ambos tipos de técnicas existen en ambos paradigmas, y deberían estar en el juego de herramientas de un buen programador.
También vale la pena señalar que los lenguajes de programación en sí mismos son fundamentalmente ejemplos de composición. Un lenguaje de programación tiene una sintaxis finita, con suerte simple, que le permite combinar sus elementos para escribir cualquier programa posible. (Esto de hecho se remonta al ejemplo anterior de Compiladores/Patrón de visitante y lo motiva.)
Deberías pensar en los tipos de datos de Haskell como una versión sobrealimentada de estructuras y enumeraciones (en el sentido C, no estoy seguro de cómo otros lenguajes las usan) . Son solo datos estúpidos. Los objetos y las clases en el verdadero sentido de OOP tienen bastante control y lógica de negocios en ellos, para lo cual en Haskell usarías funciones de orden superior, registros de funciones, clases de tipo y ese tipo de cosas. – glaebhoerl
Me pregunto si esta es una buena analogía: para reformular Haskell en términos Java (para elegir un lenguaje OOP popular), los tipos de datos son como las clases finales y typeclasses son como las interfaces JDK8 (incluidos los métodos predeterminados, que hacen que las interfaces estén muy cerca ¡herencia múltiple!)). Los constructores admiten composición. Entonces, con esta analogía se puede ver que al "renunciar" a la herencia de implementación, Haskell realmente "pierde" absolutamente nada de la potencia de la POO tradicional; en todo caso, corta una herramienta que es algo peligrosa y se reemplaza fácilmente por alternativas más seguras? –
Creo que podría ampliarlos creando un nuevo tipo de datos que contenga el anterior como una opción – lucidbrot