2011-02-10 6 views
10

Soy nuevo en Clojure y encuentro las patas de mi mar. Me preguntaba si se considera una buena o mala práctica, desde un punto de vista de programación funcional, colocar funciones en los mapas de Clojure y luego pasar esos mapas como cuasi-objetos, como suele hacerse en JavaScript. Las explicaciones también serán apreciadas.¿Es una mala idea poner funciones en los mapas de Clojure, como en JavaScript?

+0

Acabo de hacer una pregunta general relacionada con esto que puede encontrarle útil: http://stackoverflow.com/questions/4969593/achieving-polymorphism-in-functional-programming – mikera

+0

Gracias por ese enlace – dan

Respuesta

7

Los métodos multimétodos de Clojure son esencialmente mapas de funciones, por lo que no, no es una mala idea.

6

Sería malo debido a varias razones:

  1. No es necesario - En JavaScript, lo estamos haciendo a utilizar mapas simples como objetos Java como. En Clojure, si realmente quieres hacerlo, puedes usar un objeto Java real a través de la interoperabilidad de Java.
  2. Ya sea que usted está poniendo en funciones de mapas o si está utilizando objetos reales de Java, que está haciendo la programación orientada a objetos en un entorno de lenguaje que fue diseñado para la programación funcional. La primera preocupación que viene con él es que su código sólo would't encaja en. Se vería extraño, torpe y tal vez incluso demasiado complicado en Clojure. En este punto, no estoy diciendo que OOP sea necesariamente malo, solo que es mejor escribir OOP en los idiomas que fueron diseñados para ello.
  3. El punto más importante en contra de tal estilo de codificación es que el programa OOP-como inevitablemente depender de objetos que tienen un cierto estado, y sus funciones miembro (un poco como métodos) que alteran ese estado. Si estás usando state, entonces tus funciones no son puras y no puedes usar todas las bondades funcionales como la fácil paralelización.

En resumen, si usa Clojure para hacer OOP, solo se sentirá molesto. Puede hacer OOP más fácilmente en toneladas de otros idiomas, como decir Groovy. Si desea usar Clojure, hágalo de manera funcional.

Hasta ahora he escrito no hacerlo y por qué no hacerlo. Ahora me puede preguntar: Entonces, ¿cómo debería escribir funciones en Clojure?

Escriba las funciones que toman su estructura de datos (es decir, un mapa, lista, objeto ... lo que sea) como parámetro. Así, en lugar de:

foo.bar(); 

Se podría definir así:

(defn bar [foo] 
    ;stuff 
    ) 

y lo llaman así:

(bar foo) 

Ahora, para una función pura bar, foo objeto es sin cambios después de la evaluación de la función, y usted no tiene un estado compartido del objeto del que preocuparse si decide paralelizar su código, lo que tendría si estuviera haciendo cosas de la manera OOP.

Además, puede parecer una pequeña diferencia, pero tenga en cuenta que la definición de la función bar es una entidad totalmente independiente de la estructura de datos foo. Además, foo contiene solo los datos, no el comportamiento; todo el comportamiento está en la función. Esta separación te da una libertad mucho mayor al codificar.

+0

Es un corolario de lo que ¿Estás diciendo que uno también podría codificar en _JavaScript_ de una manera puramente funcional? En ese caso, me pregunto por qué el paradigma O-O-esque domina en JavaScript. – dan

+0

Sí. Es posible hacer programación funcional en JavaScript. Sospecho que el paradigma OO se importó a JavaScript bajo la influencia de otros lenguajes convencionales como Java. Además, tenga en cuenta que algunas de las buenas razones para usar FP, como la posibilidad de pararelización fácil, no son aplicables a JavaScript, ya que de todos modos es de un solo hilo. –

+0

@dan: Ah, y el hecho de que algunos navegadores tienen un tamaño de pila de llamadas bastante limitado realmente elimina cualquier posibilidad de recurrencia razonable. Esa es una buena razón para no usar FP en JavaScript. –

