2 pontos por GN⁺ 2024-05-27 | 1 comentários | Compartilhar no WhatsApp
  • Hurl é uma linguagem de programação experimental que constrói o fluxo de controle usando apenas tratamento de exceções, em vez de desvios ou laços comuns
  • Surgiu de uma conversa entre Nicole Tietz-Sokolskaya e amigos do Recurse Center, e o site oferece documentação de uso, exemplos, orientações de depuração e perguntas e respostas
  • Os depoimentos na página de introdução colocam deliberadamente um tom de brincadeira em primeiro plano, com frases como “monstrosity is beautiful” e “Certified unhinged™”
  • O código-fonte do Hurl e do site é aberto, mas para enviar patches por e-mail é necessário ceder os direitos do patch
  • A licença é oferecida em uma escolha entre AGPL-3.0, GAL-1.0 ou licença comercial

Um experimento de linguagem que deixou só o tratamento de exceções

  • Hurl foi criada com um único propósito: investigar se é possível uma linguagem composta apenas por fluxo de controle baseado em tratamento de exceções
  • A ideia veio de uma conversa entre Nicole Tietz-Sokolskaya e amigos do Recurse Center, cujas identidades não são divulgadas “por decoro”
  • O site oferece documentação de uso do Hurl, exemplos, orientações de depuração e perguntas e respostas

Parece piada, mas é um projeto publicado de verdade

  • A página de introdução traz depoimentos que revelam o caráter do Hurl
    • “This monstrosity is beautiful, and I must never touch it”
    • “Certified unhinged™!”
    • “is "🤮" an available quote?”
  • Para incluir depoimentos adicionais, é preciso enviar um e-mail a Nicole, com consentimento explícito para incluir a citação

Código-fonte e condições de licença

  • O código-fonte da linguagem Hurl e deste site está publicado no repositório do Hurl
  • Se encontrar bugs ou erros, você pode enviar patches por e-mail, mas precisa ceder todos os direitos sobre o patch
    • É uma condição para manter a possibilidade de relicenciamento e de licenciamento comercial
  • O projeto pode ser usado sob uma das três licenças a seguir
    • AGPL-3.0

