Elmord's Magic Valley

Software, lingüística e rock'n'roll. Às vezes em Português, sometimes in English.

So many parens

2013-04-17 17:17 -0300. Tags: comp, prog, lisp, em-portugues

Pode-se dividir as pessoas em dois grupos, segundo sua reação ao serem apresentadas a features novas em uma linguagem de programação:

(Na verdade o que provavelmente existe é um continuum de quanta justificativa é necessária para provocar a reação "whoa, que legal" em uma dada pessoa, but I digress.) O que acontece aí é que eu (que estou no primeiro grupo, mostly) freqüentemente acho complicado pensar em um exemplo de uso de uma feature que convença alguém do segundo grupo de que a feature é interessante. Por exemplo, a pessoa me pergunta "mas por que Lisp tem tantos parênteses?", e eu respondo "Macros!", e a pessoa "why macros?", e eu "code transformation!", e a pessoa "why code transformation", e eu "what do you mean, why?".

Pois, agora acho que encontrei um exemplo decentemente convincente. Meu TCC consiste, entre outras coisas, em desenvolver uma linguagem de programação e integrá-la ao ambiente de programação DrRacket. Para isso, estou implementando a linguagem em Racket, um descendente de Scheme, uma linguagem da família Lisp. Dentre as inúmeras bibliotecas que acompanham o Racket, estão a parser-tools/lex e parser-tools/yacc. Essas bibliotecas implementam funcionalidade equiparável aos famosos programas lex e yacc, que geram analisadores léxicos e sintáticos, respectivamente, a partir de arquivinhos de regras.

A diferença, entretanto, é que as parser-tools são implementadas como macros em Racket, de maneira integrada com o resto da linguagem. Por exemplo, ao invés de usar um programa separado que lê um arquivo meio-em-C, meio-em-lex e gera um arquivo em C, a parser-tools/lex provê uma macro lexer que, quando compilada, produz uma função que recebe uma stream e devolve a próxima token encontrada na stream. Algo do tipo:

(define example-lexer
  (lexer
    [expressão  valor-a-retornar]
    [expressão  valor-a-retornar]
    ...))

O parser funciona de maneira análoga. As macros, assim, permitem estender a linguagem com recursos sintáticos novos, sem que se tenha que usar ferramentas externas para fazer transformações de código (que potencialmente exigem reparsear o texto do programa, ao contrário do que acontece com as macros, que já recebem o programa pré-parseado como argumento). A vantagem da sintaxe uniforme (i.e., so-many-parens) é que os novos recursos adicionados mantêm a mesma cara do resto da linguagem, e que o parser do Lisp (que executa antes de a macro ser chamada) pode fazer o seu trabalho sem se preocupar com a semântica dos objetos que está parseando.

A conseqüência dessa integração é que o threshold a partir do qual vale a pena escrever uma transformação de código ao invés de fazer as coisas na mão é muito mais baixo em um Lisp do que em uma linguagem convencional. Por exemplo, certa vez eu estava escrevendo um typechecker/interpretador para uma linguagem simples. Logo que eu comecei a escrever, percebi que seria uma boa eu usar pattern matching para testar com que tipo de expressão o interpretador tinha se deparado. Por exemplo, ao invés de escrever algo do tipo:

;; Avalia a expressão 'expr' no ambiente de variáveis 'env'.
(defun evaluate (expr env)
  (cond 
    ;; Se a expressão for do tipo (+ x y), devolve x+y.
    ((eq (first expr) '+) (+ (evaluate (second expr) env)
                             (evaluate (third expr) env)))
    ;; Se for (if t x y), avalia o teste e a sub-expressão correspondente.
    ((eq (first expr) 'if) (if (evaluate (second expr) env)
                               (evaluate (third expr) env)
                             (evaluate (fourth expr) env)))
    ...))

eu queria poder escrever algo do tipo:

(defun evaluate (expr env)
  (match-case
    ((+ x y) (+ (evaluate x env) (evaluate y env)))
    ((if test then else) (if (evaluate test env)
                             (evaluate then env)
                           (evaluate else env)))
    ...))

