¿Cómo responderían los proponentes de la programación funcional a esta declaración en Code Complete?

30

En la página 839 de la segunda edición, Steve McConnell está discutiendo todas las formas en que los programadores pueden "conquistar la complejidad" en grandes programas. Sus consejos culminan con esta declaración:

"La programación orientada a objetos proporciona un nivel de abstracción que se aplica a algoritmos y datos al mismo tiempo , un tipo de abstracción que la descomposición funcional por sí sola no proporcionó".

Junto con su conclusión de que "reducir la complejidad es posiblemente la clave más importante para ser un programador efectivo" (misma página), esto parece ser un desafío para la programación funcional.

El debate entre FP y OO a menudo está enmarcado por los proponentes de FP en torno a los problemas de complejidad que se derivan específicamente de los desafíos de concurrencia o paralelización. Pero la concurrencia ciertamente no es el único tipo de complejidad que los programadores de software necesitan conquistar. Quizás enfocarse en reducir un tipo de complejidad aumenta enormemente en otras dimensiones, de modo que en muchos casos, la ganancia no vale el costo.

Si cambiamos los términos de la comparación entre FP y OO de cuestiones particulares como la concurrencia o la reutilización a la gestión de la complejidad global, ¿cómo se vería ese debate?

EDITAR

El contraste que quería destacar es que OO parece encapsular y abstraerse de la complejidad de los datos y los algoritmos, mientras que la programación funcional parece alentar a los detalles de implementación de las estructuras de datos más "expuestos" en todo el programa.

Véase, por ejemplo, Stuart Halloway (un defensor de Clojure FP) aquí diciendo que "la sobreespecificación de los tipos de datos" es "una consecuencia negativa del estilo idiomático de OO" y está a favor de conceptualizar una libreta de direcciones como un simple vector o mapa en lugar de un objeto de OO más rico con propiedades y métodos adicionales (no vectoriales y no cartográficos). (Además, los defensores del diseño orientado a dominios y OO pueden decir que exponer una libreta de direcciones como un vector o mapa sobreexpone los datos encapsulados a métodos que son irrelevantes o incluso peligrosos desde el punto de vista del dominio).

dan
fuente
3
+1 a pesar de que la pregunta se ha formulado de manera bastante antagónica, es una buena pregunta.
mattnz
16
Como muchos han dicho en las respuestas, la descomposición funcional y la programación funcional son dos bestias diferentes. Entonces, la conclusión de que "esto parece ser un desafío para la programación funcional" es simplemente erróneo, no tiene nada que ver con eso.
Fabio Fracassi
55
Claramente, el conocimiento de McConnel en los sistemas modernos de tipos de datos funcionales y los módulos de primer orden de primer orden es algo irregular. Su declaración es completamente absurda, ya que tenemos los módulos y functores de primera clase (ver SML), clases de tipo (ver Haskell). Es solo otro ejemplo de cómo la forma de pensar OO es más una religión que una metodología de diseño respetuosa. Y, por cierto, ¿de dónde sacaste esto de la concurrencia? A la mayoría de los programadores funcionales no les importa en absoluto el paralelismo.
SK-logic
66
@ SK-logic Todo McConnell dijo que "la descomposición funcional sola" no proporciona los mismos medios de abstracción que OOP, lo que me parece una declaración bastante segura. En ninguna parte dice que los lenguajes FP no tienen medios de abstracciones tan poderosas como OOP. De hecho, no menciona los lenguajes FP en absoluto. Esa es solo la interpretación del OP.
sepp2k
2
@ sepp2k, ok, ya veo. Pero aún así, un sistema muy complejo y bien en capas de estructuras de datos y abstracciones de procesamiento se puede construir sobre la nada más que descomposición funcional para el cálculo lambda casi puro, a través de la simulación del comportamiento de los módulos. No hay necesidad de las abstracciones OO en absoluto.
SK-logic

Respuestas:

13

Tenga en cuenta que el libro fue escrito hace más de 20 años. Para los programadores profesionales de la época, FP no existía, estaba enteramente en el ámbito de académicos e investigadores.

