Quanto pior, melhor

05/04/2013

Esta é a tradução para The Rise of "Worse is Better"
Eu e quase todos os projetistas de Common Lisp e CLOS, temos estado em contato direto com o estilo MIT/Stanford de design. A essência deste estilo pode ser captada pela frase "a-coisa-certa". Para estes projetistas, é importante cobrir as seguintes características:
  • Simplicidade: o projeto deve ser simples, tanto na implementação quanto na interface. É mais importante para a interface ser simples do que é para a implementação.
  • Exatidão: o projeto deve ser correto em todos os aspectos observáveis. Incorreções simplesmente não são permitidas.
  • Consistência: o projeto não deve conter inconsistencias. Para evitar inconsistências é admissível que o projeto seja ligeiramente menos simples e menos completo. Consistência é tão importante quanto é a exatidão.
  • Completude: o projeto deve cobrir tantas situações relevantes quantas forem possíveis. Todos os casos razoavelmente esperados devem ser cobertos. A simplicidade não deve reduzir a exatidão.
Eu acredito que muita gente irá concordar que estas são ótimas características. Chamarei o uso desta filosofia de projeto pelo termo 'Abordagem MIT'. Common Lisp (juntamente com CLOS) e Scheme representam a abordagem MIT no que se refere a design e implementação.
A filosofia do quanto-pior-melhor é levemente diferente:
  • Simplicidade: o design deve ser simples, tanto na implementação quanto na interface. É mais importante para a implementação ser simples do que é para a interface. Simplicidade é a questão mais importante em um projeto.
  • Exatidão: o projeto deve ser correto em todos os aspectos observáveis. É ligeiramente melhor ser simples do que ser correto.
  • Consistência: o projeto não deve possuir inconsistências demais. Em alguns casos, a consistência pode ser sacrificada em prol da simplicidade. No entanto, é melhor cortar as partes do projeto que abordam situações incomuns, do que introduzir inconsistências e/ou complexidade na implementação.
  • Completude: o projeto deve cobrir tantas situações relevantes quantas forem possiveis. Todos os casos razoavelmente esperados devem ser cobertos. A completude pode ser sacrificada em prol de qualquer outra qualidade. Na verdade, a completude deve ser descartada sempre que a simplicidade na implementação estiver comprometida. A consistência pode ser abandonada para se atingir a completude, desde que a simplicidade permaneça; em especial, a consistência na interface não tem valor.
