Elmord's Magic Valley

Computers, languages, and computer languages. Às vezes em Português, sometimes in English.

Checked exceptions

2012-04-18 01:03 -0300. Tags: comp, prog, rant, em-portugues

Passei a vida mantendo uma distância saudável do public static void fucking Java, com exceções esporádicas que ajudaram a reforçar o sentimento. Esse semestre, entretanto, estou fazendo a rica cadeira de Programação Paralela e Distribuída, cujos exercícios de laboratório devem ser todos feitos em Java. Nesses momentos de diversão incomensurável descobri mais uma das maravilhas dessa linguagem: o conceito de checked exceptions.

A idéia é genial: na declaração de um método, é possível adicionar uma cláusula throws exceção, que informa ao compilador que o método pode lançar a exceção em questão, e o programador é obrigado a tratá-la quando chamar o método, ou a declarar que o seu método também pode lançar a exceção. O resultado prático, obviamente, é que enquanto se está testando o programa acaba-se adicionando tratadores de exceção do tipo:

try { do_whatever(); } catch (Exception e) {}

Até que de fato queremos debugar um problema com do_whatever(), e queremos o bom e velho stack trace que acompanha a exceção, mas que está sendo engolido pelo catch vazio. Nesse momento, mudamos o tratador para:

try {
    do_whatever();
} catch (Exception e) {
    System.out.println(e);
    e.printStackTrace();
}

Que é, bem, o tratador de exceções padrão. Não é ótimo ter que escrever você mesmo o tratador de exceções padrão da linguagem?

Além de, de certa forma, matar o propósito das exceções (isolar o tratamento de erros do resto do código), o que os criadores da linguagem aparentemente não conceberam é que enquanto eu estou escrevendo o programa, ele não está pronto. Eu quero poder ir testando o programa à medida em que vou escrevendo, e não quero entupir o código com tratadores de exceção enquanto estou fazendo o código funcionar. De fato, o que eu quero normalmente é não tratar uma exceção, para obter um stack trace. Talvez eu sequer tenha decidido ainda qual a melhor maneira de tratar o erro. (O quê?! Você não tinha um projeto detalhado do programa antes de começar a escrevê-lo? Blasfêmia! Vá vestir sua gravata e volte para os diagramas UML.)

Lisp foi provavelmente a primeira linguagem a incorporar a idéia de que é útil executar programas incompletos, e o Common Lisp provavelmente ainda é a linguagem que faz isso com mais competência. Em (Common) Lisp, chamadas a funções inexistentes, chamadas com número errado de argumentos, tipos diferentes dos esperados, etc., geram erros de execução. Um bom compilador também gera warnings em tempo de compilação ao encontrar esses problemas, mas não impede a compilação e execução do programa. O programador pode então decidir corrigir os erros imediatamente, quando possível, ou executar o programa incompleto para fins de teste. Erros de execução em Common Lisp não encerram o programa, mas abrem o debugger, de onde se pode averiguar e alterar o estado do programa, e erros geralmente são continuáveis, i.e., é possível prosseguir com a execução de um programa após a ocorrência de um erro de execução. Além disso, as implementações oferecem um prompt interativo de onde se pode chamar as funções do programa diretamente, sem necessidade de escrever uma função main para testar funções internas do programa. O resultado é que se pode encontrar e corrigir os problemas do programa à medida em que ele é escrito, ao invés de escrever um código longo e "inteiro" e debugá-lo depois.

C, nesse e em outros quesitos, segue uma filosofia "não ajuda mas também não atrapalha". Boa parte dos erros de tipos geram warnings ao invés de erros. A linguagem não força o tratamento de erros de execução (mas também não lhe diz que eles ocorrem, além da sucinta mensagem Segmentation fault).

Hoje em dia as assim chamadas linguagens de script são mais flexíveis com programas incompletos, e muitas oferecem prompts interativos, embora estes costumem parecer afterthoughts*, especialmente comparados com o Common Lisp. Não matar o programa diante de erros e permitir continuar a execução ainda é uma idéia louca demais para essas linguagens.

Java, entretanto, destaca-se entre as demais linguagens em ativamente impedir a execução de programas incompletos, não por razões técnicas, como em C, mas por filosofia da linguagem. Ironicamente, um dos co-autores da especificação do Java é o Guy Steele, um dos criadores do Scheme, autor do Common Lisp, the Language, e um dos principais membros do comitê de padronização do Common Lisp. A vida tem dessas coisas.

* Appendix: o show de horrores

Desculpem o rant. Pretendo que eles sejam esporádicos no blog, e escrever conteúdo mais útil/interessante/informativo a maior parte do tempo. Mas sendo ranting a função primária de um blog, é impossível suprimi-la totalmente. Humanitas precisa reclamar.