Necesitamos enmarcar la "descomposición funcional" en el contexto apropiado del trabajo. El autor no se refiere a la programación funcional. Necesitamos vincular esto con la "programación estructurada" y el GOTOdesorden lleno que vino antes. Si su punto de referencia es un viejo FORTRAN / COBOL / BASIC que no tenía funciones (tal vez, si tuviera suerte, obtendría un solo nivel de GOSUB) y todas sus variables son globales, pudiendo desglosar su programa en capas de funciones es una gran ayuda.

OOP es un refinamiento adicional en este tipo de 'descomposición funcional'. No solo puede agrupar instrucciones en funciones, sino que también puede agrupar funciones relacionadas con los datos en los que están trabajando. El resultado es una pieza de código claramente definida que puede ver y comprender (idealmente) sin tener que perseguir todo su código base para encontrar qué más podría operar en sus datos.

Sean McSomething
fuente
27

Me imagino que los defensores de la programación funcional argumentarían que la mayoría de los lenguajes FP proporcionan más medios de abstracción que la "descomposición funcional sola" y, de hecho, permiten medios de abstracciones comparables en poder a los de los lenguajes orientados a objetos. Por ejemplo, uno podría citar las clases de tipo de Haskell o los módulos de orden superior de ML como tales medios de abstracción. Por lo tanto, la declaración (que estoy bastante seguro era sobre la orientación a objetos frente a la programación de procedimientos, no la programación funcional) no se aplica a ellos.

También debe señalarse que FP y OOP son conceptos ortogonales y no mutuamente excluyentes. Por lo tanto, no tiene sentido compararlos entre sí. Podría muy bien comparar "OOP imperativo" (por ejemplo, Java) versus "OOP funcional" (por ejemplo, Scala), pero la declaración que citó no se aplicaría a esa comparación.

sepp2k
fuente
10
+1 "Descomposición funcional"! = "Programación funcional". El primero se basa en la codificación secuencial clásica usando estructuras de datos vainilla sin herencia (o solo enrollada a mano), encapsulación y polimorfismo. El segundo expresa soluciones usando cálculo lambda. Dos cosas completamente diferentes.
Binario Worrier
44
Disculpas, pero la frase "programación procesal" tercamente se negó a venir a mi mente antes. Para mí, la "descomposición funcional" es mucho más indicativa de la programación de procedimientos que la programación funcional.
Binario Worrier
Sí tienes razón. Supuse que la Programación funcional favorece las funciones reutilizables que operan en las mismas estructuras de datos simples (listas, árboles, mapas) una y otra vez y en realidad afirma que este es un punto de venta sobre OO. Ver Stuart Halloway (un defensor de Clojure FP) aquí diciendo que "la sobreespecificación de los tipos de datos" es "consecuencia negativa del estilo idiomático de OO" y favoreciendo la conceptualización de una libreta de direcciones como un vector o mapa en lugar de un objeto OO más rico con otro (no -vectorish & non-maplike) propiedades y métodos.
dan
El enlace para la cita de Stuart Halloway: thinkrelevance.com/blog/2009/08/12/…
dan
2
@dan Así podría ser en el lenguaje escrito dinámicamente Clojure (no lo sé, no uso Clojure), pero creo que es peligroso concluir a partir de eso, así es como se hace en FP en general. Las personas de Haskell, por ejemplo, parecen ser muy grandes en cuanto a los tipos abstractos y la ocultación de información (quizás no tanto como las personas de Java).
sepp2k
7

Encuentro la programación funcional extremadamente útil en el manejo de la complejidad. Sin embargo, tiende a pensar en la complejidad de una manera diferente, definiéndola como funciones que actúan sobre datos inmutables en diferentes niveles en lugar de encapsular en un sentido de OOP.

Por ejemplo, recientemente escribí un juego en Clojure, y todo el estado del juego se definió en una sola estructura de datos inmutable:

