quinta-feira, 3 de setembro de 2009

web-scheme para Chicken 4

Vou aproveitar a transição de versões do sistema Chicken (versão 3 -> versão 4, com algumas incompatibilidades) para lançar uma versão nova de web-scheme, totalmente incompatível com a versão anterior (para Chicken 3). Eu mesmo provavelmente serei o maior prejudicado pelas incompatibilidades. Sem exagero, devo ter algumas dezenas de milhares de linhas de código usando web-scheme e Chicken 3. Mas o momento é esse.

Se bem me lembro, a extensão web-scheme foi lançada em 2005 e, desde então, venho pensando em algumas melhorias que só podem ser implementadas quebrando a compatibilidade com versões anteriores. Como Chicken 4 de certa forma faz isso, vou aproveitar a onda. :-)

As duas maiores modificações são sintáticas.

Uma das coisas que me incomodam em web-scheme para Chicken 3 é o mapeamento direto de nomes de tags para procedimentos. Inicialmente me pareceu uma boa idéia, e na maioria dos casos é, mas há situações que tornam esse aspecto inconveniente. Por exemplo, o procedimento equivalente à tag map, deveria, a rigor, ser map. Obviamente isso causa problemas em Scheme.

Casos semelhantes ocorrem com tags com nomes muito usuais como select (Chicken define uma macro com esse nome), title, i ou object.

Em Chicken 4 há a possibilidade de se importar um módulo prefixando símbolos do módulo importado. Então, por exemplo, seria possível importar o módulo web-scheme prefixando os procedimentos por ws:. Nos casos como o do procedimento map, não usar um prefixo seria suicídio. É difícil algum código em Scheme não fazer uso de map. web-scheme para Chicken 3 coloca "de fábrica" o prefixo ws: nos procedimentos map e select, para evitar conflito de nomes com os respectivos procedimento e macro correspondentes. Isso já é meio feio em Chicken 3, em que não há uma forma direta para se importar uma biblioteca de extensão com um prefixo -- em Chicken 4 seria muito feio, pois se o usuário decide prefixar os símbolos do módulo, ficarão dois prefixos para map e select!

Decidi, então, modificar o nome de todos os procedimentos correspondentes a tags HTML e usar uma notação semelhante à usada por Hop. Assim, em vez de (pre "texto"), tem-se (<pre> "texto").

Não estou totalmente satisfeito com esta notação, pois é conflitante com a convenção de nomes de classes quando se usa um sistema de objetos. Em Scheme não chega a ser um grande problema, pois o uso de classes não é muito usual. Em Common Lisp essa decisão seria mais problemática, pois CLOS é bastante usado. Optei por essa sitaxe porque claramente lembra a sintaxe de tags HTML e provavelmente desenvolvedores para a Web associarão <pre> à tag pre de HTML e não à classe pre de algum código parte de um sistema de objetos.

A outra modificação é com relação à sintaxe para atributos e valores de atributos de tags. Em web-scheme para Chicken 3 usa-se da seguinte forma:

(a 'href "http://minha-url.com" "Minha URL")


Em web-scheme para Chicken 4 decidi usar parâmetros por palavra-chave para representar atributos. Fica, então, assim:

(<a> href: "http://minha-url.com" "Minha URL")


As razões para esta mudaça são puramente técnicas. No esquema usado em web-scheme para Chicken 3, precisei implementar um parser para extrair atributos e seus respectivos valores. Usando parâmetros por palavra-chave, ganho o parser "de brinde" de Chicken, melhor e mais rápido.

O esquema de atributos usado por web-scheme para Chicken 3 tem uma limitação chata quando espera-se poder usar ou não atributos em função do fluxo de execução do código. Exemplo: no caso de um procedimento text-input como um wrapper para o elemento input de tipo texto:

(define (text-input text #!key maxlength)
(if maxlength
(input 'type "text" 'maxlength maxlength 'value text)
(input 'type "text" 'value text)))


Em web-scheme para Chicken 3 não há uma forma "fácil" (i.e., sem eval ou macros) de fazer isso sem duplicar código. Em web-scheme para Chicken 4 bastará:

(define (text-input text #!key maxlength)
(<input> type: "text" maxlength: maxlength value: text)


O uso de parâmetros por palavra-chave dá essa flexibilidade, o que é tratado internamente pelo código que gera o HTML. Se o valor de um parâmetro for #f o par atributo/valor não é gerado. Em web-scheme para Chicken 3 geraria uma certa complicação nos casos de atributos que não requerem valor, como selected em elementos select.

Essas serão as modificações mais radicais. Haverá também modificações nos procedimentos extras de web-scheme, os quais não estão diretamente relacionados ao mapeamento tags->procedimentos. Mas essa parte ainda está em estudo.