E já que chegamos até aqui, vou aproveitar a atmosfera smug-lisp-weenie e fazer uma análise dos prompts interativos das linguagens de script mais populares.

Python: Python é whitespace-nazi o suficiente para reclamar se o código estiver indentado mas não estiver subordinado a nenhuma estrutura de controle (e.g., a primeira linha de código de um programa nunca pode estar indentada). O prompt, obviamente, não é exceção. Como conseqüência, não se pode copiar um trecho interno de uma função e colar diretamente no prompt para testar. Além disso, o prompt interativo prestativamente imprime ... quando espera que um comando de múltiplas linhas seja completado, o que significa que não se pode copiar o trecho de código sem alterações e colar de novo no prompt (afinal seta-pra-cima anda pelo histórico linha por linha, não expressão por expressão) ou no código-fonte.

Ruby: Não é whitespace-nazi, mas compartilha dos outros problemas.

Perl: Segundo o FAQ da linguagem, a maneira típica de usar a linguagem interativamente é entrando no debugger, com um comando do tipo perl -de 42. O debugger não tem suporte a readline (i.e., sem seta-pra-cima). O debugger não imprime o resultado das expressões. Há comandos para imprimir, mas...

  DB<1> @a = (1,2,3)

  DB<2> p @a
123
  DB<3> $a = [1,2,3]

  DB<4> p $a
ARRAY(0x209d968)

Perdeu pro GDB, hein? Lamentável.

PHP: A versão linha de comando do php tem um "modo interativo", que consiste simplesmente em ler o código da stdin as usual, mas executar as expressões à medida em que são lidas. Sem prompt, sem histórico, sem impressão das expressões por default. E sim, tem que digitar o <?php antes do código.

JavaScript: Eu ia avacalhar com o JavaScript por costume, mas depois das duas acima é complicado. O prompt do SpiderMonkey usa as conversões de string padrão do JavaScript, que transformam [[1,2,3],[4,5,6]] em "1,2,3,4,5,6", mas pelo menos é fácil de remendar. O histórico é linha-por-linha (aliás, o mísero e ignorado bash anda expressão por expressão no histórico), mas pelo menos o prompt não imprime um prefixo antes das linhas de continuação. E parece que as versões mais novas do SpiderMonkey imprimem os objetos numa sintaxe JSON-like, o que resolve o problema anterior e ganha do <main.Foo instance at 0x00234269> das linguagens acima. É, a coisa tá tão feia que o JavaScript ganhou de todo o mundo. A vida tem dessas coisas.

Comentários / Comments (3)

Marcus Aurelius, 2012-05-29 12:51:13 -0300 #

Nunca entendi como Guy Steele pôde participar ao mesmo tempo da criação do Scheme/Lisp e do Java. Achei que fosse encontrar a explicação aqui, mas não! Hahaha.

Se bem que acabei de inventar uma teoria: Java era pior ainda e ele deixou menos ruim.


Vítor De Araújo, 2012-05-29 23:44:01 -0300 #

Essa é uma idéia bastante perturbadora... :P

(Perturbadora porque verossímil... :P)


Vítor Rey, 2012-12-17 12:02:39 -0200 #

Bem, se pode deixar a coisa um pouquinho melhor:

public static void main(String[] args)e throws Exception { (...)

ou simplesmente fazer um catch na main com print :P

Infelizmente o resto das funções que tem exceções também vão ter que
receber um "throws Exception" na definição...


Deixe um comentário / Leave a comment

Main menu

Recent posts

Recent comments

Tags

em-portugues (213) comp (148) prog (71) in-english (62) life (49) unix (38) pldesign (37) lang (32) random (28) about (28) mind (26) lisp (25) fenius (22) mundane (22) web (20) ramble (18) img (13) rant (12) hel (12) scheme (10) privacy (10) freedom (8) esperanto (7) music (7) lash (7) bash (7) academia (7) copyright (7) home (6) mestrado (6) shell (6) android (5) conlang (5) misc (5) emacs (5) latex (4) editor (4) etymology (4) php (4) worldly (4) book (4) politics (4) network (3) c (3) tour-de-scheme (3) security (3) kbd (3) film (3) wrong (3) cook (2) treta (2) poem (2) physics (2) x11 (2) audio (2) comic (2) lows (2) llvm (2) wm (2) philosophy (2) perl (1) wayland (1) ai (1) german (1) en-esperanto (1) golang (1) translation (1) kindle (1) pointless (1) old-chinese (1)

Elsewhere

Quod vide


Copyright © 2010-2024 Vítor De Araújo
O conteúdo deste blog, a menos que de outra forma especificado, pode ser utilizado segundo os termos da licença Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International.

Powered by Blognir.