(def starting-game-state {:map ....
                          :player ....
                          :weather ....
                          :other-stuff ....}

Y el bucle principal del juego podría definirse como la aplicación de algunas funciones puras al estado del juego en un bucle:

 (loop [initial-state starting-game-state]
   (let [user-input (get-user-input)
         game-state (update-game initial-state user-input)]
     (draw-screen game-state)
     (if-not (game-ended? game-state) (recur game-state))))

La función clave llamada es update-game, que ejecuta un paso de simulación dado un estado del juego anterior y alguna entrada del usuario, y devuelve el nuevo estado del juego.

Entonces, ¿dónde está la complejidad? En mi opinión, se ha gestionado bastante bien:

  • Ciertamente, la función de actualización del juego hace mucho trabajo, pero se construye componiendo otras funciones, por lo que en realidad es bastante simple. Una vez que baja algunos niveles, las funciones siguen siendo bastante simples, haciendo algo como "agregar un objeto a un mosaico de mapa".
  • Ciertamente, el estado del juego es una gran estructura de datos. Pero, de nuevo, solo se construye componiendo estructuras de datos de nivel inferior. También se trata de "datos puros" en lugar de tener métodos incrustados o se requiere una definición de clase (puede considerarlo como un objeto JSON inmutable muy eficiente si lo desea), por lo que hay muy pocas repeticiones.

OOP también puede gestionar la complejidad a través de la encapsulación, pero si compara esto con OOP, el funcional tiene algunas ventajas muy importantes:

  • La estructura de datos del estado del juego es inmutable, por lo que se puede hacer mucho procesamiento en paralelo fácilmente. Por ejemplo, es perfectamente seguro tener una pantalla de dibujo de llamada de representación en un hilo diferente de la lógica del juego: no pueden afectarse entre sí o ver un estado inconsistente. Esto es sorprendentemente difícil con un gran gráfico de objetos mutables ......
  • Puedes tomar una instantánea del estado del juego en cualquier momento. Las repeticiones son triviales (gracias a las estructuras de datos persistentes de Clojure, las copias apenas ocupan memoria ya que la mayoría de los datos se comparten). También puede ejecutar el juego de actualización para "predecir el futuro" para ayudar a la IA a evaluar diferentes movimientos, por ejemplo.
  • En ninguna parte tuve que hacer compensaciones difíciles para encajar en el paradigma OOP, como definir una jerarquía de clases rígida. En este sentido, la estructura de datos funcional se comporta más como un sistema flexible basado en prototipos.

Finalmente, para las personas que están interesadas en obtener más información sobre cómo gestionar la complejidad en lenguajes funcionales frente a OOP, recomiendo encarecidamente el video del discurso de apertura de Rich Hickey Simple Made Easy (filmado en la conferencia de tecnología Strange Loop )

mikera
fuente
2
Creo que un juego es uno de los peores ejemplos posibles para demostrar los supuestos "beneficios" de la inmutabilidad forzada. Las cosas se mueven constantemente en un juego, lo que significa que debes estar reconstruyendo tu estado de juego todo el tiempo. Y si todo es inmutable, eso significa que no solo tienes que reconstruir el estado del juego, sino todo lo que contiene una referencia al mismo, o que tiene una referencia a eso, y así sucesivamente hasta que estés reciclando todo programa a más de 30 FPS, ¡con toneladas de rotación GC para arrancar! No hay forma de obtener un buen rendimiento de eso ...
Mason Wheeler
77
Por supuesto, los juegos son difíciles de inmutabilidad, ¡por eso lo elegí para demostrar que todavía puede funcionar! Sin embargo, te sorprendería lo que pueden hacer las estructuras de datos persistentes: la mayoría del estado del juego no necesita ser reconstruido, solo las cosas que cambian. Y seguro que hay algo de sobrecarga, pero es solo un pequeño factor constante. Dame suficientes núcleos y
venceré a tu
3
@Mason Wheeler: En realidad, es posible obtener un rendimiento prácticamente igual (tan bueno como con la mutación) con objetos inmutables, sin demasiada GC. El truco en Clojure es utilizar estructuras de datos persistentes : son inmutables al programador, pero en realidad mutables bajo el capó. Lo mejor de ambos mundos.
Joonas Pulakka
44
@quant_dev Más núcleos son más baratos que mejores núcleos ... escapistmagazine.com/news/view/…
deworde
66
@quant_dev: no es una excusa, es un hecho matemático y arquitectónico que es mejor incurrir en una sobrecarga constante si puede compensarlo al escalar su rendimiento casi linealmente con el número de núcleos. La razón por la cual los lenguajes funcionales en última instancia ofrecerán un rendimiento superior es que hemos llegado al final de la línea para un rendimiento de núcleo único, y todo se tratará de concurrencia y paralelismo en el futuro. Los enfoques funcionales (y la inmutabilidad en particular) son importantes para que esto funcione.
mikera
3

"La programación orientada a objetos proporciona un nivel de abstracción que se aplica a algoritmos y datos al mismo tiempo, un tipo de abstracción que la descomposición funcional por sí sola no proporcionó".

La descomposición funcional por sí sola no es suficiente para crear ningún tipo de algoritmo o programa: también debe representar los datos. Creo que la afirmación anterior supone implícitamente (o al menos se puede entender así) que los "datos" en el caso funcional son del tipo más rudimentario: solo listas de símbolos y nada más. La programación en tal lenguaje obviamente no es muy conveniente. Sin embargo, muchos, especialmente los lenguajes nuevos y modernos, funcionales (o multiparadigm), como Clojure, ofrecen estructuras de datos ricas: no solo listas, sino también cadenas, vectores, mapas y conjuntos, registros, estructuras y objetos. - Con metadatos y polimorfismo.

El gran éxito práctico de las abstracciones OO difícilmente puede ser discutido. ¿Pero es la última palabra? Como escribió, los problemas de concurrencia ya son el mayor problema, y ​​el OO clásico no contiene ninguna idea de concurrencia en absoluto. Como resultado, las soluciones OO de facto para lidiar con la concurrencia son solo cinta adhesiva superpuesta: funciona, pero es fácil de arruinar, quita una cantidad considerable de recursos cerebrales de la tarea esencial en cuestión, y no escala bien. Tal vez sea posible tomar lo mejor de muchos mundos. Eso es lo que persiguen los lenguajes multiparadigm modernos.

Joonas Pulakka
fuente
1
Escuché la frase "OO en lo grande, FP en lo pequeño" en alguna parte, creo que Michael Feathers lo citó. Es decir, que FP puede ser bueno para partes particulares de un gran programa, pero en general, debería ser OO.
dan
Además, en lugar de usar Clojure para todo, incluso las cosas que se expresan de manera más limpia en una sintaxis OO más tradicional, ¿qué tal usar Clojure para los bits de procesamiento de datos donde está más limpio y usar Java u otro lenguaje OO para los otros bits? Programación políglota en lugar de programación multiparadigma con el mismo lenguaje para todas las partes del programa. (Algo así como la mayoría de las aplicaciones web usan SQL y OO para diferentes capas).
dan
@dan: use la herramienta que mejor se adapte al trabajo. En la programación políglota, el factor crucial es la comunicación conveniente entre los lenguajes, y Clojure y Java difícilmente podrían jugar mejor juntos . Creo que los programas Clojure más importantes usan al menos algunos bits de las bibliotecas Java estándar de JDK aquí y allá.
Joonas Pulakka
2

El estado mutable es la raíz de la mayoría de las complejidades y problemas relacionados con la programación y el diseño de software / sistema.

OO abarca el estado mutable. FP aborrece el estado mutable.

Tanto OO como FP tienen sus usos y puntos dulces. Elegir sabiamente. Y recuerde el dicho: "Los cierres son los objetos del pobre. Los objetos son el cierre del pobre".

Maglob
fuente
3
No estoy seguro de que su afirmación inicial sea cierta. ¿La raíz de "la mayoría" de las complejidades? En la programación que he hecho o visto, el problema no es tanto el estado mutable como la falta de abstracción y la sobreabundancia de detalles a través del código.
dan
1
@Dan: Interesante. En realidad, he visto todo lo contrario: los problemas surgen del uso excesivo de la abstracción, lo que hace que sea difícil comprender y, cuando sea necesario, corregir los detalles de lo que realmente está sucediendo.
Mason Wheeler
1

La programación funcional puede tener objetos, pero esos objetos tienden a ser inmutables. Las funciones puras (funciones sin efectos secundarios) luego operan en esas estructuras de datos. Es posible hacer objetos inmutables en lenguajes de programación orientados a objetos, pero no fueron diseñados para hacerlo y no es así como tienden a usarse. Esto dificulta razonar sobre programas orientados a objetos.

Tomemos un ejemplo muy simple. Digamos que Oracle decidió que las cadenas de Java deberían tener un método inverso y usted escribió el siguiente código.

String x = "abc";
StringBuffer y = new StringBuffer(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString());

¿Qué evalúa la última línea? Necesita un conocimiento especial de la clase String para saber que esto se evaluaría como falso.

¿Qué pasa si hice mi propia clase WuHoString

String x = "abc";
WuHoString y = new WuHoString(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString())

Es imposible saber a qué se evalúa la última línea.

En un estilo de programación funcional, se escribiría más de la siguiente manera:

String x;
equals(toString(reverse(x)), toString(reverse(WuHoString(x))))

Y debería ser cierto.

Si 1 función en una de las clases más básicas es tan difícil de razonar, entonces uno se pregunta si la introducción de esta idea de objetos mutables ha aumentado o disminuido la complejidad.

Obviamente, hay todo tipo de definiciones de lo que constituye orientado a objetos y lo que significa ser funcional y lo que significa tener ambos. Para mí, puede tener un "estilo de programación funcional" en lenguajes que no tienen funciones como las de primera clase, pero que están hechos para otros lenguajes.

WuHoUnited
fuente
3
Es un poco extraño que digas que los lenguajes OO no están diseñados para objetos inmutables y luego utilizas un ejemplo con cadenas (que son inmutables en la mayoría de los lenguajes OO, incluido Java). También debo señalar que hay lenguajes OO (o más bien multi-paradigma) que están diseñados con énfasis en objetos inmutables (Scala, por ejemplo).
sepp2k
@ sepp2k: Acostúmbrate. Los defensores de FP siempre están lanzando ejemplos extraños y artificiales que no tienen nada que ver con la codificación del mundo real. Es la única forma de hacer que los conceptos básicos de FP como la inmutabilidad forzada se vean bien.
Mason Wheeler
1
@Mason: ¿Eh? ¿No sería la mejor manera de hacer que la inmutabilidad se vea bien diciendo "Java (y C #, python, etc.) usa cadenas inmutables y funciona muy bien"?
sepp2k
1
@ sepp2k: Si las cadenas inmutables funcionan tan bien, ¿por qué las clases de estilo StringBuilder / StringBuffer siguen apareciendo por todas partes? Es solo otro ejemplo de una inversión de abstracción que se interpone en tu camino.
Mason Wheeler
2
Muchos lenguajes orientados a objetos le permiten crear objetos inmutables. Pero el concepto de vincular los métodos a la clase realmente lo desalienta desde mi punto de vista. El ejemplo de String no es realmente un ejemplo artificial. Cada vez que llamo a cualquier mehtod en Java, me arriesgo a ver si mis parámetros van a mutar dentro de esa función.
WuHoUnited
0

Creo que en la mayoría de los casos la abstracción clásica de OOP no cubre la complejidad de concurrencia. Por lo tanto, OOP (por su significado original) no excluye FP, y es por eso que vemos cosas como scala.

duyt
fuente
0

La respuesta depende del idioma. Los Lisps, por ejemplo, tienen el aspecto realmente correcto de que el código son datos: ¡los algoritmos que escribes son solo listas de Lisp! Almacena los datos de la misma manera que escribe el programa. Esta abstracción es a la vez más simple y más exhaustiva que OOP y le permite hacer cosas realmente interesantes (consulte macros).

Haskell (y un lenguaje similar, me imagino) tienen una respuesta completamente diferente: tipos de datos algebraicos. Un tipo de datos algebraicos es como una Cestructura, pero con más opciones. Estos tipos de datos proporcionan la abstracción necesaria para modelar datos; Las funciones proporcionan la abstracción necesaria para modelar algoritmos. Las clases de tipos y otras características avanzadas proporcionan un nivel aún más alto de abstracción sobre ambas.

Por ejemplo, estoy trabajando en un lenguaje de programación llamado TPL por diversión. Los tipos de datos algebraicos hacen que sea realmente fácil representar valores:

data TPLValue = Null
              | Number Integer
              | String String
              | List [TPLValue]
              | Function [TPLValue] TPLValue
              -- There's more in the real code...

Lo que esto dice, de una manera muy visual, es que un TPLValue (cualquier valor en mi idioma) puede ser un Nullo un Numbercon un Integervalor o incluso un Functioncon una lista de valores (los parámetros) y un valor final (el cuerpo )

A continuación, puedo usar clases de tipo para codificar algunos comportamientos comunes. Por ejemplo, podría hacer una TPLValueinstancia de lo Showque significa que se puede convertir en una cadena.

Además, puedo usar mis propias clases de tipos cuando necesito especificar el comportamiento de ciertos tipos (incluidos los que no implementé yo mismo). Por ejemplo, tengo una Extractableclase de tipo que me permite escribir una función que toma TPLValueay devuelve un valor normal apropiado. Por extractlo tanto, puede convertir a Numbera Integero a Stringa Stringsiempre Integery cuando Stringsean instancias de Extractable.

Finalmente, la lógica principal de mi programa está en varias funciones como evaly apply. Estos son realmente el núcleo: toman TPLValues y los convierten en más TPLValues, además de manejar el estado y los errores.

En general, las abstracciones que estoy usando en mi código Haskell son en realidad más poderosas que las que hubiera usado en un lenguaje OOP.

Tikhon Jelvis
fuente
Sí, tengo el amor eval. "¡Oye, mírame! ¡No necesito escribir mis propios agujeros de seguridad; tengo una vulnerabilidad de ejecución de código arbitraria integrada en el lenguaje de programación!" La combinación de datos con código es la causa raíz de una de las dos clases más populares de vulnerabilidades de seguridad de todos los tiempos. Cada vez que ves que alguien es pirateado debido a un ataque de inyección SQL (entre muchas otras cosas) es porque algún programador no sabe cómo separar adecuadamente los datos del código.
Mason Wheeler
evalno depende mucho de la estructura de Lisp: puede tenerlo evalen lenguajes como JavaScript y Python. El verdadero poder viene de escribir macros, que son básicamente programas que actúan sobre programas como datos y generan otros programas. Esto hace que el lenguaje sea muy flexible y que sea fácil crear abstracciones poderosas.
Tikhon Jelvis el
3
Sí, he escuchado las "macros son increíbles" hablar muchas veces antes. Pero nunca he visto un ejemplo real de una macro Lisp que haga algo que 1) sea práctico y que realmente le interese hacer en código del mundo real y 2) no se pueda lograr con la misma facilidad en cualquier lenguaje que admita funciones.
Mason Wheeler
1
@MasonWheeler en cortocircuito and. cortocircuitos or. let. let-rec. cond. defn. Ninguno de estos puede implementarse con funciones en lenguajes de orden aplicativo. for(lista de comprensiones). dotimes. doto.
1
@MattFenwick: OK, realmente debería haber agregado un tercer punto a mis dos anteriores: 3) no está integrado en ningún lenguaje de programación sensato. Porque ese es el único ejemplo macro realmente útil que he visto, y cuando dices "¡Mírame, mi lenguaje es tan flexible que puedo implementar mi propio cortocircuito and!" Escuché, "oye, mírame, ¡mi lenguaje está tan paralizado que ni siquiera viene con un cortocircuito andy tengo que reinventar la rueda para todo !"
Mason Wheeler
0

