clojure core.async

¿No es core.async contrario a los principios de Clojure?



(6)

He visto a muchos programadores de Clojure entusiasmados con la nueva biblioteca core.async y, aunque parece muy interesante, estoy teniendo dificultades para ver cómo se ajusta a los principios de Clojure, por lo que tengo estas preguntas:

  1. Utiliza el estado mutable en todas partes, como sugieren los nombres de las funciones al tener un signo de exclamación, como alt !, put !,> y otros. Si coloca o toma un valor de un canal, ese canal se modifica inplace. ¿No es contrario a la filosofía de Clojure preferir estructuras de datos inmutables, funciones puras, etc.? ¿O está hecho core.async para ser utilizado solo donde las cosas mutables no pueden ser evitadas en absoluto?
  2. Como "ir" es una macro (modificando así la estructura del código) y asegura "<!" se usa directamente en un go-block, no es posible usar "<!" dentro de otra función, como esta:

    (defn take-and-print [c] (println (<! c))) (def ch (chan 1)) (>!! ch 123) (go (take-and-print ch)) Assert failed: <! used not in (go ...) block

    Me parece que esto evita la simplicidad y la capacidad de compilación. ¿Por qué no es un problema?

  3. Tal vez como consecuencia de los dos números anteriores, una gran cantidad de código con core.async utiliza construcciones de bajo nivel como bucle / recurrencia en lugar de mapa / filtro / reducir. ¿No es un paso atrás?

¿Dónde me estoy perdiendo el punto?

Gracias por adelantado.


  1. es al revés, Core.async solo se puede usar en sistemas donde la inmutabilidad es la norma . Entonces, los principios de Clojure permiten core.async en lugar de lo inverso.

  2. Esta es una limitación, ocurre en otro lugar también en clojure, la limitación de las funciones anónimas que no se componen con el símbolo % parece presentar al menos la misma idea. No es que encontrar otro caso de limitación lo haga mejor, por supuesto.

  3. No he visto esto yo mismo, aunque esto sería un paso atrás si intentas tomar un código que es simple, y limpio cuando se expresa de una manera y luego expresarlo de una manera que es ... no de esa manera ... .


Cada programa tiene dos partes, una parte que siempre es no funcional, datos de conversación, escupir y así sucesivamente. Para esta parte que sabemos tiene core.async, es cierto que core.async tiene cosas mutables, pero tenga en cuenta dos cosas. El estado de los canales es administrado básicamente por la biblioteca. Las cosas que le pones son, lo que podría llamar flowstate. Lo que significa que cuando pones algo como un canal, no esperas regresar, o incluso cambiarlo.

Core.async es bueno para administrar esta parte de tu programa. Por lo demás, toda la transformación y el cálculo en sus datos, clojure hace todo lo posible para brindarle buenas herramientas para hacerlo funcionalmente.

Me parece que esto evita la simplicidad y la capacidad de compilación. ¿Por qué no es un problema?

Hay dos mundos, la sincronización y el mundo asincrónico. ¡Puedes poner cosas, o sacar cosas de la historia asíncrona con put! ¡y tomar! pero aparte de eso (y tal vez alguna otra función) estos mundos están separados el uno del otro. Clojure no quiere convertirse en un lenguaje completamente asíncrono. Las funciones y la transformación de datos son lo que necesita ser composable.

Tal vez como consecuencia de los dos números anteriores, una gran cantidad de código con core.async utiliza construcciones de bajo nivel como bucle / recurrencia en lugar de mapa / filtro / reducir. ¿No es un paso atrás?

La operación así por canales será posible. Core.async todavía es joven y no se han escrito todas las construcciones y funciones posibles.

Pero, en general, si tiene grandes transformaciones de datos que realmente no desea hacer en un mundo asincrónico, quiere tenerlas en una colección y luego lanzar algo así como el framework de reducciones.

Lo principal que hay que entender es esto, core.async no es el nuevo estándar, es solo una cosa más que te ayuda a programar. A veces necesita STM, a veces Agentes, a veces CSP depende y clojure quiere darle todas las opciones.

Una de las razones por las que la gente como core.async es porque ayuda con algunas cosas que de otra manera son realmente difíciles de manejar, como lidiar con devoluciones de llamadas.


La limitación de la macro go (su localidad) también es una característica: impone la localidad del código fuente de las operaciones con estado.


La primera preocupación: sí, las operaciones centrales son efectos secundarios. Sin embargo, los canales no tienen los problemas normalmente asociados con las referencias mutables ya que no representan un "lugar": los canales son opacos, no puedes inspeccionarlos, de hecho, ni siquiera puedes consultar si un canal está cerrado o no más allá de leer nulo.

La segunda preocupación: hacer algo más que un rendimiento poco profundo significaría una transformación completa del programa. Esta es una compensación y creo que es razonable. El nivel de composición es que los canales no se bloquean y componen perfectamente.

La última preocupación es que puedes hacer fácilmente un mapa de estilo Rx / filtrar / reducir operaciones a través de canales y la gente ya lo ha hecho.


Rich Hickey dijo en una de las conferencias blip.tv que Clojure es "85% funcional". Me gusta ver core.async como una parte del otro 15%. Core.async es ideal para la interacción sólida del usuario entre otras cosas que se podrían haber hecho mediante promesas, retrasos y otras cosas, probablemente de una manera más desordenada.


quizás una posible solución para usar (<! c) fuera de ir macro podría hacerse con macro y su tiempo de expansión macro:

Este es mi ejemplo:

(ns fourclojure.asynco (require [clojure.core.async :as async :refer :all])) (defmacro runtime--fn [the-fn the-value] `(~the-fn ~the-value) ) (defmacro call-fn [ the-fn] `(runtime--fn ~the-fn (<! my-chan)) ) (def my-chan (chan)) (defn read-channel [the-fn] (go (loop [] (call-fn the-fn) (recur) ) )) (defn paint [] (put! my-chan "paint!") )

Y para probarlo:

(read-channel print) (repeatedly 50 paint)

He intentado esta solución en una búsqueda anidada y también funciona. Pero no estoy seguro de si podría ser una ruta correcta