1 comentários

 
GN⁺ 2024-05-27
Opiniões no Hacker News
  • Se eu fosse projetar uma linguagem de programação, forçaria namespaces em include/import e, se possível, também impediria efeitos colaterais no nível superior.
    Receber algo como let foo = include "lib/foo.hurl" e chamar foo.init() é muito mais fácil de raciocinar.
    Por outro lado, se depois de include "lib/foo.hurl" // side effects aparece baz(buz), fica difícil saber se a função e a variável são da biblioteca padrão ou se vieram de algum include em algum lugar.

    • Quando não há binding explícito de nomes, é melhor forçar que a instrução import e o namespace coincidam.
      import "foo/bar" deveria permitir usar foo.* ou bar.*, não bazz.*. Go vem logo à mente.
    • Não é que eu discorde, mas o IntelliJ que uso no trabalho mostra claramente de onde cada referência foi importada e também permite navegar até lá com atalhos.
      O VSCode também faz algo parecido com plugins e LSP, mas é muito pior. A navegação pelo código é tão lenta que não consigo trabalhar com VSCode.
      Fico me perguntando se esse tipo de proposta só é útil quando essas ferramentas não existem. Pelo menos em ambientes profissionais, parece impossível viver sem elas.
    • Assim dá para passar parâmetros para foo.init(), algo impossível com um import puro.
    • Se o controle de fluxo é centrado em exceções, acho que o barco do “fácil de raciocinar” já zarpou.
      Isso não quer dizer que o projeto não tenha valor; pelo contrário, eu o vejo como uma obra de arte.
    • Concordo 100%.
      Já fiz um fork de Ruby para que require não sobrescrevesse a tabela de símbolos, mas o ecossistema Ruby parece depender demais de estado global mutável compartilhado, então acabei perdendo o interesse pelo próprio Ruby.
  • Exceções sempre me pareceram ruins, porque dificultam entender o contrato entre chamador e chamado e também aumentam o acoplamento do código.
    Prefiro a abordagem de tratar isso via valores de retorno, como em Go ou Rust. Ao passar os olhos pela linguagem, não ficou claro para mim se há algo que resolva esse problema.
    Se uma IDE conseguisse descobrir dinamicamente todas as exceções não capturadas de uma função e permitir pular para os pontos onde exceções podem ser lançadas, esse modelo poderia ser aceitável. Mas não sei como o acoplamento seria tratado, e o grafo de fluxo de controle parece que ficaria extremamente instável.

    • Em Java, o IntelliJ faz exatamente isso. Se há uma função que lança uma exceção e ela não é capturada por algum chamador em algum lugar do projeto, ele marca como problema e permite navegar facilmente para a implementação ou para o local da chamada.
      Mas, em Java, a menos que a exceção herde de RuntimeException, as exceções que uma função pode lançar fazem parte da assinatura da função. Nesse caso, se você lançar uma exceção sem adicioná-la à assinatura, o código não compila.
      As condições de Java tornam muito mais fácil para a IDE reportar exceções não capturadas, mas, no caso de exceções que não sejam de runtime, é um problema que dá para resolver com análise estática.
      Por outro lado, retornar valores encapsulados padronizados Ok/Err parece mais simples tanto para suporte de ferramentas quanto para conveniência dos desenvolvedores.
    • Literalmente não há diferença alguma entre lançar uma exceção e retornar uma exceção como variável.
      A única diferença é que, no modelo de passar exceções adiante, você precisa escrever à mão o código repetitivo que o compilador escreveria por você.
      Em 2024, não consigo entender por que alguém com a cabeça funcionando normalmente iria querer fazer isso à mão.
    • Eu teria gostado mais de Go se ele não engolisse tão naturalmente o contexto dos erros.
      Durante o tempo em que usei, era muito mais doloroso encontrar a causa raiz sem um depurador.
  • O exemplo de toss diz que ele “é usado principalmente para passar vários valores para fora de uma função e não é estritamente necessário, mas é bonitinho”; isso não é inútil — é uma implementação de geradores retomáveis.
    Claro, se você quiser fazê-lo fazer outra coisa além de retomar imediatamente, pode ficar bem interessante. Basta estruturar toda a base de código como uma pilha de dentro-para-fora de toss.

    • Não é exatamente a mesma coisa. Um gerador retomável pode ser retomado em algum ponto posterior do programa, mas aqui o return precisa estar lexicamente escopado ao handler.
      Por exemplo, em Python você pode chamar next() de qualquer lugar.
      Isso está mais próximo de passar um callback por um canal lateral. toss chama esse callback, e return literalmente retorna dali.
    • Foi exatamente o primeiro pensamento que tive depois de ler.
      Em outra linguagem, seria um recurso realmente útil; aqui, tratá-lo como um recurso fofo e descartável parece até meio piada.
    • É mais parecido com propagação de eventos baseada em pilha.
    • Seria algo como yield em C#?
    • Tive o mesmo primeiro pensamento. Uma linguagem pequena e legal com geradores, nada mal.
  • Hurl parece bastante próximo de um sistema de condições no estilo Smalltalk ou Common Lisp.
    Desenrolamento da pilha e retomada são apenas duas das reinicializações possíveis: https://gigamonkeys.com/book/beyond-exception-handling-condi...

  • Separado do projeto em si, acredito firmemente que o mundo seria um lugar melhor se mais coisas usassem a extensão .wtf em seus domínios.

  • Isso soa como uma forma fraca de efeitos algébricos, mas ainda é legal ver uma linguagem assim e o que se pode fazer com ela.

    • Nunca entendi efeitos algébricos, mas entendo a documentação do Hurl.
      Se efeitos algébricos são basicamente algo como a palavra-chave toss do Hurl, fico curioso sobre em que ponto eles são mais poderosos que toss.
  • É um experimento mental interessante.
    Eu realmente odeio exceções e quero uma linguagem sem elas.
    Exceções são o goto da nossa era.
    Com Maybe/Option e Effect/Result, quase não há motivo para lançar exceções e precisar rastrear mentalmente onde elas são tratadas.
    Fico um pouco preocupado que efeitos algébricos fiquem mais populares e influenciem até o JS de frontend, que já é complexo demais, porque eles incentivam lançar “exceções” para controle de fluxo.
    Se tudo isso é para evitar o problema das cores assíncrono/síncrono, pessoalmente acho que não vale nem um pouco a pena.

  • Uau, eu odeio isso. Mas, estranhamente, há algo quase elegante.
    É muito difícil de modelar mentalmente, mas ainda assim.
    Falando um pouco mais a sério, eu gostaria que houvesse sintaxes catch diferentes para exceções retomáveis e não retomáveis. Isso removeria a ambiguidade sintática sobre se return devolve ou não o fluxo de controle para o lado que lançou a exceção imediata mais próxima.
    E a biblioteca padrão não deveria fugir para funções comuns que retornam valores. Não é porque você experimentou a própria comida e teve azia que isso é motivo para não comê-la.

    • Sim. Mas, como outra alternativa, quando uma função fosse chamada em uma posição onde se espera um valor, a sintaxe poderia fazer o catch automaticamente.
  • Entendi certo que coisas lançadas com hurl podem ser capturadas, mas coisas lançadas com toss não podem? Vai levar um tempo para acostumar.
    Também me preocupa quanto Hurl eu vou precisar escrever até as pessoas começarem a me chamar de tosser.

  • Toss soa como uma construção de linguagem interessante. Ela sobe pela pilha procurando um handler de exceção e depois volta ao ponto original, retomando a execução como se nada tivesse acontecido.
    Parece que essa construção permite injetar comportamento adicional em tempo de execução.
    Normalmente, em código orientado a objetos, fazemos injeção de dependências por meio de construtores de serviços; toss permitiria isso por meio de “handlers de toss”?

    • Veja Koka. É uma linguagem “de verdade” com um sistema de efeitos algébricos.
      https://koka-lang.github.io/koka/doc/book.html#why-handlers
    • É bem parecido com o sistema de condições do Common Lisp.
      Não sei muito sobre ele, mas sei que permite injetar comportamento em runtime dessa forma.
    • Parece o Resume Next do VB.