Os antigos sistemas Unix e a linguagem C são exemplos de uso desta escola de design, e eu chamarei o uso desta estratégia de design de "Abordagem New Jersey". Eu deliberadamente caricaturizei a filosofia do quanto-pior-melhor para lhe convencer de que é obviamente uma filosofia ruim, e que a abordagem New Jersey é uma abordagem ruim.
Contudo, eu acredito que o quanto-pior-melhor, por mais esquisito que seja, possui mais chances de sobrevivência do que a-coisa-certa, e que a abordagem New Jersey quando usada para software é uma abordagem superior à abordagem MIT.
Deixe-me começar recontando uma história que mostra que a diferença MIT/New-Jersey é válida, e que os defensores de cada filosofia acreditam de fato que a sua filosofia é superior à outra.
Duas pessoas famosas, uma do MIT e a outra de Berkeley (mas trabalhando no Unix) se encontrarão certa vez para discutir sobre questões relacionadas a sistemas operacionais. A pessoa do MIT era conhecida pelo ITS (o sistema operacional do laboratório de IA do MIT), e estava estudando o código do Unix. Essa pessoa estava interessada em saber como o Unix resolvia o problema do PC (program counter) perdido. Este problema ocorre quando um programa-usuário invoca uma rotina do sistema para executar alguma operação demorada, a qual pode conter informações de estado, tais como IO buffers. Se ocorrer uma interrupção durante a operação, o estado do programa-usuário deve ser salvo. Devido ao fato de que uma chamada de rotina do sistema é, em geral, formada por uma única instrução, o registrador PC do programa-usuário não captura adequadamente o estado do processo. A rotina do sistema deve ou retroceder ou continuar o processamento. O correto, seria retroceder e restaurar o registrador PC do programa-usuário para a instrução que fez a chamada, de modo que ao continuar com a execução após uma interrupção, por exemplo, a rotina do sistema seja re-executada. O problema é chamado de 'PC perdido' porque o registrador PC é forçado a entrar em um 'modo perdedor', no qual 'perdedor' é um nome carinhoso dado para o 'usuário' lá no MIT.
O cara do MIT não encontrou nenhum código que tratasse esse tipo de caso, e perguntou ao cara de New Jersey como o problema era tratado. O cara de New Jersey disse que o pessoal do Unix estava ciente do problema, mas que a solução consistia em sempre terminar a rotina do sistema, no entanto, ás vezes um código de erro poderia ser retornado indicando que a rotina do sistema havia falhado. Um programa-usuário correto, então, teria que verificar o código de erro para determinar quando executar a rotina do sistema novamente. O cara do MIT não gostou desta solução porque ela não era o jeito certo.
O cara de New Jersey disse que a solução do Unix estava certa pois a filosofia de design do Unix era baseada na simplicidade, e que o jeito certo era complexo demais. Além de que, os programadores poderiam inserir uma linha extra de teste e de laço. O cara do MIT ressaltou que a implementação era simples, mas a interface para a funcionalidade era complexa. O cara de New Jersey disse que esta troca foi determinada ao modo-Unix, a simplicidade na implementação era mais importante do que a simplicidade na interface.
O cara do MIT então murmurou que às vezes é necessário um homem durão para amaciar um frango, mas o cara de New Jersey não entendeu (Eu também não sei se entendi).
Agora eu quero argumentar que quanto-pior-melhor é melhor. C é uma linguagem de programação projetada para escrever o Unix, e foi projetada usando a abordagem New Jersey. A linguagem C é, deste modo, uma linguagem na qual é fácil de se escrever um compilador decente, e isto requer que o programador escreva código que seja fácil de ser interpretado pelo compilador. Algumas pessoas chamam o C de linguagem assembly de alto nível. Ambos, Unix antigo e compiladores C, possuem estrutura simples, são fáceis de portar, exigem poucos recursos da máquina para executar, e proveêm cerca de 50% a 80% do que você espera de um sistema operacional e de uma liguagem de programação.
Metade dos computadores que existem são piores do que a média (menores ou mais lentos). O Unix e C executam muito bem nestas máquinas. A filosofia do quanto-pior-melhor diz que a simplicidade de implementação tem a prioridade mais alta. Isto significa que o Unix e o C são mais fáceis de portar para estas máquinas. Portanto, pode-se supor que, se 50% da funcionalidade Unix e suporte ao C sejam caracteríticas satisfatórias, estes sistemas irão começar a aparecer em todos os lugares. E irão, não irão?
Unix e C são os vírus computacionais definitivos.
Outro benefício da filosofia do quanto-pior-melhor, é que o programador se condiciona a sacrificar um pouco de segurança, conveniência, e se preocupa em obter boa performance e uso modesto de recursos. Programas escritos utilizando a abordagem New Jersey funcionarão bem tanto nas máquinas pequenas como nas máquinas grandes, e o codigo será portável, pois foi escrito em cima de um vírus.
É importante lembrar que o vírus inicial tem que ser basicamente bom. Se for bom, o espalhamento viral estará garantido enquanto o código for portável. Uma vez que o vírus tenha se espalhado, haverá pressão para melhorá-lo, possivelmente para aumentar sua funcionalidade para algo perto dos 90%, no entanto, os usuários já estarão condicionados a aceitar o pior ao invés do correto. Portanto, o software do tipo quanto-pior-melhor em primeiro lugar deverá ganhar aceitação, em segundo lugar deverá condicionar seus usuários a esperar por menos, e em erceiro lugar deverá ser melhorado ao ponto de quase se tornar o correto. Em termos concretos, mesmo que os compiladores Lisp em 1987 fossem quase tão bons quanto os compiladores C, existiam muito mais experts que queriam melhorar os compiladores C do que experts que queriam melhorar os compiladores Lisp.
A boa notícia é que em 1995 nós teremos um bom sistema operacional e uma boa linguagem de programação; a má notícia é que eles serão Unix e C++.
Existe um último benefício no quanto-pior-melhor. Devido ao fato de que a linguagem e o sistema operacional de New Jersey não são poderosos o suficiente para se construir software monolítico e complexo, sistemas maiores deverão ser projetados prevendo o reuso de componentes. Portanto, floresce aí uma tradição de integração.
Como fica o "jeito-certo"? Existem dois cenários básicos: o cenário do 'sistema grande e complexo' e o cenário da 'jóia de diamante'.
O cenário do 'sistema grande e complexo' é assim:
Primeiro, o jeito certo precisa ser projetado. Entao a sua implementação deve ser projetada. Enfim é implementado. Devido ao fato de ser o "jeito-certo", o sistema tem algo próximo a 100% da funcionalidade desejada, e a simplicidade de implementação nunca foi algo a ser levado em conta, de modo que será necessário um grande período tempo para a implementação. É um sistema grande e complexo. Requer ferramentas complexas para o uso adequado. Os últimos 20% ocupam 80% dos esforços, e desse modo o 'jeito-certo' precisará de um grande período de tempo para ficar pronto, e rodará de maneira satisfatória apenas nos hardwares mais sofisticados.
O cenário da 'jóia de diamante' é assim:
O jeito-certo leva uma eternidade para ser projetado, mas é pequeno e compacto em todos os seus pontos. Para implementar de modo que execute com velocidade, ou é impossível ou além das capacidades da maioria dos implementadores.
Os dois cenários correspondem ao Common Lisp e Scheme.
O primeiro cenário é também o cenário para software de inteligência artificial clássico.
O 'jeito-certo' frequentemente é uma peça monolítica de software, no entanto, não existe outra razão para isto fora o fato de que o 'jeito-certo' é geralmente projetado para ser monolítico. É isto, esta característica deve-se ao acaso.
A lição a ser tirada disto é que geralmente é indesejável partir para o jeito-certo de primeira. É melhor ter metade da solução disponível de modo que se espalhe como um vírus. Uma vez que as pessoas se acostumaram, arrume tempo para melhorar a solução para algo perto dos 90% do 'jeito-certo'.
Uma lição errônea é tomar a parábola ao pé da letra e concluir que C é o veículo adequado para software de Inteligência Artificial. 50% da solução tem que ser basicamente correta, e neste caso não é.
Mas, pode-se concluir somente que a comunidade Lisp precisa repensar sériamente a sua posição sobre o design do Lisp. Voltarei a falar sobre isto mais tarde.
postado por dgv @ 21:33