clojure - ¿Edición de programas "mientras están en ejecución"? ¿Por qué?
lisp scheme (13)
He estado recibiendo más en los lenguajes Lisp y Lispy últimamente, y los estoy encontrando bastante poderosos.
Una cosa que he estado leyendo en toda la red es que un beneficio de escribir en Lisp, Clojure, etc., es que puedes editar tu programa "mientras se está ejecutando".
Tal vez me estoy perdiendo algo, pero ¿cuál es el punto?
Claro, podría ahorrar unos segundos, pero ¿eso es todo? Cada vez que hago un cambio en mi programa, simplemente lo detengo y lo comienzo de nuevo, y eso ha funcionado bien durante décadas.
Debe haber una razón que no sea solo ahorrar tiempo, ¿qué es?
¿Alguien puede darme un buen estudio de caso que me hará babear sobre esta característica? :)
¡Esperando babear!
Debe haber una razón que no sea solo ahorrar tiempo, ¿qué es?
No, no hay. Quiero decir, nunca lo hay: la razón completa para usar una computadora es para ahorrar tiempo. No hay nada que una computadora pueda hacer que no puedas hacer a mano. Solo lleva un poco más de tiempo.
En este caso, no descartaría "unos segundos", dado que es una de las cosas que hago más que cualquier otra cosa, durante todo el día, para toda mi carrera de programación. Unos segundos para recompilar, unos segundos para volver a ejecutar, varios segundos para recrear el estado de mi programa la vez anterior; incluso en una estación de trabajo rápida, puede pasar fácilmente un minuto entre iteraciones. (Solía ser mucho peor, pero un hardware más rápido solo lo ha hecho peor, no es bueno. Las compilaciones de todo o nada están vinculadas a E / S, y es posible que nunca * coincidan con la velocidad de compilación más granular).
En Lisp, volver a compilar una sola función en un proceso que ya se está ejecutando es casi instantáneo (nunca lo he visto, incluso 0,1 segundos, incluso en mi computadora portátil de 5 años), y reiniciar significa que no tengo que volver a crear mi estado , incluso cuando algo señala.
Aquí hay una herramienta que me da una aceleración de 100x de una de las cosas más lentas y comunes que hago como programador. No sé qué más necesitarías. Probablemente podamos inventar algunas razones, pero si esto no es motivo suficiente, no sé cuál sería. Um, también es genial? :-)
(* Siempre que alguien dice "nunca" sobre algo relacionado con la tecnología, esa persona invariablemente termina pareciendo un completo imbécil 2 años después, y a pesar de la longevidad de Lisp, estoy seguro de que no será la excepción).
¿Porque tú puedes?
En serio, solo pruébalo por un tiempo, y sentirás el dolor cuando vuelvas a tu antiguo lenguaje de programación sin REPL.
Retroalimentación instantánea, fácil realización de pruebas rápidas sin tener que configurar un estado de programa falso en el dispositivo de prueba. Capacidad para inspeccionar el estado del programa en ejecución (cuál es el valor de esa variable). Todos estos son ahorradores de tiempo real.
Bueno, imagine que necesita parchear un servidor y no detenerlo.
Si haces esto en un lenguaje "típico", eso implicará algo de magia pesada. Tienes que robar ''detrás'' del código de ejecución. Creo que sería necesario parchear las tablas de funciones y demás, todo en el ensamblaje y manipular los punteros a las funciones. Un buen lugar para los errores.
En Lisp, la idea de actualizar sin tiempo de inactividad está integrada en el modelo de lenguaje. Si bien hay algunas complejidades de actualización de las que no se puede escapar (¿cómo maneja una conexión de larga ejecución?), No requiere la gran magia de un lenguaje compilado.
Aunque no he dedicado mucho tiempo (es decir, nada útil), resolví un prototipo de servidor en Common Lisp que haría al menos algunos parches en vivo en una red sin tiempo de inactividad.
Casey Muratori acaba de hacer algunas lecciones sobre cómo hacer esto con C y el compilador C / C ++ de Microsoft. En realidad es bastante simple, solo unas pocas docenas de líneas de código. Mira videos 22/24/25:
https://www.youtube.com/watch?v=WMSBRk5WG58
En el diseño del juego, la razón fundamental es poder sintonizar más rápidamente las constantes para encontrar el tono emocional al que apunta. Cosas como la sensación del juego, los guiones de comportamiento de los jugadores y la iluminación / ambientación del juego se benefician mucho de esto.
En el mundo real, esto se usa principalmente en el desarrollo y, como muchas características, solo vale la pena babear en el contexto correcto.
- programador personal de la dicha de la iluminación *
- verdadero despliegue continuo.
- cero tiempo de inactividad planificado Acuerdos de nivel de servicio.
- depurar servidores de producción.
* no es una garantía
para mí, y sospecho que otros aquí el beneficio real de este desarrollo impulsado por REPL es que puede ser indescriptiblemente divertido. adictivo incluso A veces puede dar un sentido de crafting-code. pruébalo ... vamos hombre, pruébalo, primero REPL''s siempre gratis :) un gran atractivo en estos días es el despliegue continuo.Actualmente, la idea de una implementación continua es que cambie una cosa, que cree todo (o que la empaquete) y que luego la implemente. con el modelo lisp es posible editar una casilla implementada (generalmente una caja que recibe un reflejo de sesiones reales del cliente) mientras está en despliegue.
solo una nota pedante en realidad no editas clases corrientes. compila una nueva copia de la clase y la deja en un lugar conocido (var) y la próxima vez que se usa, la nueva copia se encuentra y se usa. en realidad no se está editando mientras se está ejecutando y, más como si el nuevo código tuviese efecto inmediatamente, esto reduce el alcance del proceso de desarrollo de programas a expresiones (normalmente funciones).
Otro punto de babeo es la idea de obtener el beneficio de las soluciones de seguridad sin tener que declarar ningún tiempo de inactividad . puede hacer una actualización sin que le cueste a su SLA ninguno de sus preciosos "tiempos de inactividad programados". Si tiene que programar el tiempo de inactividad planificado con seis meses de anticipación, y solo obtiene dos horas de eso, entonces (para estas pobres almas) realmente podría hacer que babeen. Si tiene acceso de réplica a su aplicación en ejecución a medida que se despliega (potencialmente (con permiso) en el sitio del cliente) puede conectarse a la aplicación mientras se está ejecutando y ejecutar pruebas en el código existente en el contexto existente sin tener que detener y conecta un depurador. tampoco obtendrá ninguna pérdida de velocidad de un depurador. Es posible hacerlo con un REPL, aunque cuando obtienes la réplica allí, puedes crear código nuevo fácilmente (algunos dirán que es fácil inyectar cargadores dinámicos de clase a través del depurador) y luego arreglar las cosas. Entonces podrías conectarte a un servidor en ejecución. descubren que una función no pudo volver a conectarse a una base de datos después de una corta interrupción y luego volver a conectarla en ese momento. como ocurre con todas las construcciones de programación, nunca habrá una solución mágica y esta implementación / desarrollo continuo tiene una desventaja interesante: su programa puede ser correcto en la memoria y erróneo en el disco. si compila una función, luego la divide y guarda, entonces la única copia de trabajo del código es la que está ejecutándose. No es útil tenerlo en cuenta y volver a evaluar los archivos justo después de guardarlos. Esto puede sonar descarado, así que consulte cómo incorporar un REPL Clojure en su aplicación de producción.En los sistemas industriales, esto se utiliza para la programación de PLC para aliviar el tiempo de inactividad y las condiciones inseguras.
Estos son sistemas que se utilizan en centrales nucleares, sistemas de fabricación, fábricas de acero, etc. El proceso siempre se está ejecutando, continuamente, y el tiempo de inactividad es muy costoso o inseguro. Imagine un sistema que controla el enfriamiento de un reactor nuclear, no puede apagar ese sistema para implementar un nuevo código, debe poder modificarlo mientras se está ejecutando.
Esto es similar a la respuesta de Erlang para los sistemas de conmutación del teléfono.
Es principalmente para el desarrollo, donde solo ahorra tiempo.
Pero los ahorradores de tiempo son asombrosamente importantes.
Una vez que esté acostumbrado, volver al camino anterior es como pasar de volar a nadar en alquitrán.
Hay algunos casos de uso extremadamente interesantes. Un ejemplo es en la programación de GUI: lo vi mientras desarrollaba una aplicación GUI en tiempo real, ya que se ejecutaba junto a mi Emacs: añadí código para un nuevo botón y presioné "Cc Cc" para compilar esa única función, y el botón simplemente apareció ¡en la ventana! No tuvo que cerrar y volver a abrir la aplicación. Luego comencé a ajustar widgets y manipular el diseño, y la ventana abierta se reorganizaba automáticamente: los botones se movían, aparecían nuevos campos de texto, etc. tan pronto como ejecutaba cada pequeño cambio que había hecho.
Otro ejemplo es un excelente screencast sobre la biblioteca Clojure OpenGL "Penumbra" donde el programador crea un juego 3D tetris en tiempo real. Comienza con una ventana vacía de OpenGL junto a sus emacs. Él define un objeto de cubo - CMx - y está en la pantalla. Ejecuta un comando para girarlo, inmediatamente comienza a girar. Ejecuta un ciclo que define 5 cubos más en diferentes ubicaciones, aparecen pop-pop-pop-pop-pop. Todo responde de inmediato, el conjunto completo de herramientas de OpenGL está ahí para jugar. Agregue una nueva textura de superficie a su cubo y vea que aparezca de inmediato. Se convierte en un mundo 3D maleable: el código modifica dinámicamente el mundo existente en lugar de cerrar y volver a abrir el lienzo 3D con cada cambio.
Penumbra Livecoding Screencast - descarga la versión HD para una mejor experiencia.
También hay una gran presentación / screencast sobre la biblioteca de audio "Overtone" para Clojure. La biblioteca es un conjunto de herramientas de sintetizador donde tienes un conjunto de funciones de sintetizador para manipular la onda de sonido. Durante la presentación, el desarrollador escribe un código que inicia la reproducción de un tono. Luego pasa diez segundos escribiendo un bucle que reproduce ese sonido 10 veces pero que aumenta la frecuencia cada vez, y nuevamente CMx y lo escuchas, las notas suben más alto. En el espacio de 20 minutos en tiempo real, escucha una canción. Parece un montón de diversión.
Otros usos serían, por ejemplo: Web crawling / data mining: desarrollar y refinar algoritmos para extraer información en tiempo real, ver los datos devueltos en cada paso; Programación de robótica: envía comandos a un robot mientras está activo; Reconocimiento facial / de imágenes: con una biblioteca como OpenCV, mire sus cambios al instante y actualice lo que la biblioteca reconoce en una imagen / video mientras desarrolla el código; Trabajo de matemáticas (Clojure tiene "Incanter" para estadísticas); y cualquier entorno en el que desee ver de inmediato el efecto que han tenido los cambios en los datos con los que está trabajando.
Así que ese es el aspecto más divertido de tener un REPL frente a ti. Las cosas que no eran tangibles, maleables, interactivas, comienzan a ser. El diseño de la GUI, los gráficos en 3D, la producción de sonido programático, la extracción y la transformación de datos, estas cosas normalmente se han hecho en condiciones de plena competencia. Pero con Clojure (y hasta cierto punto con otros lenguajes dinámicos también) está hecho para ser realmente tangible e inmediato; usted ve cada cambio tan pronto como escribe el código, y si algo no funciona o no obtiene el resultado esperado, simplemente cambia lo que olvidó y lo vuelve a ejecutar inmediatamente.
Clojure está muy alineado para hacer esto. Lo más extraño es que puedes usar las bibliotecas de Java en tiempo real de la misma manera, ¡a pesar del hecho de que Java no puede hacerlo! Así que Overtone está usando una biblioteca de sintetizadores Java en tiempo real a pesar del hecho de que nunca podría hacerlo en Java, Penumbra está usando los enlaces de Java OpenGL, etc. Esto se debe a que Rich Hickey diseñó Clojure para que pueda compilar código de byte JVM sobre la marcha. Es un lenguaje increíble: Clojure ha hecho una gran contribución a lo increiblemente divertida y productiva que puede ser la programación.
Hay un eslogan de marketing para Lisp:
Con Lisp, y su método de desarrollo incremental, el costo de un cambio a un sistema de software depende del tamaño del cambio, y no del tamaño de todo el software.
Incluso si tenemos un gran sistema de software, el costo (tiempo, ...) de un cambio se mantiene en relación con el tamaño de un cambio. Si agregamos un nuevo método o cambiamos un nuevo método, el esfuerzo permanece en relación con el esfuerzo de editar el método, compilar incrementalmente el método y cargar incrementalmente el método.
En muchos entornos de software tradicionales, el cambio de un método puede necesitar una recompilación parcial, un nuevo ejecutable vinculado, un reinicio, una recarga, etc. Cuanto más grande sea el software, más tiempo llevará.
Para un ser humano esto significa que, posiblemente, salgamos de un estado de flujo . Eso es parte de la productividad de los buenos entornos Lisp: uno puede realizar muchos cambios en un sistema de software en poco tiempo, una vez que el programador se sienta cómodo y entre en este estado de flujo . Supongo que muchos han experimentado esto, donde el trabajo se realiza en poco tiempo, lo que se opone a los momentos en que uno se sienta frente a un sistema que no responde y nos enfrentamos a tiempos de espera.
También hay poca distancia cognitiva entre nosotros y el programa en el que estamos trabajando. Por ejemplo, si edita una clase en un entorno por lotes, debe imaginar el efecto de los cambios. En Lisp editas una clase y cambias al mismo tiempo los objetos. Eso significa que cambia el comportamiento de los objetos directamente, y no una nueva versión de ellos después de un ciclo de edición por lotes-compilación-enlace-ejecución-prueba.
En un sistema Lisp, cambia una clase en un sistema CAD y luego puede estar inmediatamente activo. Cuando las personas preguntan, si Lisp trabaja para grandes equipos de software, la respuesta puede ser que el gran equipo de software no es necesario, si trabajas incrementalmente. Entonces, el problema era / es que los desarrolladores de software realmente buenos y familiarizados con el desarrollo incremental eran (¿son?) Raros.
En muchas aplicaciones hay una capa de lenguaje de scripting separada, a veces para los desarrolladores originales (y no para los usuarios). En Lisp esto no es necesario, Lisp es su propio lenguaje de extensión .
Otra cosa buena además de modificar el programa sobre la marcha sin tener que reiniciar todo (lo ha hecho durante décadas no significa que sea lo mejor, ¿no?), Es que puedes inspeccionar tu programa en su estado actual y capaz de descubrir qué está pasando.
Recuerdo que alguien de la NASA describió su experiencia. Su equipo implementó el soft usado en una nave espacial en los años 70. Y efectivamente modificaron su soft remotamente sobre la marcha cuando se encontraron algunos errores.
O imagine que tiene un proceso largo que tarda días en ejecutarse y, al final, no puede escribir resultados debido a permisos u otro pequeño problema.
Otro ejemplo más. Estás en la fase de integración y tienes que hacer muchos pequeños cambios. Y de nuevo muchos de ellos. Sueño con tal posibilidad en Java porque actualmente me lleva de 30 a 40 minutos reconstruir y volver a instalar mi aplicación (para volver a crearla en 10 minutos).
Si miras algo como Erlang, el punto es evitar el tiempo de inactividad.
Funciona con cosas como interruptores telefónicos que no puedes apagar por unos segundos.
Para usos más normales, sin embargo, es una característica "agradable de tener", pero sí, probablemente no sea crítica.
Ves datos reales Esa es una gran ventaja. Entonces no tienes que especular.