2

Si realmente necesita hacer programación orientada a objetos en Clojure hay un par de maneras de hacerlo.

La primera forma es usar la familia de macros deftype, defrecord y defprotocol.

La segunda forma es usar multimedios, combinados con un mapa o tipo de registro para el almacenamiento de datos.

La tercera forma es utilizar el patrón de diseño universal, expuesto por Steve Yegge, o una más Clojure introducción específica se puede encontrar en el libro de Chris Houser y The Joy of Clojure Michael Fogus'.

El tercer enfoque es muy similar a lo que esperaría ver en JavaScript, y el primer enfoque, aunque no se considera como OOP en un sentido tradicional, es el más común en Clojure idiomático "moderno".

+0

Ninguno de los enfoques que enumeró implica poner el objeto función en mapas/objetos para emular los métodos. Todos parecen estar relacionados con el objeto con datos separados de las funciones con el comportamiento. –

+0

Aunque no es lo que el cartel original tenía en mente, los protocolos definitivamente almacenan métodos en un mapa. podría, sin embargo, estar recordando mal la alegría de la sección de clojure. – apg

8

Hazlo, si hace que tu código sea más corto o más fácil de entender, o probar, o depurar.

O si simplemente lo desea. Confía en tu juicio

4

Funciona - y en cierto sentido es natural si se tiene en cuenta funciones como objetos de primera clase en el idioma (como todos los programadores funcionales debe!)

Sin embargo - que las necesidades de atención real, porque lo que está haciendo, básicamente, es código de intercalación con datos. Es más bien como mezclar su modelo de datos con el código de presentación en MVC. Sí, puede haber algunas situaciones en las que tiene sentido, pero el principio general sería evitarlo.

El estilo poco a poco estoy converge hacia después de un año de Clojure es:

  • Algunos de alto nivel refs/átomos/vars de clara de administración de estado mutables
  • (felicitaciones a Rich Hickey!)
  • Grandes estructuras de datos inmutables anidadas dentro de las referencias de nivel superior
  • Funciones puras para manejar casi todo el procesamiento
  • Algunas funciones que terminan en! para esos efectos secundarios desagradables, usado con moderación
  • ¡Montones y montones de pruebas! Esto es realmente importante porque con el tipado dinámico y las estructuras de datos muy flexibles necesita probar casi todas sus suposiciones
  • Java (¡lamentablemente!) Para optimizar secciones de código sensibles al rendimiento, generalmente definiendo una clase Java inmutable y ligera que funciona bien en las estructuras de datos de Clojure

Esto resuelve la mayoría de las cosas bajo el sol, la única cosa que todavía estoy tratando de averiguar es la mejor manera de crear objetos con un comportamiento altamente polimórficos. Principales opciones que puedo ver son:

  • Use protocolos con DEFTYPE/defrecord - de alto rendimiento, idiomática Clojure, pero sólo se consiguen despacho único del tipo que es insuficiente para algunas situaciones altamente polimórficos
  • crear funciones que se comportan de una forma polimórfica por montones de macros/composición de función de orden superior - funciona, pero puede ser muy complicada/compleja para mantener
  • ¡Ponga las funciones dentro de los mapas! Sí, se siente mal, ¡pero funciona!

Todavía no he encontrado el camino a seguir ..... pero tengo la corazonada de que las funciones pueden colarse en los mapas un poco más en el futuro ....

1

Los espacios de nombre son, literalmente, mapas de funciones. Es mucho más sencillo usarlos para organizar tus funciones. Sin embargo, si se encuentra con una limitación de espacios de nombres para su caso de uso, podría considerar poner funciones en un mapa.

Como de costumbre cuando se sale de los caminos trillados, solo asegúrese de pensar por qué no va por la ruta obvia.

Cuestiones relacionadas