La oración citada ya no tiene ninguna validez, por lo que puedo ver.

Los lenguajes OO contemporáneos no pueden abstraer sobre tipos cuyo tipo no es *, es decir, se desconocen los tipos de tipo superior. Su sistema de tipos no permite expresar la idea de "algún contenedor con elementos Int, que permite mapear una función sobre los elementos".

Por lo tanto, esta función básica como Haskells

fmap :: Functor f => (a -> b) -> f a -> f b 

no se puede escribir fácilmente en Java *), por ejemplo, al menos no de forma segura. Por lo tanto, para obtener una funcionalidad básica, debe escribir muchas repeticiones, porque necesita

  1. Un método para aplicar una función simple a elementos de una lista
  2. Un método para aplicar la misma función simple a elementos de una matriz
  3. un método para aplicar la misma función simple a los valores de un hash,
  4. .... establecer
  5. .... árbol
  6. ... 10. pruebas unitarias para el mismo

Y, sin embargo, esos cinco métodos son básicamente el mismo código, más o menos. En contraste, en Haskell, necesitaría:

  1. Una instancia de Functor para lista, matriz, mapa, conjunto y árbol (en su mayoría predefinidos, o puede ser derivada automáticamente por el compilador)
  2. la función simple

Tenga en cuenta que esto no va a cambiar con Java 8 (solo que uno puede aplicar funciones más fácilmente, pero luego, exactamente, se materializará el problema anterior. Mientras no tenga funciones de orden superior, lo más probable es que ni siquiera capaz de comprender para qué son buenos los tipos de clase superior).

