¿Por qué los programas funcionales tienen una correlación entre el éxito y la corrección de la compilación?

12

He estado tendiendo hacia la programación funcional durante 4 años, desde que empecé a trabajar con LINQ. Recientemente, escribí un código C # funcional puro y noté, de primera mano, lo que he estado leyendo sobre programas funcionales, que una vez que se compilan tienden a ser correctos.

Intenté señalar por qué este es el caso, pero no lo he logrado.

Una suposición es que al aplicar los principios de OO, tiene una "capa de abstracción" que no está presente en los programas funcionales y esta capa de abstracción hace posible que los contratos entre objetos sean correctos mientras que la implementación es incorrecta.

¿Alguien ha pensado en esto y ha encontrado la razón abstracta subyacente de la correlación entre el éxito de la compilación y la corrección del programa en la programación funcional?

    
pregunta Aaron Anodide 19.11.2014 - 05:55

5 respuestas

12

Puedo escribir esta respuesta como alguien que prueba muchas cosas, así que para mí, la corrección no es solo lo que funciona, sino lo que funciona y es fácil de probar.

En muchos sentidos, la programación funcional es más restrictiva que la imperativa. Después de todo, ¡nada le impide mutar nunca una variable en C! De hecho, la mayoría de las características en los idiomas de PF son sencillas de hablar en términos de solo unas pocas características básicas. ¡Todo se reduce a las lambdas, la aplicación de funciones y la coincidencia de patrones!

Sin embargo, ya que hemos pagado al gaitero por adelantado, tenemos mucho menos que tratar y tenemos muchas menos opciones para que las cosas salgan mal. Si eres un fan de 1984, ¡la libertad es de hecho la esclavitud! Al utilizar 101 trucos diferentes para un programa, ¡tenemos que razonar sobre las cosas como si cualquiera de estas 101 cosas pudieran suceder! Eso es muy difícil de hacer, ya que resulta :)

Si comienzas con tijeras de seguridad en lugar de una espada, correr es moderadamente menos peligroso.

Ahora nos fijamos en tu pregunta: ¿cómo encaja todo esto en el "compila y funciona?" fenómenos. Creo que una gran parte de esto es la misma razón por la que es fácil probar el código. Después de todo, cuando escribes software estás construyendo alguna prueba informal de que es correcto. Debido a esto, lo que cubren sus pruebas naturales de mano y la propia noción de corrección (tipografía) de los compiladores es bastante.

A medida que agrega funciones e interacciones complicadas entre ellas, lo que no está controlado por el sistema de tipos aumenta. Sin embargo, su capacidad para construir pruebas informales no parece mejorar! Esto significa que hay más cosas que pueden pasar a través de su inspección inicial y deben detectarse más adelante.

    
respondido por el jozefg 19.11.2014 - 06:10
12
  

¿la razón abstracta subyacente de la correlación entre el éxito de la compilación y la corrección del programa en la programación funcional?

Estado mutable.

Los compiladores verifican las cosas estáticamente. Se aseguran de que su programa esté bien formado y el sistema de tipos proporciona un mecanismo para tratar de garantizar que se permitan los valores correctos en los lugares correctos. El sistema de tipos también trata de garantizar que se permita el tipo correcto de semántica en el tipo correcto de lugares.

Tan pronto como su programa introduce el estado, esta última restricción se vuelve menos útil. No solo debe preocuparse por los valores correctos en los lugares correctos, sino que también debe tener en cuenta el cambio de valor en puntos arbitrarios de su programa. Debe tener en cuenta que la semántica de su código cambia junto con ese estado.

Si está haciendo bien la programación funcional, no hay (o muy poco) estado mutable.

Hay un cierto debate sobre la causa aquí: si los programas sin estado funcionan después de la compilación con más frecuencia porque el compilador puede detectar más errores o si los programas sin estado funcionan después de la compilación con mayor frecuencia porque ese estilo de programación produce menos errores. p>

Es probable que sea una mezcla de ambos en mi experiencia.

    
respondido por el Telastyn 19.11.2014 - 17:36
7

En pocas palabras, las restricciones significan que hay menos formas correctas de unir las cosas, y las funciones de primera clase hacen que sea más fácil eliminar cosas como las estructuras de bucle. Tome el bucle de esta respuesta , por ejemplo:

for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
    String string = iterator.next();
    if (string.isEmpty()) {
        iterator.remove();
    }
}

Esta es la única forma segura e imperativa en Java de eliminar un elemento de una colección mientras está iterando a través de él. Hay muchas maneras que parecen muy cercanas, pero están equivocadas. Las personas que no conocen este método a veces pasan por formas complicadas de evitar el problema, como iterar a través de una copia.

No es terriblemente difícil hacer este genérico, por lo que funcionará en más que solo colecciones de Strings , pero sin funciones de primera clase, no puede reemplazar el predicado (la condición dentro de if ), por lo que este código tiende a copiarse, pegarse y modificarse ligeramente.