Em 17 linhas (não muito bonitas, mas só porque eu não estava muito preocupado com isso quando as escrevi) de Common Lisp, eu implementei uma macro match-case e segui escrevendo o programa. Se ao invés disso eu tivesse que escrever um programa externo para transformar o código, provavelmente não teria valido a pena o esforço.

#<eof>.

Comentários / Comments (9)

Menininha Feliz, 2013-04-17 20:57:50 -0300 #

Whoa, que legal! =DDDD


Marcus Aurelius, 2013-04-18 12:23:21 -0300 #

Minha categoria:

- Whoa, que legal -- Quando programando sozinho.
- Que complicação! -- Quando num time de 15 pessoas com habilidades diversas.

Até em Java, que [dentre as linguagens que lembro agora] só perde para Pascal em termos de rigidez e burocracia, dá um trabalhinho garantir que toda a equipe saiba que:

"a" + "b" + "c"; é eficiente porque é concatenado em tempo de compilação.

new StringBuilder().append("a").append("b").append("c"); é ineficiente porque concatena em tempo de execução o que poderia ter sido concatenado em tempo de compilação.

while(...) var1 += var2; é ineficiente porque cria muitas strings temporárias

while(...) builder.append(var2); é eficiente porque usa uma estrutura de dados melhor para fazer a concatenação.

Aí um carinha sabe todos os truques de Java, outro conhece os truques de JavaScript, outro conhece os truques de HTML+CSS, outro endente de autenticação, outro entende do Framework X, outro entende de SQL... E ninguém sabe como a coisa funciona como um todo.


Marcus Aurelius, 2013-04-26 10:13:47 -0300 #

Me lembrei de mim alguns anos atrás:

Já sabia criar funções em Lisp (ou Scheme), mas não tinha programado pra valer e ouvi dizer que Lisp tinha classes também. Fui ver como era e aí como se fazia. Olhei para o negócio e foi a decepção:

— Ah, é só um (defclass () (()()()())) com milhões de parâmetros... Que sem graça.

Talvez eu me reanime com Clojure. Ou não. Vai saber.


Vítor De Araújo, 2013-04-26 13:49:14 -0300 #

Com licença, generic functions > class methods. :P

(Aliás um dos posts previstos para os próximos n dias é mais ou menos sobre isso...)

Fora as loucuras de redefinição de classe em tempo de execução do Common Lisp. E before/after/around methods. E multimétodos, e... :P


Marcus Aurelius, 2013-04-26 15:22:03 -0300 #

Haha, não estou dizendo que essas coisas não sejam legais. Talvez eu não fosse iluminado o suficiente na época para ignorar os parênteses e estudar a semântica da coisa. :-)

Depois de aprender Python, agora talvez eu consiga ler somente baseado na indentação e esquecer os parênteses :-p


Vítor De Araújo, 2013-04-26 15:59:07 -0300 #

Bom, eu deixo meu syntax highlighting configurado assim:

http://inf.ufrgs.br/~vbuaraujo/foo/img/lisp-highlight.png

Faz uma bela diferença pra ler Lisp... :P


Vítor De Araújo, 2013-04-26 16:15:32 -0300 #

(Dito isso, nunca brinquei de Clojure, mas talvez seja mais útil aprender Clojure do que Common Lisp mesmo (embora eu duvido que seja tão divertido :P)...)


Marcus Aurelius, 2013-04-28 23:08:05 -0300 #

Desisti. Não vou aprender mais nada. Vou passar o resto da vida ouvindo a musquinha da menininha feliz (loituma).

Socorro!


Vítor De Araújo, 2013-04-29 00:12:32 -0300 #

Até agora eu não ouvi, por falta de Flash. :P


Deixe um comentário / Leave a comment

Main menu

Posts recentes

Comentários recentes

Tags

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

Elsewhere

Quod vide


Copyright © 2010-2020 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.