16 puntos por GN⁺ 2025-02-25 | 3 comentarios | Compartir por WhatsApp
  • Clojure no es uno de los lenguajes de programación dominantes y, para algunas personas, puede resultar poco familiar
  • Principales ventajas de Clojure
    • Productividad del desarrollador: Clojure es interactivo, reduce el trabajo repetitivo y ofrece un entorno de desarrollo eficiente. Los desarrolladores pueden lanzar productos rápido y con satisfacción
    • Mantenibilidad a largo plazo: el lenguaje Clojure y su ecosistema son maduros y estables. Es posible construir sistemas de alta calidad reduciendo los costos de mantenimiento
    • Cultura centrada en las ideas: la comunidad de Clojure explora ideas del pasado y del presente, tanto de la academia como de la industria, para buscar mejores formas de desarrollar software. Les da a los desarrolladores nuevos retos y oportunidades de aprendizaje

(hello 'clojure)

  • Clojure es uno de los lenguajes de la familia Lisp, creada en la década de 1950
    • Lisp comenzó como un modelo teórico, pero también ofrece una gran elegancia conceptual en la programación práctica
    • Como la sintaxis del código coincide con la estructura de los datos, tiene varias ventajas frente a lenguajes con gramáticas más complejas
    • Fue uno de los principales lenguajes usados durante el auge anterior de la IA
  • Los lenguajes de la familia Lisp han ganado popularidad por períodos y luego la han perdido, pero en los últimos 10 años Clojure ha llamado la atención
  • Clojure corre sobre la JVM de Java y refleja conceptos modernos de programación
    • Fuerte soporte para estructuras de datos inmutables
    • Diseño pensado para la concurrencia
  • Aunque creció sin el respaldo de grandes corporaciones y su comunidad es pequeña, tiene características de lenguaje muy potentes
  • Clojure puede ejecutarse en distintos entornos
    • ClojureScript: se ejecuta compilando a JavaScript
    • ClojureCLR: se ejecuta en entornos .NET
    • Babashka: intérprete de scripting rápido basado en GraalVM
    • Jank: soporte para compilación nativa
  • Aprender Clojure tiene la ventaja de permitir reutilizar el mismo conocimiento en varios entornos

Desarrollo interactivo

  • Programar es un proceso repetitivo de escribir código y validarlo
    • Sin retroalimentación, es difícil tener certeza de que el código funciona correctamente
    • Formas comunes de retroalimentación:
      • Repetir la ejecución de scripts
      • Interacción con la UI y salida de logs
      • Uso de pruebas unitarias
      • Uso del compilador y herramientas de análisis estático
    • La rapidez de la retroalimentación impacta mucho en la productividad del desarrollador
      • Mientras más lenta sea, más tiempo se invierte en depurar y menos en escribir código
  • Además de las formas tradicionales de retroalimentación, Clojure ofrece desarrollo interactivo (interactive development)
    • El núcleo es el desarrollo basado en REPL (Read-Eval-Print Loop)
    • Antes de escribir código, el desarrollador ejecuta el runtime de Clojure y lo conecta al editor
    • Puede ejecutar fragmentos de código uno por uno y recibir retroalimentación inmediata
    • Gracias a la estructura sintáctica consistente de Lisp, es posible ejecutar libremente desde pequeños fragmentos hasta bloques grandes de código
  • A diferencia de un REPL común, Clojure puede ejecutar fragmentos de código mientras está conectado a un sistema en ejecución
    • Este enfoque ofrece un ciclo de retroalimentación mucho más potente que el clásico “escribir código → ejecutar → depurar”
    • Permite modificar y depurar programas en tiempo real
    • No es solo un REPL simple, sino una herramienta de interacción poderosa que también puede usarse en entornos de producción

Una cultura que valora la estabilidad

  • Elegir Clojure no solo significa obtener una tecnología poderosa, sino también pasar a formar parte de una comunidad con una filosofía y principios particulares
    • Si solo se adopta la tecnología sin interactuar con la comunidad, es difícil experimentar el verdadero valor de Clojure
  • La comunidad de Clojure considera la estabilidad y la compatibilidad hacia atrás como valores importantes
  • El lenguaje central se mejora y amplía de forma continua casi sin cambios que rompan compatibilidad
  • El ecosistema open source de Clojure sigue la misma filosofía y minimiza los cambios innecesarios (churn)
    • Esto contrasta con la mayoría de los ecosistemas modernos de lenguajes de programación
    • El desperdicio de recursos provocado por cambios innecesarios asciende a decenas de miles de millones de dólares a nivel mundial
  • En Clojure, actualizar a la última versión del lenguaje y las librerías es algo muy natural
    • Puedes aprovechar correcciones de errores, actualizaciones de seguridad y mejoras de rendimiento sin tener que reescribir la base de código
    • En otros lenguajes, código existente puede romperse con una nueva versión; en Clojure, normalmente se mantiene intacto
  • Algunos desarrolladores podrían preguntarse: “¿cómo se puede evolucionar sin cambiar?”
    • Pero estabilidad y estancamiento no son lo mismo
    • Clojure evoluciona agregando y mejorando funciones sin romper las existentes
    • Los desarrolladores pueden seguir usando herramientas cada vez mejores sin tener que hacer cambios innecesarios en el código

Sistemas de información y representación del conocimiento

  • En el desarrollo web y de aplicaciones de negocio, lo esencial es recopilar, acceder y procesar información
  • Sin embargo, muchos lenguajes dominantes muestran diseños ineficientes para representar y manipular información
    • Obligan a usar estructuras de datos de bajo nivel, lo que introduce complejidad innecesaria
    • Los sistemas de tipos estáticos suelen ser demasiado rígidos, dificultando la manipulación flexible de datos
  • Clojure ofrece por defecto estructuras de datos funcionales y poderosas capacidades de manipulación de datos
    • Como lenguaje de tipado dinámico, sigue la “Open World Assumption”
      • Es una forma de maximizar la extensibilidad y flexibilidad de los datos
    • Está muy influenciado por RDF (framework de modelado de datos para la Web Semántica)
      • Un ejemplo representativo es la gran sinergia con bases de datos de grafos como Datomic
      • Al usar keywords con namespace, es posible asignar significado independiente del contexto
  • La estructura de Map en Clojure con keywords con namespace puede expresar significado de forma más refinada que un JSON simple
  • Es fácil extender los datos adicionalmente y expresarlos de forma intuitiva evitando colisiones de nombres

Funciones pequeñas y componibles, y datos inmutables

  • En Clojure, lo habitual es programar alrededor de funciones puras (pure functions) y datos inmutables (immutable data)
  • Se puede escribir código imperativo al estilo Java, Ruby o C, pero el código idiomático de Clojure es muy distinto
    • Funciones puras: devuelven resultados basados solo en sus entradas y no modifican estado externo
    • Datos inmutables: su significado se basa en valores, no en referencias (reference) ni identidades de objeto (identity)
  • Como no dependen de estado externo, el código es más predecible
  • No hay modificaciones de variables globales ni efectos secundarios inesperados (side effects)
  • Como no existe el riesgo de que los datos cambien, es más fácil resolver problemas de procesamiento paralelo y concurrencia

Manejo de concurrencia

  • La computación moderna funciona sobre procesadores multinúcleo, y la concurrencia es un elemento indispensable
    • A medida que la ley de Moore llega a sus límites, aprovechar el paralelismo es clave para mejorar el rendimiento
    • Pero al tratar con estado mutable (mutable state) aparecen problemas de sincronización y control de tiempos complejos
  • Clojure enfatiza la inmutabilidad (immutability) para resolver los problemas de concurrencia desde la raíz
    • Manipular memoria mutable introduce dependencia del tiempo y del orden
    • Las transformaciones puras de datos (data-in, data-out) siempre pueden ejecutarse en paralelo de forma segura
  • Clojure aprovecha las capacidades de concurrencia existentes de la JVM (java.util.concurrent), pero ofrece herramientas de abstracción de más alto nivel
    • Atoms: operaciones atómicas con CAS (Compare-and-Set) y reintentos automáticos
    • Refs: memoria transaccional por software (Software Transactional Memory, STM)
    • Agents: aplicación de actualizaciones de forma asíncrona
    • Futures: interfaz fork-and-join basada en thread pools
  • También ofrece la librería core.async
    • Soporta el patrón CSP (Communicating Sequential Processes), similar a las goroutines de Go
    • Se puede comparar con el modelo Actor de Erlang/Elixir o con Akka en Scala
  • Incluso sin usar las abstracciones de alto nivel de Clojure, también es posible usar técnicas de control de concurrencia de nivel más bajo
    • Soporta colas sincronizadas, referencias atómicas, locks, semáforos, diversos thread pools y gestión manual de hilos
  • Si hace falta, es posible un control fino de la concurrencia, pero en la mayoría de los casos usar herramientas abstraídas es más seguro y eficiente

Razonamiento local (Local Reasoning)

  • Hay un límite para la complejidad de código que se puede considerar al mismo tiempo
  • Cuando el estado del programa y sus cambios ocurren en muchos lugares, entender y mantener el código se vuelve difícil
  • Razones por las que Clojure facilita el razonamiento local
    • Código centrado en funciones puras (pure function)
      • Es posible entender completamente el comportamiento de una función solo con sus entradas
      • No hace falta considerar estado externo a la función
    • Diferencias frente a los lenguajes orientados a objetos
      • Clojure minimiza el polimorfismo (polymorphism), así que es fácil saber dónde se ejecuta el código
      • En la programación orientada a objetos (OOP), “todo ocurre en otra parte”, pero
        en Clojure solo hay que seguir las funciones definidas en los namespaces
  • Gracias a la estructura sintáctica consistente de Clojure, refactorizar código es sencillo
    • El uso de datos inmutables y funciones puras reduce los efectos secundarios inesperados al modificar código
    • Al separar por aparte solo la mínima cantidad de código imperativo, es posible combinar armónicamente código imperativo y funcional

Pruebas sencillas

  • En código basado en funciones puras de Clojure, se puede probar solo introduciendo valores de entrada y verificando los de salida
    • No hacen falta inicializaciones complejas de estado, configuración de objetos mock ni control de timing para las pruebas
    • Por eso, las pruebas son más confiables y tienen menos “flakiness” (fallas inconsistentes en pruebas)
  • Soporta técnicas avanzadas de testing como Property Based Testing (Generative Testing)
    • Genera entradas aleatorias para buscar casos que violen ciertas propiedades o invariantes
    • Incluye técnicas de shrinking para encontrar el caso mínimo que falla
  • Este concepto nació en Haskell y se implementó en muchos lenguajes mediante frameworks de pruebas basados en QuickCheck
  • En Clojure resulta todavía más efectivo por la sinergia con sus estructuras de datos inmutables y el desarrollo basado en REPL

Ventajas al contratar desarrolladores de Clojure

  • En general, hay relativamente menos desarrolladores de Clojure que de lenguajes populares como JavaScript o Python
  • Pero así como hay pocos desarrolladores de Clojure, tampoco hay tantas empresas que usen Clojure
    • Por lo tanto, el equilibrio general entre oferta y demanda se mantiene
    • En la práctica, las empresas que buscan desarrolladores de Clojure y los desarrolladores suelen empatar razonablemente bien
  • Los desarrolladores de Clojure suelen tener una gran capacidad para resolver problemas
    • En lugar de limitarse a aprender tecnologías populares, muchos exploran nuevas formas de pensar e ideas
    • De hecho, las empresas que usan Clojure suelen evaluar que hay menos candidatos, pero muchos de alta calidad
    • Ejemplo: Nubank formó directamente en Clojure a cientos de desarrolladores en Brasil y dejó un caso exitoso
  • Encontrar desarrolladores de Clojure es difícil, pero cuando se encuentra talento adecuado, hay altas probabilidades de incorporar excelentes desarrolladores
    • En vez de buscar únicamente gente con experiencia previa en el lenguaje, también es una buena opción formar desarrolladores con alta capacidad de aprendizaje
    • La propia comunidad de Clojure tiene la característica de atraer desarrolladores que piensan a fondo los problemas y los resuelven

Trade-offs y ajuste del nivel de abstracción

  • Clojure es un lenguaje de alto nivel (high-level) y busca escribir código conciso y expresivo
    • Gracias a sus estructuras de datos funcionales (inmutables) y sus poderosas APIs de manipulación de datos, se reduce la complejidad innecesaria
  • Las estructuras de datos de Clojure usan internamente árboles (Hash Array Mapped Trie) para garantizar inmutabilidad
    • Al actualizar, se produce path copying, lo que puede aumentar la carga del GC (garbage collection)
    • En la interoperabilidad (interop) con Java también puede haber runtime reflection y boxing/unboxing
    • En aplicaciones comunes, este costo suele ser prácticamente despreciable y la ganancia en productividad es grande
  • En casos donde se requiere alto rendimiento, como motores gráficos en tiempo real, procesamiento de señales u operaciones numéricas, es posible optimizar a bajo nivel
    • En Clojure se pueden usar type hints para eliminar reflection y optimizar operaciones con tipos primitivos
    • El uso de arreglos contiguos de tipos primitivos permite aprovechar la caché de CPU
    • También se pueden usar librerías con aceleración por GPU
  • La mayoría de los lenguajes de alto nivel necesitan extensiones nativas en C/Rust cuando el rendimiento es crítico, pero
    Clojure puede resolver la mayoría de los problemas de rendimiento aprovechando las optimizaciones de la JVM (compilación JIT)
    • Con profiling y un poco de optimización, es posible maximizar el rendimiento de cosas como event loops

Metaprogramación y APIs centradas en datos

  • Como lenguaje de la familia Lisp, Clojure puede tratar el código como si fuera datos
    • Similar a JSON, pero con una estructura más fácil de leer para representar programas
    • Usa un formato de datos llamado EDN (Extensible Data Notation) que ofrece una representación parecida a JSON
  • Clojure ofrece la capacidad de transformar el propio código usando macros
    • Sin embargo, la comunidad de Clojure tiene una cultura de limitar cuidadosamente el uso de macros
    • Las macros pueden ser difíciles de depurar y menos compatibles con herramientas de análisis estático
  • En su lugar, se prefiere un diseño de APIs orientado por datos (data-driven)
    • Ejemplo: ruteo HTTP, generación de HTML/CSS, validación de datos, etc.
    • En vez de invocar directamente funciones específicas, se especifica el comportamiento usando estructuras de datos (maps, vectores)
    • Las APIs basadas en datos pueden manipularse dinámicamente y permiten guardar y modificar fácilmente la configuración del usuario
  • Gracias a estas APIs centradas en datos, es posible reconfigurar dinámicamente sistemas en runtime
    • Esto facilita implementar sistemas de simulación muy flexibles, gestión dinámica de configuración y metaprogramación

Interoperabilidad con Java (Interop) y aprovechamiento del ecosistema

  • El desarrollo moderno de aplicaciones consiste en combinar innumerables librerías open source y APIs
    • Como Clojure corre sobre la JVM, puede aprovechar millones de paquetes Java disponibles en Maven Central
    • Es posible invocar librerías Java de forma sencilla incluso sin reflection
  • En comparación con Java, Clojure tiene código mucho más conciso y facilita la programación experimental con REPL
    • Permite explorar y combinar APIs mucho más rápido que en Java
    • Con ClojureScript, se puede aprovechar de la misma manera el ecosistema de JavaScript y NPM

Una cultura centrada en las ideas

  • Es posible tener proyectos exitosos con cualquier lenguaje, y por el contrario, si se usa mal, también se puede fracasar con cualquier lenguaje
    • Una buena herramienta no crea automáticamente buenos desarrolladores
  • Clojure tiene una barrera de entrada alta y requiere ensayo y error durante el aprendizaje, pero eso permite pensar con más profundidad
  • Algunos proyectos de Clojure todavía arrastran estilos de código tipo Java o Python y no aprovechan bien el lenguaje
    • Pero los equipos que adoptan la filosofía y las ideas de Clojure desarrollan mejores capacidades de diseño de software
  • La comunidad de Clojure cuestiona constantemente las formas tradicionales de desarrollo y explora métodos mejores
    • Las charlas de Rich Hickey (creador de Clojure) no son simples introducciones técnicas, sino una exploración de principios fundamentales del diseño de software
    • En las conferencias de Clojure, el foco está más en ideas, análisis de papers y experiencias compartidas que en la presentación de librerías

Conclusión

  • Clojure no es solo un lenguaje de programación, sino una comunidad de personas que reflexionan sobre mejores formas de desarrollar software
  • En esta comunidad, ampliar los propios límites y explorar nuevas posibilidades es un valor central
  • Los desarrolladores que crecen dentro de esta cultura no solo aprenden a usar bien Clojure, sino que se convierten en ingenieros de software con mejores habilidades para resolver problemas

3 comentarios

 
roryk 2025-02-26

Personalmente uso Clojure, y coincido mucho con el contenido del artículo.
En el trabajo he usado principalmente Python y Java(Type)Script, pero si no les das aunque sea un poco de mantenimiento, es muy fácil que el propio lenguaje y las librerías cambien y el código se vuelva legado; en cambio, con Clojure me ha dejado muy satisfecho lo fácil que resulta retomar y modificar código que escribí, incluso un año después.
Desde entonces, para uso personal, salvo que haya limitaciones de alguna librería en particular, sigo prefiriendo Clojure.

 
plumpmath 2025-02-25

¡Jank, Jank~!

 
GN⁺ 2025-02-25
Opiniones de Hacker News
  • Si me preguntaran qué tipo de programación he disfrutado, diría que construir pipelines para procesar datos en el shell y escribir Clojure y ClojureScript durante los últimos 5 años

    • Actualmente somos 4 personas escribiendo Clojure y acumulamos más de 30 años de experiencia total con Clojure
    • Iniciamos una consultora en Praga que prefiere Clojure, y creemos que puede ser una buena elección para distintos tipos de sistemas
  • He usado Clojure durante 12 años, y antes de eso usé Java por más de 12 años

    • He creado excelentes apps y librerías con Clojure y Java
    • Describo a Clojure como "el lenguaje de programación menos malo"; su núcleo es potente y estable
    • Cuando dominas las herramientas, mejoras tu velocidad y precisión de trabajo
    • El flujo de trabajo orientado al REPL de Clojure es una propuesta de valor importante
  • Me encanta escribir Clojure, y al compararlo con otros lenguajes me di cuenta de que no necesito explicar ese afecto profundo por Clojure

    • No me interesan los problemas personales con otros lenguajes, y mi amor por Clojure se debe a razones teóricas, prácticas, emocionales y financieras
    • Clojure no es perfecto, pero es el más satisfactorio entre los lenguajes de programación de uso general
  • El cofundador tenía como meta construir la mayor cantidad posible de producto con la menor empresa posible

    • Tras 5 años de bootstrapping, alcanzaron más de 1 millón de dólares en ARR, y Clojure jugó un papel importante
    • Clojure también contribuyó a mi felicidad como programador, y planeamos seguir haciendo crecer al equipo central de producto en Clojure
  • He operado un negocio SaaS con Clojure durante 10 años, y sin Clojure no habría sido posible

    • La estabilidad del lenguaje es muy útil; en otros ecosistemas a menudo hay que reescribir software con frecuencia
    • Recomiendo leer publicaciones con perspectiva de personas que realmente han usado el lenguaje, en lugar de críticas superficiales
  • A quienes usan Clojure les recomiendo <a href="https://www.flow-storm.org/">Flow Storm</a>

    • Si se usa junto con una buena suite de pruebas, permite activar distintos escenarios
  • Aprendí mucho de Rich Hickey y tenía entusiasmo por Clojure y la FP

    • Sin embargo, a medida que el negocio creció, el tipado dinámico se volvió una carga, y después de que se fue personal clave fue difícil encontrar desarrolladores de Clojure
    • Actualmente, la mayor parte del stack está compuesta por Python y Go
  • Hubo comentarios de que la documentación de ClojureDocs está desactualizada, y quería agregar una función para poder votar las respuestas

    • Gracias al algoritmo de Google, ClojureDocs sigue siendo lo más usado
  • Me sorprendió la parte sobre la estabilidad de Clojure, porque cada vez que lo probaba cada año sentía que todo había cambiado

    • Me pregunto si ejecutar "Hello World" sigue siendo lento; leer Clojure es agradable, pero escribirlo siempre fue un obstáculo
  • Empecé con Common Lisp y luego me pasé a Go y Rust, pero recientemente he vuelto a mirar Clojure

    • Gracias al REPL y la programación interactiva, se puede trabajar rápido
    • La JVM es un runtime excelente