15

Muchas veces, me he encontrado con declaraciones del formulario X hace/no compone bien.¿Qué significa para algo "componer bien"?

puedo recordar algunos casos que he leído recientemente:

  • macros no componen así (de contexto: clojure)
  • cerraduras no componen así (contexto: clojure)
  • La programación imperativa no es buena ... etc.

Quiero entender las implicaciones de la capacidad de compilación en términos de diseño/lectura/escritura de código? Los ejemplos serían agradables.

+3

posible duplicado de [¿Qué significa capacidad de compilación en el contexto de la programación funcional?] (Http://stackoverflow.com/questions/2887013/what-does-composability-mean-in-context-of-functional- programación) – missingfaktor

Respuesta

10

"Componer" funciones básicamente significa simplemente unir dos o más funciones juntas para hacer una gran función que combina su funcionalidad de una manera útil. Esencialmente, usted define una secuencia de funciones y canaliza los resultados de cada uno en el siguiente, dando finalmente el resultado de todo el proceso. Clojure proporciona la función comp para hacer esto por usted, también puede hacerlo a mano.

Las funciones que puede encadenar con otras funciones de forma creativa son más útiles en general que las funciones a las que solo puede llamar en determinadas condiciones. Por ejemplo, si no tuviéramos la función last y solo tuviéramos las funciones de lista Lisp tradicionales, podríamos definir fácilmente last como (def last (comp first reverse)). Mira eso, ni siquiera necesitamos defn o mencionar cualquier argumento, porque simplemente estamos conectando el resultado de una función a otra. Esto no funcionaría si, por ejemplo, reverse tomara la ruta imperativa de modificar la secuencia in situ. Las macros también son problemáticas porque no puede pasarlas a funciones como comp o apply.

+1

Un ejemplo de macros que no se componen bien. Digamos que queríamos hacer algo como esto. (reduce y [true true false true]). Desafortunadamente porque y es una macro no podemos pasarla para reducirla. – Glen

2

La composición (en el contexto que describe en un nivel funcional) es típicamente la capacidad de pasar una función a otra limpiamente y sin procesamiento intermedio. Tal ejemplo de composición está en std :: cout en C++:

cout < < cada < < artículo < < enlaces < < en;

Ese es un simple ejemplo de composición que realmente no se "parece" a la composición.

Otro ejemplo con una forma más visible de composición:

foo (bar (baz()));

Wikipedia Link

composición es útil para facilitar la lectura y la compacidad, sin embargo encadenamiento grandes colecciones de funciones de enclavamiento que potencialmente pueden devuelven códigos de error o de datos no deseados pueden ser peligrosos (por eso lo mejor es minimizar el código de error o devuelto nulo valores)

Siempre que sus funciones utilicen excepciones, o alternativamente devuelvan null objects, puede minimizar el requisito de bifurcación (if) en los errores y maximizar el potencial de composición de su código sin ningún riesgo adicional.


objeto composition (vs inheritance) es un tema aparte (y no lo que están pidiendo, pero comparte el nombre). Es uno de contención para derivar la jerarquía de objetos en oposición a la herencia directa.

2

"Bang for the Buck" - componer bien implica una alta proporción de expresividad por regla de composición. Cada macro presenta sus propias reglas de composición. Cada estructura de datos personalizada hace lo mismo. Las funciones, especialmente las que usan estructuras de datos generales, tienen muchas menos reglas.

Asignación y otros efectos secundarios, especialmente la concurrencia wrt tienen aún más reglas.

2

Piense cuando escribe funciones o métodos. Usted crea un grupo de funcionalidades para realizar una tarea específica. Al trabajar en un lenguaje orientado a objetos, agrupa su comportamiento en torno a las acciones que cree que realizará una entidad distinta en el sistema. Los programas funcionales se alejan de esto alentando a los autores a agrupar la funcionalidad de acuerdo con una abstracción. Por ejemplo, la biblioteca Clojure Ring comprende un grupo de abstracciones que cubren el enrutamiento en aplicaciones web.

Ring es composable donde las funciones que describen rutas en el sistema (rutas) se pueden agrupar en funciones de orden superior (middlewhere). De hecho, Clojure es tan dinámico que es posible (y se lo alienta) crear patrones de rutas que puedan crearse dinámicamente en tiempo de ejecución. Esta es la esencia de la compibilidad, en lugar de idear patrones que resuelven un problema específico, se enfoca en patrones que generan soluciones para cierto tipo de problemas. Los constructores y los generadores de códigos son solo dos de los patrones comunes utilizados en la programación funcional. La programación de funciones es el arte de patrones que generan otros patrones (y así sucesivamente).

La idea es resolver un problema en su nivel más básico y luego idear patrones o grupos de las funciones de nivel más bajo que resuelven el problema. Una vez que comienzas a ver patrones en el nivel más bajo, descubres la composición. A medida que las personas descubren patrones de segundo orden en grupos de funciones, pueden comenzar a ver un tercer nivel. Y así sucesivamente ...

1

En el contexto de clojure, this comment aborda ciertos aspectos de la capacidad de compilación. En general, parece surgir cuando las unidades del sistema hacen bien una cosa, no requieren que otras unidades entiendan sus elementos internos, eviten los efectos secundarios y acepten y devuelvan las estructuras de datos generalizadas del sistema. Todo lo anterior se puede ver en el ejemplo C++ de M2tM.

3

La composición en la programación significa ensamblar piezas más grandes de las más pequeñas.

  • La composición de funciones unarias crea una función unaria más complicada encadenando otras más simples.
  • La composición de las construcciones de flujo de control coloca las construcciones de flujo de control dentro de otras construcciones de flujo de control.
  • La composición de las estructuras de datos combina múltiples estructuras de datos más simples en una más complicada.

Idealmente, una unidad compuesta funciona como una unidad básica y usted, como programador, no necesita conocer la diferencia. Si las cosas no alcanzan el ideal, si algo no funciona bien, su programa compuesto puede no tener el comportamiento combinado (previsto) de sus piezas individuales.

Supongamos que tengo un código C simple.

void run_with_resource(void) { 
    Resource *r = create_resource(); 
    do_some_work(r); 
    destroy_resource(r); 
}

C facilita el razonamiento de la composición sobre el flujo de control a nivel de funciones.No me tiene que importar lo que realmente sucede dentro de do_some_work(); Sé simplemente mirando esta pequeña función que cada vez que se crea un recurso en la línea 2 con create_resource(), eventualmente será destruido en la línea 4 por destroy_resource().

Bueno, no del todo. ¿Qué ocurre si create_resource() adquiere un bloqueo y destroy_resource() lo libera? Entonces tengo que preocuparme si do_some_work adquiere el mismo bloqueo, lo que evitaría que la función termine. ¿Qué ocurre si do_some_work() llama a longjmp() y omite por completo el final de mi función? Hasta que sepa lo que sucede en do_some_work(), no podré predecir el flujo de control de mi función. Ya no tenemos la composicionalidad: ya no podemos descomponer el programa en partes, razonar acerca de las partes de forma independiente y llevar nuestras conclusiones de regreso a todo el programa. Esto hace que el diseño y la depuración sean mucho más difíciles y es por eso que a la gente le importa si algo se compone bien.

1

del composability, aplicado a las funciones, significa que las funciones son más pequeñas y bien definido, por lo tanto fácil de integrar en otras funciones (que he visto esta idea en el libro "la alegría de clojure")

el concepto puede aplicarse a otras cosas que se supone que se componen en otra cosa.

el propósito de composibilidad es reutilizar. por ejemplo, una función bien-build (componibles) es más fácil de reutilizar

macros no son tan bien componibles porque no se puede pasar como parámetros

bloqueo son una mierda porque no se puede Realmente les da nombres (defínalos bien) o reutilícelos. usted acaba de hacer InPlace

lenguajes imperativos no son tan componibles debido a que (algunos de ellos, al menos) no tienen cierres . si quieres que la funcionalidad pase como parámetro, estás jodido. tienes que construir un objeto y pasarlo; descargo de responsabilidad aquí: esta última idea no estoy totalmente convencido es cierto, por lo tanto, investigar más antes de tomar por sentado

otra idea en los lenguajes imperativos es que no componen así porque implican estado (de wikipedia knowledgebase :) "Programación imperativa: describe el cálculo en términos de enunciados que cambian un estado de programa").

estado no funciona bien porque aunque haya dado un "algo" específico en la entrada, ese "algo" genera una salida de acuerdo con su estado. diferente estado interno, diferente comportamiento. y así puedes decir adiós a lo que esperabas que sucediera.

con estado, depende mucho de saber cuál es el estado actual de un objeto ... si desea predecir su comportamiento. más cosas a tener en la parte posterior de su mente, menos componibles (recuerda bien definidos o "pequeña y simple", como en "fácil de usar"??)

ps: el pensamiento de clojure aprendizaje ¿eh? investigando ...? bien por usted !: P

Cuestiones relacionadas