scala - programming - Cuándo usar Cierres
scala programming (5)
Nota: salte a "Pregunta" a continuación si solo desea omitir el contexto
Al dar charlas sobre Scala, casi siempre estoy seguro de que se me preguntará "¿pero cuándo usarías un Cierre?" Normalmente, digo "esperar hasta que te muestre una para la comprensión" y luego muestro cómo la comprensión puede reducirse a usar cierres flatMap / map +.
Este ejemplo ayuda, sin embargo, es difícil para mí explicar un general "aquí es cuando reconocerías cuándo usar un cierre". Es una de esas cosas a las que "simplemente te acostumbras" en la programación funcional, casi lo haces sin pensar.
Normalmente hablo con desarrolladores de Java cuando hablo de Scala, y una analogía que me viene a la mente es "¿sabes cuándo creas una clase interna anónima que necesita acceder a una variable en algún lugar arriba de donde se definió la clase? Eso es como un cierre ". Así que uso el procesamiento de devolución de llamada de eventos como un ejemplo particular (como inevitablemente se usan clases internas anónimas en ese escenario).
Teniendo en cuenta la audiencia (sin experiencia, o experiencia limitada con FP) no continúo para discutir términos como las Mónadas, estas personas típicamente buscan ejemplos concretos / pragmáticos con los que puedan relacionarse.
Pregunta: ¿ Alguien tiene su propia forma de explicar con éxito los cierres que realmente golpea a casa para los desarrolladores de Java (u otros lenguajes de OO)?
¿Por qué no inviertes el orden de presentación? Cuando normalmente presenta Closures, presente un patrón bien conocido (como Listener o Visitor). Luego, muestre cómo Closures soluciona el problema sin la creación de objetos adicionales "auxiliares" o el procesamiento innecesario de marcos de códigos.
Usted dice que su audiencia está buscando ejemplos concretos de cómo funciona Closures. Después de presentar algunos de ellos, puede retroceder y definir correctamente un Cierre. La técnica de presentar el problema, resolverlo, resolverlo de nuevo, resolverlo nuevamente y luego presentar la teoría más amplia no es ajeno a los programadores. Así es como descubrieron patrones en primer lugar.
Creo que está en camino de relacionar los cierres con las clases internas anónimas por el simple hecho de presentar el concepto a los desarrolladores de Java. Recomiendo tomar algún problema que implique el uso intensivo de devoluciones de llamadas, y mostrar una diapositiva con una comparación del código requerido en Java utilizando clases internas anónimas y el código requerido en Scala utilizando cierres.
Aquí hay un par de ideas para problemas relacionados con devoluciones de llamada:
- Botón de clic de Swing manipuladores
- Cualquier cosa que implique un ejecutable o invocable (es decir, enviar una tarea a un ThreadPoolExecutor)
- Análisis de XML basado en eventos SAX
- El código de acceso a la base de datos basado en JDBC pasa a través de un ejecutor de transacciones común para reducir la adquisición / liberación de la conexión estándar
Para cada uno de estos ejemplos, espero que la solución Scala / closure resulte más corta y más simple que la solución Java / anonymous inner class.
Muchos desarrolladores de Java ya estarán familiarizados con los patrones de diseño ''closures-lite'' que imitan los cierres con clases:
- visitante
- mando
- estrategia
- Functor (ver el manejo de colecciones de Google Guice para muchos ejemplos)
- devolución de llamada / oyente (grandes franjas de Swing)
Cada vez que un desarrollador de Java busca uno de estos patrones para resolver un problema en Scala, debería considerar los cierres como una alternativa ligera.
Como mencionas, los cierres de primera clase son, de alguna manera, solo una extensión lógica de estos patrones: si mi clase anónima solo tiene un método, ¿por qué no eliminar la clase por completo y solo tener el método en sí?
También puede valer la pena analizar lo que todos estos patrones tienen en común: todos son una forma de abstracción del flujo de control . Separa el flujo de control de una acción para realizar en ciertos puntos de este flujo. Cualquier separación similar de preocupaciones puede beneficiarse de los cierres.
Si se refiere a funciones anónimas en general, y no solo a cierres, normalmente utilizo el siguiente ejemplo. Proporcione una colección simple (en este caso, una lista de nombres) y muestre cuánto más simple es consultar y modificar con funciones anónimas.
Java: obtenga una nueva lista de nombres que comiencen con "A":
List<String> names = Arrays.asList("Ed", "Bob", "Anna");
List<String> namesOnA = new ArrayList<String>();
for(String name : names) {
if(name.startsWith("A")) {
namesOnA.add(name);
}
}
Scala: obtener una lista de nombres que comiencen con "A":
val names = List("Ed", "Bob", "Anna")
val namesOnA = names.filter(_.startsWith("A"))
Ejemplos similares utilizando map, reductionLeft y otros métodos en las colecciones. La mayoría de los desarrolladores de Java encuentran esto interesante.
Usted solicitó una explicación simple de los cierres para desarrolladores que provienen de idiomas en los que no existen o se usan con poca frecuencia.
Aquí hay una explicación sucinta comparándolos con los fotogramas que no se desasignan cuando se devuelve una función: "Cierres de JavaScript 101: no son mágicos" . Encuentro que esta es una conceptualización útil para un desarrollador tradicional que lucha con la idea.
Además, los Goslings que votaron por ellos en los primeros días del debate sobre el cierre de Java serían de interés para los desarrolladores de Java. Tenga en cuenta que también hace referencia a la propuesta original de Neal Gafter para su inclusión en Java.