Incluso los nuevos lenguajes OO como Ceilán no tienen tipos de tipo superior. (Le pregunté a Gavin King últimamente, y él me dijo que no era importante en este momento). Sin embargo, no sé sobre Kotlin.

*) Para ser justos, puede tener una interfaz Functor que tenga un método fmap. Lo malo es que no puede decir: Hola, sé cómo implementar fmap para la clase de biblioteca SuperConcurrentBlockedDoublyLinkedDequeHasMap, querido compilador, acepte que de ahora en adelante, todos los SuperConcurrentBlockedDoublyLinkedDequeHasMaps son Functores.

Ingo
fuente
FTR: la typechecker Ceilán y JavaScript backend hacen de soporte superior mano tipos (y también más altos rangos tipos). Se considera una característica "experimental". Sin embargo, nuestra comunidad ha tenido problemas para encontrar aplicaciones prácticas para esta funcionalidad, por lo que es una pregunta abierta si alguna vez será una parte "oficial" del lenguaje. Yo no espero que sea apoyado por el back-end de Java en algún momento.
Gavin King
-2

Cualquiera que haya programado en dBase sabría cuán útiles fueron las macros de una sola línea para hacer código reutilizable. Aunque no he programado en Lisp, he leído de muchos otros que juran por compilar macros de tiempo. La idea de inyectar código en su código en tiempo de compilación se utiliza de forma simple en cada programa C con la directiva "include". Debido a que Lisp puede hacer esto con un programa de Lisp y porque Lisp es altamente reflexivo, obtienes inclusiones mucho más flexibles.