Combina funciones de primera clase que te dan la habilidad para pasar el predicado como un parámetro, con la restricción de inmutabilidad que lo hace muy molesto si no lo haces, y obtienes bloques de construcción simples como filter , como en este código de Scala que hace lo mismo:

list filter (!_.isEmpty)

Ahora piense en lo que el sistema de tipos verifica en su caso, en el momento de la compilación en el caso de Scala, pero estas comprobaciones también las realizan los sistemas de tipos dinámicos la primera vez que lo ejecuta:

  • list debe ser algún tipo de tipo que admita el método filter , es decir, una colección.
  • Los elementos de list deben tener un método isEmpty que devuelve un valor booleano.
  • La salida será una colección (potencialmente) más pequeña con el mismo tipo de elementos.

Una vez que se han comprobado esas cosas, ¿qué otras formas quedan para que el programador las arruine? Accidentalmente olvidé el ! , lo que causó una falla de prueba extremadamente obvia. Ese es prácticamente el único error que se puede cometer, y solo lo hice porque estaba traduciendo directamente del código que probó la condición inversa.

Este patrón se repite una y otra vez. Las funciones de primera clase le permiten refactorizar las cosas en pequeñas utilidades reutilizables con una semántica precisa, restricciones como la inmutabilidad le dan el ímpetu para hacerlo, y la verificación de los parámetros de esas utilidades deja poco espacio para arruinarlas.

Por supuesto, todo esto depende de que el programador sepa que la función de simplificación como filter ya existe, y de poder encontrarla, o de reconocer el beneficio de crear uno. Intente implementar esto usted mismo en todas partes utilizando solo la recursión de cola, y está de vuelta en el mismo barco de complejidad que la versión imperativa, pero peor. Solo porque puedas escribirlo de manera muy simple, no significa que la versión simple sea obvia.

    
respondido por el Karl Bielefeldt 20.11.2014 - 06:16
2

No creo que haya una correlación significativa entre la compilación de programación funcional y la corrección del tiempo de ejecución. Puede haber alguna correlación entre la compilación estática tipificada y la corrección del tiempo de ejecución, ya que al menos puede tener los tipos correctos, si no está lanzando.

El aspecto del lenguaje de programación que de alguna manera puede correlacionar la compilación exitosa con la corrección del tipo de tiempo de ejecución, como usted describe, es la tipificación estática, e incluso entonces, solo si no está debilitando el corrector de tipos con conversiones que solo se pueden afirmar en tiempo de ejecución ( en entornos con valores o lugares fuertemente tipados, por ejemplo, Java o .Net) o en absoluto (en entornos donde la información de tipo se pierde o con poca escritura, por ejemplo, C y C ++).

Sin embargo, la programación funcional en sí misma puede ayudar de otras maneras, como evitar los datos compartidos y el estado mutable.

Ambos aspectos juntos pueden tener una correlación significativa en la corrección, pero debe ser consciente de que no tener errores de compilación y de tiempo de ejecución no dice nada, estrictamente hablando, acerca de la corrección en un sentido más amplio, ya que en el programa hace lo que se supone que debe hacer y falla rápidamente sobre una entrada no válida o un error de tiempo de ejecución incontrolable. Para eso, necesita reglas de negocios, requisitos, casos de uso, aserciones, pruebas unitarias, pruebas de integración, etc. Al final, al menos en mi opinión, brindan mucha más confianza que la programación funcional, la escritura estática o ambas.

    
respondido por el acelent 19.11.2014 - 17:23
2

Explicación para los gerentes:

Un programa funcional es como una máquina grande donde todo está conectado, tubos, cables. [Un automóvil]

Un programa de procedimiento es como un edificio con salas que contienen una máquina pequeña, que almacena productos parciales en contenedores y obtiene productos parciales de otros lugares. [Una fábrica]

Entonces, cuando la máquina funcional ya encaja: está destinada a producir algo. Si se ejecuta un complejo de procedimientos, es posible que haya supervisado efectos específicos, introducido caos, no garantizado el funcionamiento. Incluso si tiene una lista de verificación de todo lo que se integra correctamente, hay tantos estados, situaciones posibles (productos parciales alrededor, cubos desbordados, faltantes), que las garantías son difíciles de dar.

Pero en serio, el código de procedimiento no especifica tanto la semántica del resultado deseado como el código funcional. Los programadores de procedimientos pueden alejarse más fácilmente mediante códigos y datos circunstanciales e introducir varias formas de hacer una cosa (algunas de ellas imperfectas). Normalmente se crean datos extraños. Los programadores funcionales pueden tardar más tiempo cuando el problema se vuelve más complejo.

Un lenguaje funcional fuerte y tipificado aún puede hacer mejores datos y análisis de flujo. Con un lenguaje de procedimiento, el objetivo de un programa a menudo tiene que definirse fuera del programa, como un análisis de corrección formal.

    
respondido por el Joop Eggen 19.11.2014 - 18:46

Lea otras preguntas en las etiquetas