Un poco de ayuda para entender el futuro y la tarea de Scalaz.
asynchronous concurrency (4)
Estoy tratando de entender la idea y el propósito detrás del paquete concurrente de scalaz, principalmente las clases de Tarea y Futuro, pero al usarlas en alguna aplicación, ahora está lejos de ser analógico secuencial simple, mientras que scala.concurrent.Future
, funciona más que mejor. ¿Puede alguien compartir su experiencia al escribir una aplicación concurrente / asíncrona con scalaz, básicamente cómo usar su método async
correctamente? Como entiendo por las fuentes, async
no usa un hilo separado como el llamado al future
estándar, o los métodos de fork/apply
de Scalaz funcionan, entonces ¿por qué se llama async
? ¿Significa que para obtener una concurrencia real con scalaz siempre tengo que llamar a fork(now(...))
o apply
?
Al componer Tareas con map y flatMap, puede obtener una ganancia de rendimiento al no usar la bifurcación, vea:
http://blog.higher-order.com/blog/2015/06/18/easy-performance-wins-with-scalaz/
No soy un experto en scalaz, pero intentaré ayudarte un poco. Déjame intentar responder a tus preguntas una por una:
1) ¿Puede alguien compartir su experiencia al escribir una aplicación concurrente / asíncrona con scalaz, básicamente cómo usar su método asíncrono correctamente?
Veamos primero la firma async
:
def async[A](listen: (A => Unit) => Unit): Future[A]
Esto podría ser un poco críptico al principio, por lo que siempre es una buena idea ver las pruebas para entender los posibles casos de uso. En https://github.com/scalaz/scalaz/blob/scalaz-seven/tests/src/test/scala/scalaz/concurrent/FutureTest.scala puede encontrar el siguiente código:
"when constructed from Future.async" ! prop{(n: Int) =>
def callback(call: Int => Unit): Unit = call(n)
Future.async(callback).run must_==
}
Como sabemos por la firma Future.async
simplemente construya un nuevo Futuro utilizando la función de firma (A => Unit) => Unit
. Lo que realmente significa es que Future.async toma como función de parámetro la cual para una devolución de llamada determinada realiza todos los cálculos necesarios y pasa el resultado a esa devolución de llamada.
Lo que es importante tener en cuenta es que Future.async
no ejecuta ningún cálculo en sí mismo, solo prepara la estructura para ejecutarlos más tarde.
2) Como entiendo por las fuentes, async no usa un hilo separado como el llamado al futuro estándar, o los métodos de bifurcación / aplicación de Scalaz funcionan, entonces ¿por qué se llama async?
Estás en lo correcto. Solo fork
y apply
parece estar ejecutando cualquier cosa utilizando subprocesos, lo cual es fácil de notar al mirar las firmas que contienen el implicit pool: ExecutorService
. No puedo hablar por los autores aquí, pero supongo que async está relacionado con la devolución de llamada. Significa que en lugar de bloquear en Futuro para obtener un resultado al final, usarás la devolución de llamada asíncrona.
3) ¿Significa que para obtener una concurrencia real con scalaz siempre tengo que llamar a fork (now (...)) o aplicar?
Por lo que puedo decir, sí. Tenga en cuenta que cuando está creando Future utilizando la sintaxis Future(x)
, está utilizando el método de apply
aquí, por lo que este es un tipo de comportamiento predeterminado (que está bien).
Si desea comprender mejor el diseño de Scalaz Futures, le recomiendo que lea "Programación funcional en Scala" . Creo que este libro está escrito por los principales colaboradores de Scalaz y en el capítulo 7 se analiza el diseño de API para una biblioteca de paralelismo puramente funcional. No es exactamente lo mismo que Scalaz Future, pero puedes ver muchas similitudes.
También puede leer la maravillosa publicación del blog de Timothy Perrett sobre Tarea y futuro de Scalaz que cubre muchos detalles no tan obvios.
async
se utiliza para adaptar una API asíncrona basada en el callback como Future
. Se llama async
porque se espera que se use con algo que se ejecute de forma asíncrona, tal vez llamando a la devolución de llamada desde otro hilo en algún lugar más abajo de la línea. Esta es una concurrencia "real", siempre que la API a la que llama realmente la use de forma asíncrona (por ejemplo, uso Future.async
con las partes asíncronas del AWS SDK como AmazonSimpleDBAsyncClient
).
Si desea la concurrencia "real" de la API de Task
scalaz directamente, debe usar elementos como fork
o gatherUnordered
, ya que muchas de las API están predeterminadas para ser seguras / deterministas y reiniciables, con concurrencia solo cuando se solicita explícitamente.