Una especie de pregunta de novato de programación funcional aquí:
He estado leyendo las transcripciones de algunas de las charlas de Rich Hickey, y en varias de sus más conocidas, recomienda usar colas como una alternativa a que las funciones se llamen entre sí. (Por ejemplo, en diseño, composición y rendimiento y en Simple Made Easy ).
No entiendo esto, en varios aspectos:
¿Está hablando de poner datos en una cola y luego hacer que cada función los use? Entonces, en lugar de que la función A llame a la función B para llevar a cabo su propio cálculo, solo tenemos que la función B abofetea su salida en una cola y luego la función A la toma. O, alternativamente, ¿estamos hablando de poner funciones en una cola y luego aplicarlas sucesivamente a los datos (seguramente no, porque eso implicaría una mutación masiva, ¿verdad? ¿Y también la multiplicación de colas para funciones de arias múltiples, o como árboles o algo así? )
¿Cómo hace eso para simplificar las cosas? Mi intuición sería que esta estrategia crearía más complejidad, porque la cola sería una especie de estado, y luego debe preocuparse "¿y si alguna otra función se coló y puso algunos datos encima de la cola?"
Una respuesta a una pregunta de implementación sobre SO sugiere que la idea es crear un montón de colas diferentes. Entonces cada función pone su salida en su propia cola (??). Pero eso también me confunde, porque si está ejecutando una función una vez, entonces ¿por qué necesita una cola para su salida cuando podría tomar esa salida y darle un nombre como (var, átomo, entrada en una gran tabla hash, lo que sea). Por el contrario, si una función se ejecuta varias veces y pega su salida en una cola, entonces se ha infligido nuevamente el estado y debe preocuparse por el orden en que se llama todo, las funciones posteriores se vuelven menos puras, etc.
Claramente no entiendo el punto aquí. ¿Alguien puede explicar un poco?
fuente
Job
objeto genérico , lo inserta en una cola y hace que uno o más subprocesos de trabajo trabajen en esa cola. ElJob
entonces despacha másJob
s en la cola al finalizar. Los valores de retorno se reemplazan por devoluciones de llamada en ese concepto. Es una pesadilla depurar y verificar, ya que carece de una pila de llamadas, y eficiente y flexible por la misma razón.Respuestas:
Es más un ejercicio de diseño que una recomendación general. Por lo general, no va a poner una cola entre todas sus llamadas a funciones directas. Eso sería ridículo. Sin embargo, si no diseña sus funciones como si se pudiera insertar una cola entre cualquiera de las llamadas a funciones directas, no puede justificar justificadamente que ha escrito código reutilizable y composable. Ese es el punto que Rich Hickey está haciendo.
Esta es una razón importante detrás del éxito de Apache Spark , por ejemplo. Usted escribe código que parece que está haciendo llamadas directas a funciones en colecciones locales, y el marco traduce ese código en mensajes que pasan en colas entre nodos del clúster. El tipo de codificación simple, composable y reutilizable que Rich Hickey defiende lo hace posible.
fuente
Una cosa a tener en cuenta es que la programación funcional le permite conectar funciones entre sí indirectamente a través de objetos mediadores que se encargan de obtener argumentos para alimentar las funciones y enrutar de manera flexible sus resultados a los destinatarios que desean sus resultados. Supongamos que tiene un código de llamada directa directo que se parece a este ejemplo, en Haskell:
Bueno, usando de Haskell
Applicative
clase y sus<$>
y<*>
operadores podemos volver a escribir mecánicamente ese código para esto:... donde ahora
myThing
ya no está llamando directamentef
yg
, sino más bien de conectarlos a través de algunos mediadores de tipof
. Entonces, por ejemplo,f
podría ser algúnStream
tipo proporcionado por una biblioteca que proporciona una interfaz a un sistema de colas, en cuyo caso tendríamos este tipo:Sistemas como este existen. De hecho, puede ver las secuencias de Java 8 como una versión de este paradigma. Obtienes un código como este:
Aquí está utilizando las siguientes funciones:
t -> t.getType() == Transaction.GROCERY
comparing(Transaction::getValue).reversed()
Transaction::getId
toList()
... y en lugar de hacer que se llamen directamente, está utilizando el
Stream
sistema para mediar entre ellos. Este ejemplo de código no llama a laTransaction::getId
función directamente,Stream
sino a las transacciones que sobrevivieron a la anteriorfilter
. Puede pensar en elStream
como un tipo de cola muy mínimo que combina funciones indirectamente y enruta valores entre ellas.fuente