2 puntos por GN⁺ 2024-05-27 | 1 comentarios | Compartir por WhatsApp
  • Hurl es un lenguaje de programación experimental que construye el flujo de control solo con manejo de excepciones, en lugar de bifurcaciones o bucles comunes
  • Surgió de una conversación entre Nicole Tietz-Sokolskaya y amigos de Recurse Center, y el sitio ofrece documentación de uso, ejemplos, guía de depuración y preguntas y respuestas
  • Los testimonios de la página de introducción ponen deliberadamente al frente un tono de broma, con frases como “monstrosity is beautiful” y “Certified unhinged™”
  • El código fuente de Hurl y del sitio está publicado, pero para enviar parches por email se requiere ceder los derechos del parche
  • La licencia se ofrece a elección entre AGPL-3.0, GAL-1.0 o una licencia comercial

Un experimento de lenguaje que deja solo el manejo de excepciones

  • Hurl fue creado con un solo propósito: explorar si es posible un lenguaje construido únicamente con flujo de control basado en manejo de excepciones
  • La idea surgió de una conversación entre Nicole Tietz-Sokolskaya y amigos de Recurse Center, cuyas identidades no se revelan “por decoro”
  • El sitio ofrece documentación de uso de Hurl, ejemplos, guía de depuración y preguntas y respuestas

Parece una broma, pero es un proyecto publicado de verdad

  • La página de introducción incluye testimonios que muestran el carácter de Hurl
    • “This monstrosity is beautiful, and I must never touch it”
    • “Certified unhinged™!”
    • “is "🤮" an available quote?”
  • Si quieres incluir un testimonio adicional, debes enviarle un email a Nicole, y se requiere consentimiento explícito para incluir la cita

Código fuente y condiciones de licencia

  • El código fuente del lenguaje Hurl y de este sitio está publicado en Hurl's repo
  • Si encuentras bugs o errores, puedes enviar parches por email, pero debes ceder todos los derechos sobre el parche
    • Es una condición para mantener la posibilidad de relicenciamiento y de licenciamiento comercial
  • El proyecto puede usarse bajo una de las siguientes tres licencias
    • AGPL-3.0

      • GAL-1.0: Gay Agenda License
      • licencia comercial
      • Durante la revisión de licencias también se consideraron joke licenses y unfortunate licenses, pero finalmente se usan las tres licencias anteriores