Cualquier programador que simplemente tome una cadena de texto arbitraria de la web y la pase a su base de datos no es un programador. Del mismo modo, cualquiera que permita que los datos del "usuario" se conviertan automáticamente en código ejecutable es obviamente estúpido. Eso no significa que permitir que los programas manipulen datos en tiempo de ejecución y luego ejecutarlos como código es una mala idea. Creo que esta técnica será indispensable en el futuro, ya que tendrá un código "inteligente" que realmente escribirá la mayoría de los programas. Todo el "problema de datos / código" o no es una cuestión de seguridad en el idioma.

Uno de los problemas con la mayoría de los idiomas es que fueron creados para que una sola persona fuera de línea ejecute algunas funciones por sí mismo. Los programas del mundo real requieren que muchas personas tengan acceso en todo momento y al mismo tiempo desde múltiples núcleos y múltiples grupos de computadoras. La seguridad debería ser parte del lenguaje más que del sistema operativo y en un futuro no muy lejano lo será.

David Clark
fuente
2
Bienvenido a programadores. Considere atenuar la retórica en su respuesta y respaldar algunas de sus afirmaciones con referencias externas.
1
Cualquier programador que permita que los datos del usuario se conviertan automáticamente en código ejecutable es obviamente ignorante . No estupido. Hacerlo de esa manera es a menudo fácil y obvio, y si no saben por qué es una mala idea y que existe una mejor solución, no se les puede culpar por hacerlo. (Sin embargo, cualquiera que siga haciéndolo después de que se les haya enseñado que hay una mejor manera es obviamente estúpido).
Mason Wheeler