1 comentarios

 
GN⁺ 2024-05-27
Opiniones en Hacker News
  • Si diseñara un lenguaje de programación, haría obligatorio el uso de namespaces en include/import y, si es posible, también impediría los efectos secundarios a nivel superior.
    Recibirlo como let foo = include "lib/foo.hurl" y luego llamar a foo.init() hace que sea mucho más fácil razonar sobre el código.
    En cambio, si después de include "lib/foo.hurl" // side effects aparece baz(buz), es difícil saber si la función y la variable vienen de la biblioteca estándar o si fueron incluidas desde algún lado.

    • Cuando no hay un enlace de nombres explícito, conviene forzar que la sentencia import y el namespace coincidan.
      import "foo/bar" debería permitir usar foo.* o bar.*, no bazz.*. Go es justo lo que se me viene a la mente.
    • No es que no esté de acuerdo, pero IntelliJ, que uso en el trabajo, muestra claramente desde dónde se importó cada referencia y permite saltar ahí con un atajo.
      VSCode también hace algo parecido con plugins y LSP, pero mucho peor. La navegación de código es tan lenta que no puedo trabajar con VSCode.
      Me pregunto si este tipo de propuesta solo es útil cuando no existen esas herramientas. Al menos en un entorno profesional, parece imposible vivir sin ellas.
    • Así puedes pasarle parámetros a foo.init(), algo que no se puede hacer con un import “desnudo”.
    • Si es un lenguaje donde el control de flujo gira en torno a excepciones, creo que el barco de “fácil de razonar” ya zarpó.
      Eso no significa que este proyecto no tenga valor; más bien lo veo como una obra de arte.
    • 100% de acuerdo.
      Una vez hice un fork de Ruby para que require no sobrescribiera la tabla de símbolos, pero el ecosistema de Ruby parecía depender demasiado del estado global mutable compartido, así que al final perdí el interés en Ruby como tal.
  • Las excepciones siempre me parecieron malas porque hacen difícil entender el contrato entre quien llama y quien es llamado, y además aumentan el acoplamiento del código.
    Prefiero el enfoque de manejarlo con valores de retorno, como en Go o Rust. Al echarle un vistazo rápido al lenguaje, no sé bien si hay algo que resuelva este problema.
    Si el IDE pudiera detectar dinámicamente todas las excepciones no capturadas de una función y permitir saltar a los lugares donde pueden lanzarse, este modelo podría estar bien. Pero no sé cómo manejaría el acoplamiento, y el grafo de flujo de control parece que se volvería extremadamente inestable.

    • En Java, IntelliJ hace exactamente eso. Si hay una función que lanza una excepción y algún llamador en el proyecto no la captura, lo marca como problema, y es fácil ir a la implementación o a los puntos de llamada.
      Claro que en Java, salvo que herede de RuntimeException, las excepciones que una función puede lanzar forman parte de la firma de la función. Si lanzas una excepción sin agregarla a la firma, no compila.
      Las condiciones de Java hacen mucho más fácil que el IDE reporte excepciones no capturadas, pero si no son excepciones de runtime, es un problema que se puede resolver con análisis estático.
      En cambio, devolver valores envueltos estandarizados como Ok/Err parece más simple tanto para el soporte de herramientas como para la comodidad del desarrollador.
    • Literalmente no hay ninguna diferencia entre lanzar una excepción y devolver una excepción como variable.
      La única diferencia es que, con el enfoque de pasar excepciones, tienes que escribir a mano el código repetitivo que el compilador podría hacer por ti.
      No entiendo cómo alguien con una cabeza que funcione normalmente en 2024 querría hacer eso a mano.
    • Creo que me habría gustado más Go si no se tragara tan naturalmente el contexto de los errores.
      Durante el tiempo que lo usé, encontrar la causa raíz sin depurador fue mucho más doloroso.
  • El ejemplo de toss dice que “se usa principalmente para pasar varios valores fuera de una función y no es estrictamente necesario, pero es lindo”, pero lejos de ser inútil, en realidad implementa generadores reanudables.
    Claro, puede volverse bastante interesante si quieres hacer algo distinto de reanudar de inmediato. Solo tienes que organizar todo el codebase como una pila interna-externa de toss.

    • No es exactamente lo mismo. Un generador reanudable puede reanudarse en cualquier punto posterior del programa, pero aquí return tiene que estar léxicamente acotado al manejador.
      Por ejemplo, en Python puedes llamar a next() desde cualquier lugar.
      Esto se parece más a pasar un callback por un canal lateral. toss llama a ese callback, y return literalmente retorna desde ahí.
    • Eso fue exactamente lo primero que pensé al leerlo.
      En otros lenguajes sería una función realmente útil, pero aquí se trata como algo lindo y descartable; casi parece un chiste.
    • Se parece más a la propagación de eventos basada en pila.
    • ¿Es como yield en C#?
    • Pensé lo mismo al principio. Un lenguaje pequeño y elegante con generadores suena bien.
  • Hurl parece bastante cercano al sistema de condiciones al estilo Smalltalk o Common Lisp.
    Desenrollar la pila y reanudar son solo dos de los reinicios posibles: https://gigamonkeys.com/book/beyond-exception-handling-condi...

  • Más allá del proyecto en sí, creo firmemente que el mundo sería un lugar mejor si más cosas usaran la extensión .wtf en sus dominios.

  • Esto suena como una forma débil de efectos algebraicos, pero sigue siendo genial ver lenguajes así y lo que se puede hacer con ellos.

    • Nunca entendí los efectos algebraicos, pero sí entiendo la documentación de Hurl.
      Si los efectos algebraicos son básicamente algo como la palabra clave toss de Hurl, me pregunto en qué sentido son más potentes que toss.
  • Es un experimento mental interesante.
    Detesto de verdad las excepciones y quiero un lenguaje sin excepciones.
    Las excepciones son el goto de nuestra era.
    Teniendo Maybe/Option y Effect/Result, casi no hay razón para lanzar excepciones y tener que rastrear mentalmente dónde se manejan.
    Me preocupa un poco que los efectos algebraicos ganen más popularidad y terminen influyendo también en el ya demasiado complejo JS de frontend, porque fomentan lanzar “excepciones” para controlar el flujo.
    Si todo esto es para evitar el problema de los colores async/sync, para mí no vale la pena en absoluto.

  • Wow, esto no me gusta. Pero, curiosamente, tiene algo casi elegante.
    Es muy difícil de modelar mentalmente, pero aun así.
    Hablando un poco más en serio, me gustaría que hubiera una sintaxis catch distinta para excepciones reanudables y no reanudables. Así desaparecería la ambigüedad sintáctica sobre si return devuelve el flujo de control al lado que lanzó la excepción inmediata más cercana o no.
    Y la biblioteca estándar no debería escaparse usando funciones normales que devuelven valores. Que te dé acidez comer tu propia comida para perros no es motivo para no comerla.

    • Exacto. Aunque otra alternativa sería hacer que, cuando se llama a una función en una posición donde se espera un valor, la sintaxis la capture automáticamente.
  • ¿Entendí bien que lo que se hace con hurl se puede capturar, pero lo que se hace con toss no? Va a tomar un tiempo acostumbrarse.
    También me preocupa cuánto Hurl tendría que escribir antes de que la gente empiece a llamarme tosser.

  • Toss suena como una construcción de lenguaje interesante. Sube por la pila buscando un manejador de excepciones y luego vuelve al punto original para reanudar la ejecución como si nada hubiera pasado.
    Parece que con esta construcción se podría inyectar comportamiento adicional en runtime.
    Normalmente, en código orientado a objetos, la inyección de dependencias se hace mediante constructores de servicios, pero ¿toss permite hacerlo con “manejadores de toss”?

    • Mira Koka. Es un lenguaje “real” con un sistema de efectos algebraicos.
      https://koka-lang.github.io/koka/doc/book.html#why-handlers
    • Es bastante parecido al sistema de condiciones de Common Lisp.
      No sé mucho, pero sí sé que permite inyectar comportamiento en runtime de esta manera.
    • Se parece a Resume Next de VB.