Elmord's Magic Valley

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

Posts com a tag: rant

Mail, mail, mail

2015-08-10 23:13 -0300. Tags: comp, rant, life, em-portugues

Como é o causo de quando em quando por aqui (e provavelmente mais quando do que se me apercebe), este post tem o objetivo único e exclusivo de reclamar da vida (com uma e outra informação útil solta, talvez, e meramente incidental). Com este anteaviso, prossigo o relato.

Até agora, o meu cliente de e-mail favorito era o Claws Mail. Agora parando para pensar, faz pelo menos uns 10 anos que eu uso o Claws Mail (quando eu comecei a usá-lo, ele ainda se chamava Sylpheed-Claws), e durante a maior parte desse tempo, eu fui um cliente satisfeito.

Eu sempre preferi manter cópia dos e-mails localmente, então eu baixava os e-mails via POP. (Além disso, no 3G era simplesmente inviável usar IMAP.) Em algum momento no ano passado, o INF resolveu defasar o POP em favor do IMAP. Durante um certo período, o POP simplesmente ficou fora do ar (desconfio que isso não tenha sido intencional). Além disso, o sistema anti-spam, que anteriormente marcava as mensagens como spam adicionando [***SPAM***] ao subject, passou a mover as mensagens para uma pasta Junk, que não vem pelo POP. Como é da natureza dos anti-spams marcar coisas como e-mails de confirmação de inscrição como spam, ficar sem ver essa pasta não era factível. Conclusão: tive que migrar para o IMAP.

Turned out, entretanto, que o suporte ao Claws a manter as pastas de IMAP sempre sincronizadas para uso offline era menos que excelente (e acho que nessa época eu ainda usava 3G), então usar o IMAP da INF diretamente não era viável. Além disso, nesse meio tempo eu comecei a ler e-mail pelo celular, e ter as contas não-sincronizadas no celular e no PC estava meio desagradável. A solução que eu encontrei então foi usar um programinha chamado OfflineIMAP, que permite manter uma mailbox local sincronizada com um servidor IMAP. Diferentemente dos fetchmails da vida, a sincronização do OfflineIMAP é bidirecional: mudanças em qualquer uma das pontas são propagadas para a outra. O problema é que o OfflineIMAP só suporta mailboxes no formato Maildir, e o Claws só suporta mailboxes no formato MH. A solução comumente adotada nessa situação é instalar um servidor IMAP local (eu usei o Dovecot), fazer o OfflineIMAP sincronizar o servidor IMAP remoto com o servidor IMAP local (ao invés de uma pasta Maildir), e apontar o cliente de e-mail para o IMAP local. Deu um trabalhinho, mas consegui.

Um inconveniente disso é que se você quiser atualizar os e-mails manualmente ao invés de esperar o próximo momento de atualização, é necessário mandar um sinal para o processo (pkill -USR1 offlineimap). Outro inconveniente é que o OfflineIMAP não consegue identificar que uma mensagem foi movida para outra pasta; na sincronização, ele deleta a mensagem original na outra ponta e faz upload da mensagem para a pasta nova. A primeira conseqüência disso é que não é possível mover coisas para a pasta Junk localmente, porque o IMAP da INF não aceita fazer uploads de mensagens novas para a pasta Junk, apenas que mensagens de outras pastas sejam movidas para ela. A outra conseqüência é que e-mails grandes movidos localmente precisam ser uploadeados novamente, o que é lento e sujeito ao famoso trancaço da INF, uma doença que afeta todos os serviços da INF (IMAP, POP, HTTP, SSH, you name it) desde absolutamente sempre, em que o servidor da INF esquece da conexão e o upload/download simplesmente trava. Baixar os e-mails por POP via 3G era algo que quase sempre exigia múltiplas tentativas (e como POP não suporta "resumar" o download de uma mensagem, os e-mails de 5MB da lista da graduação com PDFs e fotos de produtos à venda eram algo especialmente lamentável, em particular porque normalmente eu não tinha o menor interesse nos e-mails de 5MB em questão). Bons tempos. Mas o problema ainda persiste, em escala menor, com os uploads desnecessários de mensagens de 5MB do OfflineIMAP. Mas ok, no geral, tudo funciona mais ou menos bem.

O problema é que agora o Claws Mail não estava mais notificando a chegada de mensagens novas. As it happens, a idéia do Claws de o que é uma mensagem nova em uma conta IMAP depende da flag "Recent" mantida pelo servidor; essa flag diz se uma mensagem já foi de alguma forma vista por um cliente. Se uma mensagem não possui essa flag, o Claws não a considera como uma mensagem nova, mesmo que ela esteja não-lida. De certa forma faz sentido, porque se você já abriu a mailbox em um outro aparelho e viu que a mensagem estava lá (mesmo sem tê-la aberto), você já sabe que ela existe e não precisa ser notificado. O problema é que a flag Recent é de inteiro controle do servidor IMAP – ela é removida automaticamente quando uam mensagem é "vista" e um cliente IMAP simplesmente não tem como readicionar a flag –, e o OfflineIMAP, no processo de baixar as mensagens para o servidor local, "vê" a mensagem. Well, fuck.

Na verdade o problema no Claws é pior, porque o Claws não aplica filtros automaticamente a mensagens que não sejam novas. Ok, dá para fazer a filtragem diretamente no servidor, configurando os filtros pelo webmail do serviço. O problema é quando o filtro deveria mover a mensagem para fora da mailbox. Por exemplo, eu tinha uma regra que movia todas as mensagens da CONLANG Mailing List para uma mailbox local (onde eu mantenho todo o archive da lista). Além de não aplicar os filtros a mensagens vistas, o Claws também não aplica filtros a mensagens novas que não estejam na Inbox da mailbox. Isso quer dizer que se eu adiciono um filtro no webmail para mover as mensagens da CONLANG para uma pasta "Conlang", e um filtro local para mover as mensagens novas em "Conlang" para o archive local, o filtro local não funciona.

Eu fiquei convivendo com essa situação assim mesmo por um bom tempo. Há uns meses atrás, eu me irritei com o Claws e fui ver se achava um outro cliente de e-mail que me agradasse. O primeiro que eu tentei foi o Thunderbird, que na ocasião eu não gostei não lembro mais por que razões específicas. No processo de procurar um cliente de e-mail novo eu acabei lembrando do Gnus, que foi o motivo original pelo qual eu acabei começando a usar o Emacs. No fim, eu resolvi parcialmente o meu problema com o Claws comentando fora o teste que limitava os filtros apenas à Inbox, larguei de mão o Gnus, e tudo ficou bem (com o efeito colateral de que eu comecei a usar o Emacs, so: yay). It soon turned out, entretanto, que a versão do RSSyl (o plugin leitor de feeds do Claws) que veio na versão que eu recompilei do Claws estava falhando em atualizar alguns feeds (na verdade ele parecia só estar perdendo um feed específico, o Little Strange World, mas eu não sabia quantos mais ele podia estar perdendo sem eu saber), então eu acabei voltando a usar o Claws pré-compilado do Debian e convivendo com seus defeitos. Era desagradável não ter barulhinho de notificação e ter que mover as mensagens da CONLANG para o archive local manualmente (eu acabei parando de mover), mas estava dando para engolir.

A última gota foi na última sexta-feira. Eu recebi um e-mail no começo da tarde com o subject "Matricula 2015/2 - URGENTE", me informando que eu não havia feito a matrícula nesse semestre e me pedindo para "nos informar uma posição a respeito". Isso me deixou bastante alarmado; eu achei que não era necessário me reinscrever na Dissertação de Mestrado, pois no Trabalho de Graduação na INF o camarada só se inscreve no semestre inicial e não é necessário fazer nada no semestre seguinte para continuar matriculado. Respondi explicando a situação e perguntando o que deveria fazer. Algum tempo depois, responderam-me da secretaria informando que eu deveria realizar a matrícula. A essa altura, entretanto, o período de matrícula já tinha encerrado há uma semana, então não era possível fazer isso pelo Portal do Aluno. Às 17h08 mandei um e-mail perguntando como eu deveria fazer a matrícula, e fui trabalhar no bendito código da minha dissertação. Turns out que às 17h09 tinha vindo uma resposta, perguntando em que disciplinas eu gostaria de me matricular, mas eu só vi isso por volta das 17h45, pois o Claws não notificou. A secretaria fecha às 17h30, o que significa que um problema que eu poderia ter resolvido no mesmo dia só seria resolvido na segunda (at best) por conta da tosquice do meu cliente de e-mail. Just kill me.

Eu passei o resto da sexta-feira tentando resolver o problema dos e-mails novos, e foi então que eu descobri o causo da flag Recent e o fato de que, como ela é controlada pelo servidor, não tem muito o que fazer (talvez haja alguma gambiarra, mas não encontrei). Resolvi então tentar usar o Claws direto com o IMAP da INF e evitar o OfflineIMAP, já que agora eu não uso mais o 3G. O que aconteceu foi algo ainda mais bizarro: algumas mensagens estavam sendo remarcadas como novas depois de já terem sido vistas (mas não lidas). Além disso, as mensagens que estavam sendo filtradas no servidor não estavam sendo marcadas como novas (não sei se porque o Claws considerou que mensagens novas fora da Inbox não eram dignas de serem chamadas de novas, ou se porque o sistema de filtragem no servidor acaba marcando as mensagens como "vistas" como efeito colateral, ou se por algum outro motivo). A essa altura eu enchi o saco do Claws e fui catar um cliente de e-mail novo.

(No processo de tentar figure out os problemas eu descobri uma porção de coisas legais, entretanto; a principal delas é que você pode usar o comando:

openssl s_client -crlf -connect servidor:porta

como uma espécie de netcat para abrir uma conexão SSL, e dar comandos de IMAP via terminal. Por sinal, eu achei bem mais conveniente fazer isso de dentro do Shell mode do Emacs do que por um emulador de terminal. But I digress.)

Thunderbird

Eu resolvi dar uma segunda chance para o Thunderbird/Icedove. And boy is it terrible. Para começar, o Thunderbird não faz a distinção entre mensagens novas e não-lidas que o Claws (quando funciona) faz; não existe um comando para saltar para a próxima mensagem nova, apenas para a próxima não-lida, o que é um problema porque eu tenho o costume de deixar mensagens que requerem atenção futura como não-lidas e não quero desmarcá-las sem querer enquanto leio mensagens novas. By the way, o atalho para ir para a próxima não-lida (N) não funciona quando você não está com uma pasta aberta (se você está na paginazinha da conta, por exemplo).

"Ah, mas você pode marcar as mensagens como 'starred' ao invés de marcar como não-lidas", right? A diferença é que uma pasta com uma mensagem não-lida aparece em negrito e com o número de mensagens não lidas ao lado do nome, o que me chama atenção para o fato de que há alguma pendência ali. Uma pasta com mensagens starred não mostra nenhuma indicação de que contém mensagens starred; o Claws pelo menos mostra um "✓" nos ícones das pastas que contêm mensagens marcadas, o que é bem pouco perceptível no meu tema com fundo preto, mas é melhor que nada. Por falar em tema, diversos elementos da interface do Thunderbird ignoram o meu tema e mostram elementos com cores incompatíveis, seja meramente destoantes do restante, seja com o texto da cor certa e o fundo da cor errada (e.g., branco no fundo cinza). Tudo bem que o meu tema é um arquivo gtkrc hacked together de qualquer jeito cinco anos atrás que eu basicamente nunca mais mexi, mas a maioria dos programas funciona relativamente bem com ele. Anyway.

O programa simplesmente não foi pensado para ser usado com o teclado. Coisas como alternar entre headers completos e simples não possuem um atalho de teclado; para adicionar um atalho, é necessário instalar um addon (!). O atalho do "quick filter" (o equivalente do search do Claws) é Ctrl+Shift+K; no Claws, é /. Aliás, o termo de pesquisa se perde quando se troca de pasta, e não tem histórico; no Claws o termo fica até a caixa ser fechada (com Esc, por exemplo), e ↑/↓ percorrem os últimos termos usados. Navegar pela lista de mensagens com as setas automaticamente seleciona a mensagem para visualização (o que a marca como lida, obviamente); no Claws, isso é configurável. O Claws permite habilitar o comportamento que era o default em aplicativos GTK 1, em que se pode parar sobre um comando de um menu qualquer e pressionar uma tecla para associá-la como atalho para aquele comando; assim, a maioria dos keybindings pode ser facilmente customizada. Alguns elementos da interface do Claws parecem ter sido projetados especificamente para funcionar bem com isso; por exemplo, o menu "Edit > Advanced" da janela de composição de mensagens contém entradas que parecem não fazer muito sentido em um menu, como "Move to beginning of line" e "Delete a word backward", até que você se dá conta de que eles estão ali para que você possa associar as teclas de atalho de sua preferência a esses comandos. Esse é o tipo de atenção a detalhes que torna o Claws totalmente excelente. Eu na verdade fiquei meio chocado com o fato de que um programa da idade do Thunderbird tenha tantos gaps de funcionalidade/usabilidade. Talvez o usuário no público-alvo do Thunderbird tenha expectativas diferentes da interface. I don't know.

On the bright side, o Thunderbird tem um search global por todas as pastas, que o Claws não tem. Além disso, a sincronização do IMAP para uso offline funciona direito. Finalmente, o Enigmail (add-on do Thunderbird para uso de PGP) parece ser fácil de usar. Acho que isso resume o que eu tenho de bom para dizer sobre o Thunderbird.

Ah, right, o problema das notificações: por algum motivo, o Thunderbird também não está mostrando notificações quando chegam mensagens novas (o que me dá alguma esperança de que o problema seja elsewhere e eu consiga corrigi-lo e voltar a usar o Claws), mas pelo menos eu achei um addon que permite adicionar um ícone à systray com o número de mensagens não-lidas em pastas selecionadas, o que resolve parcialmente meu problema. (O Claws também tem um plugin para adicionar um ícone à systray, mas ao invés de mostrar um número ele mostra um ícone não-customizável e horrível de perceber se está indicando a presença de mensagens não-lidas ou não.) A outra "vantagem" do Thunderbird é que eu achei ele tão ruim de usar que eu provavelmente vou continuar procurando outra solução ao invés de me acomodar com os problemas como eu fiz com o Claws por um ano e tanto.

Gnus

And then there is Gnus. Como dito anteriormente, o Gnus foi o que me levou a mexer no Emacs de novo, mas eu acabei largando ele de mão, em parte porque eu achei que tinha resolvido meu problema com o Claws, em parte porque configurar o Gnus é um tanto quanto não-trivial (e aprender a usá-lo também), especialmente para mim que não tinha praticamente nenhuma experiência com o Emacs então. Agora que eu já estou há mais de dois meses usando o Emacs (estou-vos devendo um post sobre isso, por sinal) e já estou relativamente habituado à criatura, acho que está na hora de dar uma segunda chance ao Gnus.

O Gnus é bastante bizarro, na verdade. Ele faz uma porção de coisas que fazem sentido em um news-reader, mas not so much em um mail-reader. Por exemplo, por padrão ele oculta "grupos" (pastas) que não têm mensagens não-lidas, e oculta as mensagens lidas em um grupo. Eu passei o último sábado quebrando a cabeça tentando configurar o dito cujo, e turns out que o problema que eu passei mais tempo tentando resolver era um bug no Gnus (que aparentemente eu vou ter que reportar uma hora, pois não achei nenhuma menção dele na Internet). Fazer ele funcionar com duas contas de e-mail, e escolher a conta apropriada para mandar mensagens dependendo do contexto, salvar a mensagem enviada na pasta apropriada, etc., é algo que eu tenho que olhar com calma como se faz. Pelo menos o conceito do Gnus de uma mensagem vista aparentemente é independente de o que o IMAP acha que foi visto, o que já é um bom começo. Apesar de ser complicado e cheio de bizarrices, eu tenho a sensação de que o Gnus é suficientemente hackable/customizável para eu conseguir fazê-lo fazer o que eu quero (de certa forma isso é meio verdade sobre o Emacs como um todo). Se eu for bem-sucedido, provavelmente vou postar um tutorial de configuração por aqui.

EOF

Well, esse post ficou bem maior do que eu esperava. Mas agora que eu já escrevi, vou publicar e eras isso. Já que você chegou até aqui, leve umas músicas como recompensa.

3 comentários / comments

Orthographica II: ortografia etimológica, o latim e o proto-indo-europeu

2014-08-23 00:05 -0300. Tags: lang, rant, em-portugues

No último post eu falei um pouco sobre a questão da reforma ortográfica do português e argumentei contra a idéia de que a reforma seria um "emburrecimento da língua". Talvez você queira lê-lo antes de continuar. Neste post, apresento mais alguns pensamentos que tive sobre o assunto.

A idéia de simplificar a ortografia suscita uma tremenda oposição, como os comentários na notícia linkada no último post servem para exemplificar. Num nível mais superficial, há a oposição à mudança puramente por ser uma mudança. Não há muito o que dizer aqui além do que eu já disse no último post; orthographia não se escreve mais assim desde 1943 e ninguém está sentindo falta desses hs. Da mesma forma, acredito que ninguém ache ruim escrever bife e futebol ao inves de beef e football, e no entanto assim foi há nem tantas décadas assim.

Um argumento mais elaborado é de que a grafia das palavras deve ser mantida como está para refletir a etimologia das palavras. Por exemplo, exibir se escreve com x porque provém da palavra latina exhibere, que se escreve com x (em latim, esse x tem de fato som de ks). Manter essa ortografia é bom porque evidencia o prefixo ex-, que é comum a tantas outras palavras e tem um significado mais ou menos bem-definido. Similarmente, escrever biologia ao invés de biolojia é bom porque evidencia a relação dessa palavra com a palavra biólogo. Se escrevêssemos tudo como falamos, essas relações entre as palavras, que são visíveis em suas formas gregas e latinas originais, seriam obscurecidas.

Acontece, entretanto, que quando os romanos começaram a escrever o latim, eles o escreveram exatamente como o falavam: em latim clássico, c sempre tem som de [k] (Cicero se lê "quíquero"), g tem sempre som de [g] (magis se lê "máguis"), x sempre tem som de [ks], as consoantes duplas (como em anno) realmente eram pronunciadas mais longas, e assim por diante. Em geral, quando uma língua recém adota um sistema de escrita [alfabético], ele tende a refletir bastante fielmente a fala; é só com o tempo que, à medida em que a língua vai mudando, a escrita resiste a acompanhar essas mudanças, e perde-se a relação direta entre a grafia e a pronúncia.

Ok, mas em latim não tem problema escrever como se fala, porque latim é latim e, sendo uma língua mais "pura" e menos "corroída" pela ação do tempo, essas relações que para nós só são visíveis na ortografia eram parte da língua latina viva, e portanto sua escrita baseada na fala as conserva, right?

Well, não é bem assim. O latim não brotou do nada. Assim como o português, o espanhol, o italiano, o francês, etc. derivam de uma língua anterior (o latim), e assim formam a família das línguas latinas ou românicas, também o latim, o grego, o sânscrito, o germânico, o eslávico, etc. derivam de uma língua comum, e formam assim a família das línguas indo-européias. O problema é que essa língua é tão antiga que nunca chegou a ser escrita; porém, é possível observar as similaridades e diferenças das línguas descendentes que ela deixou e reconstruir com alguma segurança como era essa língua original. A essa língua reconstruída denomina-se proto-indo-europeu.

E assim como o português é uma versão "corroída" de latim, o latim é uma versão "corroída" de proto-indo-europeu, e algumas relações entre palavras que eram visíveis em proto-indo-europeu ou em proto-latim não o são em latim. Voltando ao nosso exemplo clássico, exibir (pronunciado "ezibir") em latim é exhibere (pronunciado "eks-híbere", sem nenhuma letra muda nem irregularidade), mostrando claramente na pronúncia e na escrita o prefixo ex-. Mas e o que fazer do -hibere? Pois acontece que hibere nada mais é do que uma versão "corrompida" do verbo habere, que dá origem ao nosso haver. Essa mudança habere → hibere não é de uma natureza muito diferente da pronúncia de comi como "cumi" ou escola como "iscola" em português brasileiro moderno. E no entanto, essa "corrupção" que ocorreu entre o proto-latim e o latim foi tomada como forma oficial em latim; em latim clássico, se escreve como se fala.

Outro exemplo: as sequências de sons /ts/ e /ds/ do proto-indo-europeu se perderam em latim. Assim, "potentes" (no plural) em latim é potentes, mas no singular é potens, pois o t da forma original (*potents) se perdeu. Da mesma forma, a conjugação do verbo "ser" (esse) em latim é sum, es, est (de onde vem o nosso sou, és, é), mas a conjugação do verbo "poder", que em latim é derivado da expressão potis esse ("ser capaz") é possum, potes, potest, pois o t do *potsum original se perdeu, e essa perda é refletida na escrita.

E quanto ao próprio verbo "ser"? Sum, es, est, sumus, estis, sunt não é exatamente um primor de regularidade. Ok, o verbo "ser" é irregular em tudo que é língua, especialmente nas línguas indo-européias... interessante essa última observação, não? Acontece que a forma reconstruída desse verbo em proto-indo-europeu é h₁ésmi, h₁ési, h₁ésti, h₁sm̥ós, h₁sté, h₁sénti, que tem uma cara bem mais regular; até dá para distinguir um radical: h₁es-. (Em proto-indo-europeu, é comum os radicais terem a forma consoante-vogal-consoante, onde a vogal alterna entre e, o e ausência de vogal ao longo da conjugação ou declinação.) Agora o que é esse h₁? Trata-se de uma das chamadas consoantes laringeais do PIE. As laringeais não são nada mais, nada menos do que sons similares a h, que se perderam em todas as línguas descendentes exceto na família do hitita, deixando apenas vestígios de sua anterior existência (normalmente na forma de vogais ou efeitos sobre vogais adjacentes) na maioria das línguas indo-européias. That's right, os hs do PIE já tinham ficado mudos 3 mil anos atrás e os romanos nem chegaram a tomar conhecimento de sua existência, e portanto eles não são refletidos na escrita do latim. (Os hs do latim provêm de outros fonemas do PIE, tais como /gʰ/, que se "corromperam" e transformaram-se em h com o tempo.) Se os falantes de proto-indo-europeu estivessem vivos e dotados de escrita nessa época, eles teriam tido a mesma reação diante da escrita do latim que nós temos diante de propostas de reforma ortográfica em português.


I disapprove of your orthographic developments!

O ponto onde eu quero chegar é: defende-se a manutenção da ortografia atual para conservar as relações etimológicas com o latim, língua-origem do português, mas o próprio latim não preserva em sua ortografia diversas relações etimológicas com a sua língua-origem. Se os falantes de latim clássico podiam escrever como falavam, por que nós não podemos?

Disclaimer

Não, embora possa parecer, eu não estou promulgando a reforma; no momento não expresso nem apoio nem oposição. Meu intento foi unicamente demonstrar uma fraqueza do argumento pró grafia etimológica. Certamente há outros argumentos possíveis em favor da grafia etimológica (por exemplo, que manter uma proximidade com a ortografia do latim facilita nossa vida para aprender outras línguas latinas e o inglês), bem como contra (escrevemos amigo e amizade (do latim amicus e amicitate-) e nem por isso deixamos de perceber a relação entre essas palavras). Decida por si mesmo. Ou não.

7 comentários / comments

Orthographica

2014-08-20 01:31 -0300. Tags: lang, rant, ramble, em-portugues

A notícia de que estão querendo propor outra reforma ortográfica me trouxe à mente umas cousas de que eu gostaria de falar.

Da inconsistência da ortografia vigente

Certa feita, estávamos eu e familiares percorrendo as verdejantes terras de Viamãoheimr, quando um dos indivíduos exclamou algo do tipo: "Olha lá, a placa escrito 'ezibida' com 'z'. E aposto que a pessoa não escreveu assim pra ser diferente, não sabia mesmo." O primeiro pensamento que me veio à mente foi:

  1. Será que, ao invés de zoar da ignorância do povo, nós não devêssemos ficar é preocupados com o estado da educação neste país?

Mas logo depois me ocorreu outro:

  1. Não é fantástico que hoje em dia praticamente todo o mundo saiba ler e escrever, ainda que imperfeitamente, coisa que não era verdade dois séculos atrás nem durante a maior parte da história da escrita?

E, finalmente:

  1. "Exibida" ainda se escreve com "x", really?

É quanto a este último que eu pretendo falar.

A nossa ortografia mantém um bocado de irregularidades (no sentido de que nem toda letra ou seqüência de letras corresponde a um único som e vice-versa), em nome de conservar a etimologia das palavras. Por exemplo, exibir se escreve com x para manter a ortografia similar à palavra latina exhibere que lhe dá origem. Seria um argumento válido para a manutenção da ortografia vigente, se não fosse pelo fato de que esse princípio é seguido de maneira bastante inconsistente.

Por exemplo, diversas palavras que contêm os prefixos/preposições latinos ex- e extra- são escritas com x em português, tais como extrair, extraditar, etc. Mas estranho (do latim extraneus) se escreve com s, sem nenhum bom motivo (que me conste). Em um caso extremo, extensão se escreve com x, mas estender se escreve com s.

O h etimológico é mantido em início de palavra, mas não em outras posições: habitar se escreve com h, mas desabitar não. Que princípio justifica a manutenção em um caso e não no outro?

C tem som de [k] ou de [s] dependendo do contexto, o que ajuda a manter a ortografia de palavras como pouco e paucidade consistente, mesmo já fazendo uns mil e tantos anos que esses dois cs não têm mais o mesmo som. Por outro lado, o passado de fico é fiquei, uma mudança de c para qu que não ocorre por nenhum motivo etimológico, mas tão somente para contornar a irregularidade da pronúncia da letra c em português.

"Estão emburrecendo o português"

Na notícia que me linkaram sobre o assunto, pode-se encontrar um bocado de comentários dizendo que a proposta "emburrece o português", "é a legalização da burrice", "emburrecer a gramática", "E são regras, pelo amor de Deus! Isso não pode ser aprovado, são várias gerações que aprenderam a escrever seguindo uma regra", etc., etc. Eu não perdi muito tempo na referida seção de comentários porque a minha tolerância a essas coisas hoje em dia é muito pequena, mas deu para dar uma idéia. (Também havia uma boa dose de comentários pró-reforma no texto. Curiosamente, dei uma olhada na versão de papel da Zero Hora de hoje ontem, na seção de mensagens dos leitores, e haviam publicado uns quatro comentários recebidos via Facebook sobre o assunto, nenhum pró-reforma. Chamamos isso de imparcialidade.)

Esses comentários me corroem o fígado por duas razões. Em primeiro lugar: reformar a ortografia "emburrece a língua" (tanto quanto isso faz sentido)? Então o que fazer das reformas que eliminaram o ph, o h de exhibido, o y de pyrâmide, o ch de chaos, a consoante dupla de innovar? Elas "emburreceram a língua"? Não são tantas gerações assim que aprenderam a escrever em qualquer dada ortografia no Brasil; a última reforma foi aprovada em 2009, a anterior em 1971 e a anterior a essa em 1943.

Em segundo lugar: a ortografia não é a língua; estritamente, a ortografia não é sequer parte da gramática. Mesmo que o português deixasse de ser escrito, ou que fosse escrito em alfabeto cirílico, continuaria sendo a mesma língua, com as mesmas regras gramaticais. Evidentemente, as línguas costumam ter um sistema de escrita oficial associado a si, mas a língua é independente do sistema de escrita usado para escrevê-la (há inclusive línguas que possuem múltiplos sistemas de escrita oficiais).

Então tu defende a reforma?

Não. Muito embora eu discorde da noção de que a reforma "emburrece a língua", nem por isso eu aprovo a tal reforma. Na verdade eu sequer aprovo ou (tanto quanto me é possível) adoto a última reforma aprovada (como alguns leitores hão de ter notado pelo meu uso da grafia "idéia"). Na verdade eu tenho cá para mim sérias ressalvas quanto à existência de um órgão regulador da língua e da regulação da norma ortográfica por lei; o inglês vive sem um órgão regulador e ninguém sofreu danos deletérios por conta disso. (Ok, talvez a ortografia do inglês possa ser considerada um dano deletério.) Especialmente quando reformas são promulgadas sem qualquer consulta à população (isso é um problema geral da democracia representativa, but I digress).

Voltando especificamente à última reforma proposta: este pessoal do R7 resolveu falar com El Hombre Marcos Bagno, que levanta alguns pontos interessantes com os quais qualquer reforma que pretenda aproximar a escrita da fala tem que lidar. Por exemplo, em mestre o s tem som de "s", mas em mesmo tem som de "z"; deveremos escrever mezmo? Em alguns lugares, o s de mestre tem som de "s", em outros tem som de "x"; deveremos aceitar tanto mestre quanto mextre como grafias válidas?

De qualquer forma, a proposta tal como está sendo apresentada parece não ter sido muito bem pensada. Em particular, pelo menos na reforma tal como os jornais a estão apresentando, tanto c quanto q seriam mantidos com som de [k], e o som inicial de "rato" continuaria sendo escrito com "r" no início de palavra e "rr" no meio. Se é para fazer uma reforma radical como a que se está propondo, então que pelo menos adotem uma ortografia realmente lógica, e que as exceções, se houverem, sejam justificadas por algum guiding principle.

Alguns dos proponentes da reforma dizem que "A simplificação ortográfica é a porta para a eliminação do analfabetismo". Será? O Japão usa um sistema de escrita absurdamente complicado, que consiste de dois silabários e um conjunto de mais ou menos dois mil ideogramas, cuja leitura pode ser drasticamente diferente dependendo do contexto (今 se lê "ima" e 日 se lê "hi", mas 今日 se lê "kyō"), e no entanto o Japão tem uma taxa de alfabetização de mais de oito mil 99% (fonte). Claro que, pode-se argumentar, quanto mais complexo o sistema de escrita, mais tempo se perde apredendo-o que poderia ser usado de maneira mais útil para outras coisas, e mais inacessível ele se torna a quem não tem esse tempo para dedicar a aprendê-lo (e.g., quem não termina o ensino fundamental). Por outro lado, não me é claro se haveria um ganho significativo em termos de alfabetização passando da ortografia atual do português (que já é bastante próxima da fala) para uma ortografia mais regular.

Então tu defende o quê?

Eu não defendo nada. Isso aqui é para terminar que nem um daqueles episódios do South Park que começam com uma questão polêmica e no final se fica com a impressão de que ambos os lados da discussão estão errados.

(Ok, eu pessoalmente defendo o status quo. Aliás, o status quo ante, de volta à ortografia anterior à última reforma. Mas neste post aqui eu não tento defender nada.)

Caveat commentator

Eu tenho uma experiência prévia ruim com posts que escapam para o mundo selvagem e atraem comentários menos-que-positivos. Na verdade a negatividade da experiência em questão foi amplificada pelo fato de eu ter sido pego absolutamente de surpresa na ocasião; hoje em dia eu já posto psicologicamente preparado para estar errado na Internet. De qualquer forma, dado o teor dos comentários na notícia da Zero Hora linkada, parece-me boa precaução deixar um recado a quem pretender comentar o post: caso pretenda deixar sua opinião sobre a reforma, faça-o apenas se for apresentar argumentos para defender sua posição. Comentários do tipo "eu aprovo", "eu desaprovo", "oh, o emburrecimento da língua", "meu, tu mal consegue escrever um parágrafo em português sem tacar um termo em inglês no meio ou conjugar a segunda pessoa errado, quem é tu pra falar de português?", etc., serão sumariamente eliminados, e os autores serão sumariamente insultados (vou mostrar a língua para eles).

Update: Assista ao próximo capítulo de Orthographica: uma batalha entre fonemas e grafemas.

11 comentários / comments

LotR #2

2013-05-20 03:05 -0300. Tags: book, rant, em-portugues

Tolkien é um ótimo escritor. As histórias que ele conta é que não compartilham da mesma glória.

Acabei de ler o primeiro capítulo do segundo volume do Senhor dos Anéis. Ok, eu sobrevivi ao primeiro volume (que eu comecei a ler sem nenhuma pressa algum tempo atrás). Sobrevivi ao Tom Bombadil e ao banho anual da Goldberry. Sobrevivi às canções élficas que derrubam até os hobbits de sono (sim, isso é parte da história). Sobrevivi até a Lothlórien e o um mês que a galera ficou jogando fora lá comendo, dormindo e vendo televisão (a.k.a. Mirror of Galadriel) enquanto Sauron et al. tocavam o terror no mundo. Mas este capítulo foi demás.

Veja bem. Boromir, o inútil, depois de tentar roubar o anel do Frodo e atrapalhar a missão, não tendo feito nada de muito útil até esse ponto da história exceto ter ajudado a escavar a neve do monte Caradhras, que no fim das contas eles não atravessaram, perde a briga para um bando de orcs. Aragorn encontra Boromir semi-morto, depois de os orcs terem ido embora. "Eu ferrei com tudo. Agora os orcs levaram os hobbits embora. Vou morrer minha morte miserável aqui", diz Boromir, e morre sua morte miserável ali. Nesse momento a galera sai correndo na pista dos orcs para salvar os hobbits, certo?

Errado. Primeiro, Aragorn, Legolas e Gimli discutem como eles vão fazer o funeral do Boromir. Sim, é mais importante satisfazer o morto do que ir salvar os hobbits (afinal ninguém se importa muito com os hobbits nessa história). Aí eles decidem que cavar dá muito trabalho, então melhor é colocar o Boromir dentro do barquinho dele, junto com seus pertences e os dos orcs que ele conseguiu matar, e levar o barquinho até um ponto do rio e deixar o rio levar embora, não sem primeiro fazer dez mil preparativos. Claro que depois de largarem o barquinho, Aragorn e Legolas ainda cantam umas musiquinhas em homenagem ao morto. A essas alturas o Tolkien já está de saco cheio de escrever poemas (o livro é cheio de poemas), então as musiquinhas têm uma métrica totalmente capenga em comparação com as do primeiro livro. Really, a cena parece as firulas de cavalaria do Dom Quixote, com a diferença de que o Dom Quixote é intencionalmente zoação.

Até que, no final da tarde (essa história toda começou de manhã), Aragorn conclui: "Já sei! Quem sabe a gente vai atrás dos orcs, for a change?" "‘Well, after them!’ said Gimli. ‘Dwarves too can go swiftly, and they do not tire sooner than Orcs. But it will be a long chase: they have a long start.’" A long start? Imagine! Só umas seis horas de vantagem enquanto a gente colocava um morto num barco. O que são meras seis horas comparadas com os trinta dias que a gente passou comendo e dormindo em Lothlórien?

A essas alturas, a motivação primária que eu tenho para continuar lendo esse livro é o prospecto de ler The Last Ringbearer depois. E no fim das contas eu estou reclamando à toa; essa história é uma boa fonte de diversão.

6 comentários / comments

fgetcsv: A cautionary tale

2013-05-18 01:52 -0300. Tags: comp, prog, web, php, rant, em-portugues

Quando eu reescrevi o blog system, eu resolvi manter o índice de posts em um arquivo CSV e usar as funções fgetcsv e fputcsv para manipulá-lo. Minha intuição prontamente me disse "isso é PHP, tu vai te ferrar", mas eu não lhe dei importância. Afinal, provavelmente seria mais eficiente usar essas funções do que ler uma linha inteira e usar a explode para separar os campos. (Sim, eu usei um txt ao invés de um SQLite. Eu sou feliz assim, ok?)

Na verdade o que eu queria era manter o arquivo como tab-separated values, de maneira que ele fosse o mais fácil possível de manipular com as ferramentas convencionais do Unix (cut, awk, etc.). As funções do PHP aceitam um delimitador como argumento, então pensei eu que bastaria mudar o delimitador para "\t" ao invés da vírgula e tudo estaria bem. Evidentemente eu estava errado: um dos argumentos da fputcsv é um "enclosure", um caractere que deve ser usado ao redor de valores que contêm espaços (ou outras situações? who knows?). O valor padrão para a enclosure é a aspa dupla. Acontece que a fputcsv exige uma enclosure: não é possível passar uma string vazia, ou NULL, por exemplo, para evitar que a função imprima uma enclosure em volta das strings que considerar dignas de serem envoltas. Lá se vai meu tab-separated file bonitinho. Mas ok, não é um problema fatal.

A segunda curiosidade é que a fgetcsv aceita um argumento "escape", que diz qual é o caractere de escape (\ por default). Evidentemente, você tem que usar um caractere de escape; a possibilidade de ler um arquivo em um formato em que todos os caracteres exceto o delimitador de campo e o "\n" tenham seus valores literais é inconcebível. Mas ok, podemos setar o escape para um caractere não-imprimível do ASCII (e.g., "\1") e esquecer da existência dele. Acontece que a fputcsv não aceita um caractere de escape, logo você não tem como usar o mesmo caractere não-imprimível nas duas funções. WTF?

Na verdade, agora testando melhor (já que a documentação não nos conta muita coisa), aparentemente a fputcsv nunca produz um caractere de escape: se o delimitador aparece em um dos campos, ele é duplicado na saída (i.e., a"b vira a""b). Evidentemente, não há como eliminar esse comportamento. Mas então o que será que faz o escape character da fgetcsv?

# php -r 'while ($a = fgetcsv(STDIN, 999, ",", "\"", "\\"))
             { var_export($a); echo "\n"; }'
a,z
array (
  0 => 'a',
  1 => 'z',
)
a\tb,z
array (
  0 => 'a\\tb',
  1 => 'z',
)

Ok, o escape não serve para introduzir seqüências do tipo \t. Talvez para remover o significado especial de outros caracteres?

a\,b,z
array (
  0 => 'a\\',
  1 => 'b',
  2 => 'z',
)
a b,z
array (
  0 => 'a b',
  1 => 'z',
)
a\ b,z
array (
  0 => 'a\\ b',
  1 => 'z',
)

Muito bem. Como vimos, o escape character serve para, hã... hmm.

Mas o fatal blow eu tive hoje, olhando a lista de todos os posts do blog e constatando que o post sobre o filme π estava aparecendo com o nome vazio. Eis que:

# php -r '
    $h = fopen("entryidx.entries", "r");
    while ($a = fgetcsv($h, 9999, "\t"))
       if ($a[0]=="20120322-pi") var_export($a);'
array (
  0 => '20120322-pi',
  1 => 'π',
  2 => '2012-03-22 23:53 -0300',
  3 => 'film',
)

# LC_ALL=C php -r '
    $h = fopen("entryidx.entries", "r");
    while ($a = fgetcsv($h, 9999, "\t"))
       if ($a[0]=="20120322-pi") var_export($a);'
array (
  0 => '20120322-pi',
  1 => '',
  2 => '2012-03-22 23:53 -0300',
  3 => 'film',
)

A próxima versão do Blognir deverá usar fgets/explode.

UPDATE: Aparentemente o problema só ocorre quando um caractere não-ASCII aparece no começo de um campo. Whut?

UPDATE 2:

"a b","c d"
array (
  0 => 'a b',
  1 => 'c d',
)
"a"b","c"d"
array (
  0 => 'ab"',
  1 => 'cd"',
)
"a\"b","c d"
array (
  0 => 'a\\"b',
  1 => 'c d',
)

5 comentários / comments

Blergh

2013-03-29 02:03 -0300. Tags: about, life, mind, rant, em-portugues

[Este post foi engolido por um tigre. (31/03/2013)]

13 comentários / comments

inf.ufrgs.br, PHP, e outros infortúnios

2013-03-10 22:26 -0300. Tags: comp, life, php, rant, em-portugues

Com este post tenho o objetivo único e exclusivo de reclamar da vida. É improvável que haja qualquer informação proveitosa contida aqui. You have been warned.

Quando eu entrei para a Inf, os Sioux possuíam o mundo as páginas pessoais dos alunos eram contidas no diretório home de seus usuários (especificamente, em ~/public_html). O camarada podia acessar a portal.inf.ufrgs.br por SSH e editar as páginas remotamente com o Vim. Outra vantagem é que se podia rodar scripts remotamente para gerar páginas ou fazer algum procedimento de manutenção. Bem, mais ou menos. O home da portal é montado sem permissão de execução, o que significa que só dá para rodar os binários que já estão lá (e.g., bash, perl), e para poder rodar os scripts que eu tinha no ~/bin sem ter que digitar bash nome-do-script toda vez eu tinha que fazer uma gambiarra no ~/.bashrc para criar uma função com o mesmo nome do script para cada script presente no ~/bin. Mas ok, dava pra tolerar.

Em algum momento da história do mundo, as páginas pessoais foram levadas para um servidor separado, "por motivos de segurança". Conseqüentemente, não dava mais para alterar as páginas via portal. Montar o diretório das páginas por sshfs? Claro que não, a portal não tem sshfs e não executa binários do home. Solicitar à administração da rede para instalarem o sshfs na portal? Não faremos isso, por motivos de segurança. Well, dá pra montar o sshfs usando a máquina de casa, e se divertir com a latência do 3G. Heh.

Um dia me contaram que dava para acessar a portal, e de lá se logar nas máquinas dos laboratórios da graduação, de onde se pode rodar sshfs e tudo o mais que se queira. Hmm, boa! O camarada tem que se logar três vezes (uma na portal, uma nos labs, e uma no sshfs), mas ainda vale a pena. Se o 3G cai meu Vim fica parado lá me esperando e eu não corro o risco de ter salvo meio arquivo no home.

No semestre passado, atualizaram o OS das máquinas dos labs, e agora não é mais possível fazer o login remoto. Yay! Bom, sobrou o sshfs de casa. Felizmente, agora o meu 3G é um pouquinho melhor, com uma latência normalmente abaixo do 1 segundo (vs. os 4 da Tim), e a coisa é pelo menos utilizável.

O servidor das páginas pessoais também monta os homes sem permissão de execução, o que significa que só dá para usar as linguagens de script que já estão lá, i.e., PHP. PHP é uma linguagem/implementação notavelmente medonha, mas ok, é o que temos. Eu quero um blog, e eu quero agora, e eu quero escrever meus posts no Vim e salvar um txt somewhere para postá-los e ser feliz. Let's do it.

O PHP tem uma (so-called) "feature" chamada "magic quotes". A idéia é: programadores são incompetentes e esquecem de escapar as aspas antes de montar uma query de SQL, então vamos escapar automaticamente qualquer entrada que venha de um formulário. (A noção de montar queries de SQL na mão e escapar strings na mão é uma coisa que strikes me as fundamentally wrong, but I digress.) Acontece que o PHP só faz isso se ele estiver configurado para fazer isso no php.ini. E como (pelo que eu entendo) ele faz o escaping antes de rodar o script, o script não tem a oportunidade de desativar as magic quotes. Conclusão? Eu tenho uma função no blog cujo único propósito é receber uma string e remover os escapes se as magic quotes estiverem ativas, ou retornar a string intacta caso contrário. Lindo, hein?

E as closures. Em JavaScript (é, eu vou usar JavaScript, outra linguagem linda, como exemplo, mas acho que assim será mais compreensível do que o Common Lisp que eu ia usar) podemos fazer coisas do tipo:

function menores_que(n, lista) {
    return lista.filter(function(x) { return x<n; });
}

menores_que(5, [2,3,5,7,11]);   // Retorna [2,3].

Aqui, passamos uma função anônima para o método filter, que seleciona os elementos da lista que satisfazem o predicado passado como argumento. (Esse método só existe no JavaScript da Mozilla, eu acho.) Note que a subfunção usa o n definido fora dela. Em PHP 5.3 foi introduzida uma feature similar. Porém, na versão PHP, você tem que declarar explicitamente quais variáveis externas serão capturadas pela closure. WTF? Isso é tão ridículo que não sei quem mais poderia ter a mesma idéia. Oh, well. (Pelo menos em C++11 há a opção de dizer "captura tudo e não enche".)

Ok, ok. Eu sobrevivi. 753 linhas de PHP. Até que ficou pequeninho para a quantidade de coisas que o blog faz. O problema é que o código ficou todo gambiarrento, e sempre que eu vou adicionar uma feature é um trabalho medonho, e sempre que eu penso em reescrever do zero (coisa que uma hora ou outra eu vou acabar fazendo, já que eu não consigo ficar cinco minutos editando o código atual sem ranger os dentes) eu lembro que terei que fazer isso em PHP (o que produzirá uma quantidade equiparável de ranger de dentes) e abandono a idéia. Mas terá que ser feito, porque PHP é a única coisa que roda na Inf (e muitos outros lugares). Very well. (Actually, not.)

A última coisa que eu quis adicionar no blog foi uma feature de "Comentários recentes" na barra lateral. A idéia original era simplíssima: quando um leitor posta um comentário, além de criar o arquivinho de comentário no diretório apropriado, criamos também um symlink para o arquivinho em um diretório recent_comments da vida. Se o número de links exceder uma quantidade x, removem-se os links mais antigos. Simples, hã? Só que se eu tento usar a função symlink(), ela simplesmente retorna FALSE e nada acontece. What gives?

Acontece que o tal do Suhosin bloqueia a função symlink() do PHP se a opção base_opendir do PHP estiver ativada. Essa opção serve para limitar de que diretórios os scripts podem abrir arquivos. A idéia é que se o usuário puder usar a symlink(), ele poderia em tese despistar a base_opendir criando um link no diretório local para um lugar de onde o script não teria acesso (e.g., /etc/passwd). Porque o PHP não poderia primeiro resolver o symlink e depois testar se o arquivo está dentro dos diretórios permitidos antes de abri-lo, right? Nem a symlink() poderia verificar se o alvo do link está dentro dos diretórios permitidos e criar ou não o link dependendo do caso, right?

Acontece que aparentemete o PHP verifica o caminho resolvido antes de abrir o arquivo. You see, embora o PHP não possa criar symlinks, eu mesmo posso, por sshfs. Quer dizer que qualquer vulnerabilidade que links arbitrários pudessem causar ainda poderiam ser provocadas por mim. Mas eu testei criar um symlink local para o /etc/passwd, e o PHP se recusa a abri-lo, alegando violação da base_opendir. Quer dizer que mesmo que a symlink() estivesse ativa ainda não haveria vulnerabilidade. Mas ela não está, e isso impede gratuitamente minha implementação simples da feature de comentários recentes. Eu poderia entrar em contato com a administração da rede da Inf e pedir para eles habilitarem a feature. Eu não farei isso por duas razões: (1) a admrede vai me dizer que não vai habilitar nada "por motivos de segurança"; (2) um dos princípios por trás deste blog system é rodar em qualquer lugar que tenha um servidor com PHP, sem depender nem de bibliotecas non-standard nem da boa-vontade de sysadmins, e como o symlink() desativado é o padrão do Suhosin, não posso contar com a utilizabilidade dessa função. Tudo bem, o Unix original não tinha symlinks. Vamos brincar de anos setenta.

8 comentários / comments

Qué voy a ser, je ne sais plus

2013-01-11 04:03 -0200. Tags: life, rant, academia, em-portugues

(For your enlightening. Midnight rant follows.)

Tendo terminado hoje a "prova" de Tópicos X (embora ainda não a tenha enviado), termina para mim para todos os fins práticos o semestre de 2012/2. Tópicos Especiais em Computação X, a.k.a. Grandes Desafios da Computação, é uma cadeira de 2 créditos que visa apresentar diversas áreas de pesquisa em Ciência da Computação. A maior parte das aulas consiste de uma palestra dada por um professor sobre sua área de pesquisa, no que ela consiste e quais são os desafios atuais que ela tem para resolver, além de dar uma idéia de como funciona a pós-graduação. Em uma dessas aulas, um professor mencionou (com uma certa ênfase) que a pós-graduação "não é mais do mesmo", que a dinâmica das aulas é diferente, que os alunos têm um papel muito mais ativo do que na graduação.

A primeira reação que eu tive diante disso foi uma boa dose de descrença. Talvez alguns professores dêem boas aulas, e os alunos desempenhe um papel mais ativo e autônomo, mas desconfio seriamente que com muitos professores a coisa não seja bem assim.

By the way, quem falou em autônomo? Isso aí brotou da minha cabecinha não-categorial. Só porque os alunos são mais ativos, não quer dizer que eles tenham algum poder de decisão sobre o que aprendem. Disse ainda o camarada palestrante que como não há cadeiras obrigatórias no mestrado, o aluno pode escolher fazer cadeiras apenas nas áreas que lhe interessam. Porém, no caso das cadeiras eletivas da graduação, os fatos de (1) haver um número mínimo de créditos eletivos obrigatórios, (2) não haver cadeiras suficientes em determinados tópicos, e (3) diversas cadeiras existirem apenas "para inglês ver", não sendo oferecidas em nenhum semestre na prática, significam que no geral é inviável fazer todas as eletivas em assuntos de interesse, e não vejo por que isso seria diferente no mestrado.

O segundo pensamento que me ocorreu é, se o mestrado é toda essa maravilha, por que diabos a graduação não é? É para filtrar dos reles mortais os poucos que têm saco de aturar a graduação inteira e ainda têm ânimo de continuar na academia? É por falta de professores (assumindo que uma melhoria exigisse turmas menores, como são no mestrado, proposição da qual eu duvido)? Porque "sempre foi assim"? Porque no final das contas o objetivo da graduação é produzir um diploma que dá ao camarada o direito de ser empregado em lugares que pagam melhor, e não adquirir e construir conhecimento? Todas as alternativas acima?

É nessas horas que eu me pergunto se vale a pena seguir a vida acadêmica. Meu objetivo inicial, logo que eu entrei nesse curso, era me tornar professor e viver uma vida feliz e contente dando aula e fazendo pesquisa. O curso começou a ficar menos-que-excelente pelo terceiro semestre, e lá pelo sexto ou sétimo eu já estava de saco cheio o suficiente para ter vontade de largar a faculdade. Muita gente que eu conheço encheu o saco bem antes; o que me sustentou nesse tempo foi primariamente o fato de que eu me interesso realmente muito por Ciência da Computação, e leio sobre coisas que me interessam fora do curso, o que mantém meu "amor à arte" firme e forte, apesar do curso. Felizmente a parte mais maçante do curso já se foi, e agora só me restam 6 créditos eletivos por fazer (ou 8, se o professor de Tópicos X não gostar das minhas respostas, já que a prova não é lá muito objetiva*) e o TCC (que é trabalhoso, mas pelo menos eu arranjei um assunto que me interessa para fazer ele sobre (e que venha o monstro da gramática me devorar)).

Como se não bastassem os horrores da graduação para desmotivar o sujeito, eu vejo por aí coisas do tipo:

Basically everyone who gets tenure, including me, finds him/herself continuing to get busier every year. Not only do the demands on our time increase, but we can no longer fall back on the convenient selfish excuse “sorry—can’t possibly do that time consuming task while I’m on the tenure track.” The constantly overloaded TODO list gets old, and it also makes it hard to get actual research done. It’s no coincidence that a lot of tenured professors rely on students to do all of the technical work associated with their research programs. Untenured professors not only have more time to do research but also may be reluctant to risk their careers by putting students on too many critical paths.

Life After Tenure (ênfase minha)

Mais de uma vez eu já li ou ouvi relatos de que os encargos administrativos de um professor tomam tempo suficiente para atapalhar a "pesquisa de verdade". E aí reside um baita de um problema, porque se eu tiver que escolher entre ser professor e desenvolver software, eu prefiro desenvolver software. Na minha concepção inicial do universo, eu não teria que escolher, mas parece que minhas suposições podem estar erradas. Je ne sais plus.

* Appendix A: De como eu saí da Letras

(Não, isso não tem nada a ver com o resto do post.)

Para quem não sabe, antes de entrar para a Ciência da Computação (em 2009) eu cursei um semestre e meio de Letras. Os motivos para isso foram meu interesse por lingüística e a ausência de um curso específico de lingüística em uma faculdade próxima (a faculdade mais próxima com um curso de Lingüística é a Unicamp), o fato de que eu estava de saco cheio de computadores (primariamente porque eu tinha zilhões de problemas com computadores na época, primariamente porque eu tinha uma máquina extremamente capenga, mas também porque viviam me alugando para resolver problemas), e o fato de que eu estava de saco cheio de matemática (graças a um curso de iniciação científica oferecido pelo IMPA para os primeiros dez bilhões de colocados na Olimpíada Brasileira de Matemática das Escolas Públicas, o qual eu abandonei depois de três meses, embora eu estivesse sendo pago para participar). Quaisquer que tenham sido os motivos, eu tomei a decisão de fazer o curso de "Letras – Tradutor Português e Japonês" (porque foi a língua que me pareceu lingüisticamente mais interessante, e não porque eu gosto de animê e mangá, como aparentemente era o caso de todos os outros (cerca de oito) alunos).

Embora algumas cadeiras fossem insuportáveis (e.g., Leitura e Produção Textual), algumas cadeiras eram muito legais (Conceitos Básicos de Lingüística, Japonês I), e embora historicamente eu detestasse de coração o estudo de literatura que se faz no ensino fundamental/médio, até as cadeiras de literatura estavam interessantes.

Até que chegou a primeira prova de Literatura Brasileira A. Eu fiz a bendita prova e tirei um C. Nesse momento eu contemplei a prova, mirei as respostas, e me dei conta de que eu não fazia a menor idéia de por que eu tinha tirado um C, ou o que eu poderia ter respondido diferente para tirar uma nota melhor. Nesse momento eu me dei conta de que se eu não era capaz de determinar o que estava "errado" na prova, meu desempenho no resto do curso provavelmente seria a mesma coisa, já que responder "certo" parecia exigir uma habilidade mágica que não só eu não tinha, como sequer compreendia a natureza da mesma. Nesse momento eu tive um profundo insight: "Que diabos eu estou fazendo aqui?!"

(Fora que o conteúdo de lingüística do curso é um quase nada.)

E foi assim que eu não fiz a segunda prova de Literatura Brasileira A (embora tenha ido às aulas), terminei as outras cadeiras decentemente, no semestre seguinte peguei apenas quatro cadeiras (incluindo uma eletiva, nenhuma relacionada a literatura), larguei todas em setembro para me mudar temporariamente para a casa da minha mãe e estudar para o vestibular, e no ano seguinte entrei para a Ciência da Computação, onde, se muitas das cadeiras são terrivelmente maçantes, pelo menos os critérios de avaliação são largamente objetivos.

Post scriptum

O vídeo linkado diz "que voy a hacer" ao invés de "qué voy a ser". Nunca tinha me ocorrido essa interpretação (acentuação notwithstanding). Mas camarada Google diz:

E no entanto os primeiros sites retornados por uma pesquisa por manu chao me gustas tu lyrics favorecem o "que voy a hacer". E agora?

9 comentários / comments

The shell is completely wrong

2013-01-04 13:56 -0200. Tags: comp, prog, bash, unix, wrong, rant, em-portugues

Já faz algum tempo que eu estou para escrever uma série de posts sobre como os ambientes computacionais modernos estão completamente errados. Este não é um dos posts previstos. Ao contrário da maior parte das idéias que eu pretendia/pretendo expor na série, este post trata de um tópico dentro do âmbito do tranqüilamente implementável, sem envolver mudanças de paradigma. Considere este o zero-ésimo post da série. Considere este também o proto-manifesto de um shell que eu posso vir a escrever ou não durante as férias. Comentários são bem-vindos.

[Foto de John McCarthy com a legenda 'Programming: You're doing it completely wrong']

It's a fucking programming language

Although most users think of the shell as an interactive command interpreter, it is really a programming language in which each statement runs a command. Because it must satisfy both the interactive and programming aspects of command execution, it is a strange language, shaped as much by history as by design.

The Unix Programming Environment

Embora desde os primórdios o shell do Unix tenha sido reconhecido como uma linguagem de programação, as pessoas tendem a não encará-lo como uma "linguagem de verdade"; o shell serve para rodar "scripts", ao invés de "programas". Não só os usuários de shells têm essa visão, mas também os próprios desenvolvedores de shells: embora bash, zsh e companhia tenham acumulado diversas features ao longo dos anos, faltam nos shells features básicas que se espera encontrar em qualquer linguagem de programação razoável. Por exemplo, extensões do bash em relação ao Bourne shell (sh) original incluem:

  • Variáveis array (que só podem ser arrays de string);
  • Arrays associativos, i.e., arrays indexados por strings;
  • Um comando mapfile para ler o conteúdo de um arquivo para dentro de um array (mas não mapeia coisa nenhuma: alterações no array não se refletem no arquivo);
  • Sintaxe para permitir fall-through das cláusulas de um case (i.e., permitir o comportamento de um case sem break em C);

E no entanto:

  • Arrays só podem conter strings; não é possível criar estruturas de dados aninhadas;
  • Arrays não são elementos de primeira classe: não é possível passar um array como argumento para uma função, ou retornar um array; by the way...
  • Não existe um mecanismo decente para retornar valores de uma função; o jeito é usar uma variável global, ou imprimir o valor a retornar para a stdout e ler usando a sintaxe $(função args...), que cria um subprocesso só para executar a função e captura a saída.

Além disso, a sintaxe e a semântica de certas features são bastante facão. Isso em parte se deve ao fato de que o bash tentou acrescentar features novas sem quebrar compatibilidade com a sintaxe do sh, mas também por falta de princípios de design decentes. Um mecanismo para retornar valores de funções já seria um bom começo, e ajudaria a limpar outros aspectos do shell, já que seria possível forncecer mais funcionalidades através de funções, ao invés de usar sintaxe abstrusa (por exemplo, uma função lowercase string, ao invés da nova novidade, a substituição ${varname,,pattern}, onde pattern é opcional e indica quais caracteres devem ser transformados (sim, o padrão casa com cada caractere; se o padrão tiver mais de um caractere ele não casa com nada); ou uma função substr string start length, ao invés da substituição ${varname:start:length}, cujo uso de : conflita com a substituição ${varname:-string}, o que impede que start comece com -).

Se tanto desenvolvedores quanto usuários do shell tendem a concordar que o shell não foi feito para ser uma "linguagem de verdade", poder-se-ia argumentar que eu que sou o perdido e que o propósito do shell é de fato ser uma linguagem "de brincadeira". Mas que sentido faz isso? Para que termos uma linguagem de programação pela metade, se podemos ter uma linguagem mais decente adicionando umas poucas funcionalidades básicas?

É possível argumentar alguns motivos técnicos para a resistência a estruturas de dados. Um deles é que o mecanismo de invocação de programas é totalmente baseado em strings: não é possível chamar um programa externo passando um array como argumento, por exemplo, e isso não é culpa do shell, e sim da maneira como programas são chamados no Unix. (O que por sinal é lamentável; seria ótimo poder passar estruturas complexas entre os programas. Voltaremos a esse assunto mais adiante.) Isso não é um problema insuperável; só porque comandos externos não podem receber dados estruturados não quer dizer que não possamos passar dados estruturados internamente entre as funções do shell. O caso em que o usuário tenta chamar um comando externo com um array exige um tratamento especial (transformar o array em string, ou em múltiplos argumentos, ou emitir um erro), mas isso não é motivo para eliminar estruturas de dados complexas da linguagem.

(Outro possível motivo é medinho de pôr um garbage collector dentro do shell. Há quem ache ainda hoje que garbage collection é coisa do demônio. Mas dada a popularidade de linguagens garbage-collected hoje em dia (Java, Perl, Python, Ruby, C#, etc.) e dada a existência de bibliotecas de garbage collection para C, esse também não é um motivo forte.)

Shells alternativos

Houve e há diversos shells que tentam escapar da tradição do Bourne shell. Um deles (que serviu de inspiração para outros) é o rc, o shell do Plan 9, que possui versões para Unix. O rc tem uma sintaxe um pouco mais limpa (ou não) do que o sh, mas não apresenta grandes avanços em termos de features. Uma diferença não diretamente relacionada com o shell é que no Plan 9 o exit status de um programa é uma string, e não um inteiro entre 0 e 255 como no Unix, o que possibilita usar o exit status como meio de retornar valores de funções. Porém, o shell não apresenta nenhum recurso sintático para executar uma função e substituir a chamada pelo exit status.

Inspirado no rc surgiu o es, um shell com funções de primeira classe, variáveis com escopo léxico, closures e exceptions. Uma característica interessante do es é que boa parte dos internals do shell são expostos ao usuário. Por exemplo, o operador pipe (|) é apenas açúcar sintático para uma chamada a uma função %pipe, que pode ser substituída pelo usuário de modo a modificar o comportamento do pipeline (um exemplo dado no artigo linkado é calcular o tempo de execução de cada programa da pipeline). O es possui listas/arrays, mas não permite listas aninhadas. (O motivo oferecido é fazer com que passagem de arrays para comandos externos e para funções tenham a mesma semântica; porém, dado que o shell tem que lidar com a possibilidade de o usuário tentar passar uma função para um programa externo, esse argumento não é tão convincente. Não sei o que o shell faz nessa situação; pelo artigo eu suponho que ele passe o corpo da função como uma string, e de fato parece que o shell armazena as funções internamente como strings.) O es também não possui outros tipos de estruturas de dados complexas, embora seja possível implementá-las (ainda que de maneira convoluta) através de closures. O es também permite retornar valores complexos a partir de funções, com uma sintaxe para chamar a função e recuperar o valor. Um ponto levantado no artigo é que esse mecanismo de retorno e o mecanismo de exceptions não interage bem com comandos externos: um script externo não tem como retornar valores ou propagar uma exception para o script que o chamou. Voltaremos a esse tópico mais adiante.

Um shell posterior inspirado no rc e no es é o shell do Inferno. Esse shell não tem grandes novidades comparado com o es (embora o fato de ele rodar no Inferno lhe confira alguns poderes mágicos, como por exemplo realizar comunicação pela rede atavés do sistema de arquivos, e embora ele tenha alguns módulos interessantes, tais como uma interface gráfica). Entretanto, um ponto que chama a atenção é a sintaxe: comandos como if, for e while não são tratados de maneira especial sintaticamente. Ao invés disso, eles são comandos normais que recebem blocos de código como argumentos. A idéia é similar ao método each do Ruby: ao invés de se usar uma estrutura de controle especial para iterar sobre os itens de uma lista, chama-se um método que recebe um bloco de código e o chama sobre cada elemento:

# Ruby.
a = [1, 2, 3, 4, 5]
a.each {|i|
    puts i
}

Com isso, o usuário pode definir novas estruturas de controle que se comportam de maneira similar às estruturas padrão da linguagem.

Outros shells alternativos incluem o fish, um shell com diversos recursos interativos, mas (na minha humilde opinião) sem grandes avanços em termos de programação, e o xmlsh, um shell escrito em Java cujo objetivo é permitir a manipulação de estruturas de dados baseadas em XML, e que conspira em última instância substituir a comunicação baseada em texto dos programas atuais do Unix por um modelo de comunicação estruturada baseada em XML. (Voltaremos a esse assunto mais adiante (ainda).)

O que eu faria diferente

Um shell tem dois casos de uso distintos: como um interpretador de comandos interativo, e como um interpretador de programas. O fato de que o shell deve ser conveniente de usar interativamente se reflete na sintaxe de sua linguagem: strings literais geralmente não precisam ser colocadas entre aspas; valores literais são mais freqüentes do que variáveis, e portanto nomes por si sós representam strings (o caso mais freqüente), e variáveis são indicadas com um símbolo especial ($); execução de comandos externos usa uma sintaxe simples; há uma sintaxe conveniente para gerar listas de arquivos (*.txt, ao invés de uma chamada de função), combinar entrada e saída de comandos (|), redirecionar entrada e saída para arquivos, etc. Essa moldagem da sintaxe ao redor do uso interativo limita as possibilidades sintáticas das features voltadas à programabilidade (e.g., sintaxe para chamadas de função, estruturas de dados, operações aritméticas).

Conveniências lingüísticas à parte, a minha opinião é de que o núcleo do shell deva ser a linguagem de programação em si, e não as facilidades de uso interativo. Coisas como edição de linha de comando, histórico e completamento automático de nomes de arquivos e comandos não deveriam ser internas ao shell; ao invés disso, a linguagem do shell deveria ser suficientemente capaz para que essas coisas todas pudessem ser implementadas como scripts.

Em termos de features da linguagem, é possível tornar o shell mais hábil sem quebrar (muito) a compatibilidade com sh e bash. O primeiro passo é criar um mecanismo para permitir retornar valores de funções sem criar um subshell. Para isso, é necessário definir um comando para retornar valores, e uma sintaxe para chamar uma função e recuperar o valor. Eu usaria reply valor (já que return N já está em uso, para sair da função com um exit status N) e $[função args...] para isso. (Atualmente $[...] causa avaliação aritmética em bash. A sintaxe $[...] é considerada obsoleta, em favor da sintaxe do padrão POSIX, $((...)).)

O segundo passo é tornar arrays elementos de primeira classe, permitindo passá-los para funções e retorná-los, e permitindo armazená-los onde quer que se possa armazenar um valor (por exemplo, dentro de outros arrays). Acaba-se com a noção de "array variable" do bash: uma variável contém um array, não é um array. $array não retorna mais o primeiro valor do array, e sim o array inteiro. É possível passar um array literal para uma função:

função arg1 (1 2 3) arg3

Convém criar uma sintaxe para arrays associativos, possivelmente %(chave valor chave valor ...). Também convém usar um operador diferente para indexar arrays associativos e não-associativos, já que em bash, índices de arrays não-associativos sofrem avaliação aritmética:

i=0
echo ${array_comum[i]}   # Elemento da i-ésima posição
echo ${array_assoc{i}}   # Elemento cuja chave é a string "i"

(Outra alternativa seria nunca fazer avaliação aritmética sem que o programador mande explicitamente, mas isso não só quebra a compatibilidade como é inconveniente na prática.)

Word splitting implícita sobre a os valores de variáveis fora de aspas teria a morte horrível que merece. Pathname expansion sobre valores de variáveis teria as três mortes horríveis que merece.

Em bash, os elementos de uma pipeline são executados em subshells (com exceção do primeiro (ou do último se a opção lastpipe estiver ativa, outra novidade do bash 4)), o que significa que uma função usada em um pipeline não tem como alterar os valores das variáveis globais, pois as alterações que fizer serão isoladas em seu subprocesso, o que freqüemente é inconveniente. Por exemplo, código desse tipo não funciona em bash (embora seja possível contornar o problema em muitos casos):

files=()
find . -name '*.txt' | while read file; do
    files+=("$file")
done

echo "${files[@]}"   # Array continua vazio

Uma feature desejável seria que todos os itens de uma pipeline que possam ser executados internamente ao shell o sejam. Uma solução é, ao invés de criar novos processos, criar threads, e simular os redirecionamentos de entrada e saída do pipe internamente (e.g., se um comando função1 | função2 é executado, o shell executa cada função em uma thread, e faz mágica internamente para dar à função1 a ilusão de que o que ela imprime para a stdout vai para a stdout (file descriptor 1), quando no entanto os dados vão parar em um outro file descriptor usado para fazer a comunicação entre as funções (ou mesmo em uma string, evitando chamadas de sistema para leitura e escrita em file descriptors)). O mesmo mecanismo pode ser usado para executar $(função args...) sem gerar um subprocesso.

Oops! Mas o ponto de inventar a sintaxe $[...] e reply ... era justamente evitar criar um subprocesso! Uma diferença, entretanto, é que a sintaxe $(...) só serve para retornar strings (pela sua definição), enquanto $[...] serve também para retornar estruturas de dados complexas.

Dado o paralelo entre $(...) e as pipelines, ocorre a idéia interessante de termos um equivalente do pipeline para $[...], i.e., um pipeline capaz de passar dados estruturados. Com isso, poderíamos escrever "generators", a la Python e companhia. Algo do tipo:

generate_factorials() {
    local i=1 val=1
    while :; do
        yield $val # Entrega um valor para a próxima função; aqui usamos um valor simples,
                   # mas poderíamos passar qualquer estrutura de dados.
        ((i++, val*=i))
    done
}

consume() {
    local val
    while val=$[take]; do
        echo "Recebi $val"
    done
}

generate_factorials ^ consume   # Sintaxe hipotética para o "pipeline de objetos"

Hmmrgh, agora temos dois conceitos parecidíssimos mas diferentes no shell. Quem sabe se ao invés de usar um tipo especial de pipeline, usássemos o mesmo mecanismo de pipeline para as duas coisas? Conceitualmente o comando yield então escreveria o valor para a fake stdout, e take o leria da fake stdin, enquanto internamente o shell transferiria o objeto de uma função para a outra. Da mesma forma, por consistência, o comando reply escreveria o valor de retorno para a fake stdout, e $[...] o leria da fake stdin. (Não temos como unificar $(...) e $[...] porque a semântica das duas expressões é diferente: uma retorna uma string, não importa o que o subcomando faça, enquanto a outra pode retornar qualquer tipo de valor. $[...] é análogo a um take, enquanto $(...) é análogo a um read.)

A questão é: se yield escreve na fake stdout, o que é que ele escreve? A princípio, não precisaria escrever nada "real": contanto que haja um take na outra ponta, poderíamos transferir o valor do yield para o take por mágica internamente, e apenas conceitualmente escrever no file descriptor correspondente à pipeline. Porém, se ela escrevesse alguma coisa, e essa coisa representasse o valor a ser transferido, as duas pontas do pipeline não precisariam mais estar no mesmo processo! Quer dizer, da mesma forma que o nosso pipeline comum sabe distinguir se as duas pontas estão em processos diferentes ou não, e usa file descriptors de verdade ou fake stdin/stdout dependendo do caso, o nosso pipeline de objetos também poderia fazer o mesmo. Se as duas pontas estão no mesmo processo, transferimos o objeto puro e simples. Mas se as duas pontas estão em processos distintos, podemos serializar o objeto de um lado e des-serializar do outro, de modo que é possível passar uma stream de objetos para um script externo de maneira transparente. Da mesma maneira, $[...] poderia funcionar com scripts externos, lendo o valor de retorno serializado da stdout do script e des-serializando-o novamente. Assim, resolvemos parte do problema mencionado no artigo do shell es: conseguimos fazer nossos valores complexos conviverem com o mundo texto-puro do Unix.

Em parte: falta dar um jeito de podermos passar argumentos complexos para os programas externos. A princípio poderíamos passar uma representação serializada dos argumentos. Porém, precisamos arranjar um meio de permitir ao programa chamado distinguir o array (1 2 3) da string composta pelos sete caracteres (1 2 3). Uma idéia seria sempre passar as strings com aspas em volta. Mas não podemos fazer isso porque não sabemos se o programa chamado é um script ou não, e portanto não sabemos se ele está preparado para entender as aspas (e.g., o comando ls não entende a opção "-la", com aspas em volta). Somos obrigados a passar as strings literalmente. Uma solução é passar uma variável de ambiente para o programa dizendo o tipo de cada argumento; se o subprocesso for um script, ele saberá interpretar a variável e distinguir strings de representações serializadas, e se for um outro programa qualquer, ele não dará bola para a variável*, interpretará as strings como strings, e as representações serializadas como strings também; não faz sentido passar outros objetos para não-scripts, de qualquer forma.

A semântica da passagem por serialização pode ser um pouco diferente da passagem direta dentro de um mesmo processo. Se as estruturas de dados são mutáveis, as mudanças em um processo diferente não se refletirão (a princípio) no processo original (pois o subprocesso tem uma cópia, não uma referência ao objeto original). Porém, isso não há de ser problema.

Um problema com passar valores pela stdout é que o valor de uma chamada de função não é ignorado por padrão. Isto é, enquanto na maior parte das linguagens de programação o valor de retorno de uma função é descartado por padrão se não for usado, no nosso caso a chamada imprime seu valor de retorno para a stdout, e para descartá-lo temos que tomar alguma ação explícita (um >/dev/null, por exemplo). Não sei até que ponto isso é um problema.

It's text all way down (and up)

A idéia de passar objetos estruturados entre os programas não é novidade. A grande glória das pipelines é permitir que combinemos diversos programas simples de modo a realizar uma tarefa mais complexa. No Unix, grande parte dos programas lêem e emitem texto em um formato simples (campos separados por algum caractere como TAB ou :, um registro por linha). A universalidade desse formato e a grande quantidade de ferramentas para manipulá-lo permite que combinemos programas que não foram necessariamente feitos para se comunicar um com o outro. Por exemplo, se quiséssemos contar quantos usuários locais usam cada shell, poderíamos usar um comando do tipo:

# cat /etc/passwd | cut -d: -f7 | sort | uniq -c | sort -rn
     17 /bin/sh
      5 /bin/false
      2 /bin/bash
      1 /usr/sbin/nologin
      1 /bin/sync

No entanto, texto puro por vezes deixa um pouco a desejar. E se quisermos trabalhar com dados hierárquicos, ao invés de tabelas? E se quisermos usar o separador dentro de um dos campos? Esse último problema é freqüente em programas que manipulam nomes de arquivo. No Unix, um nome de arquivo pode conter qualquer caractere, com exceção do ASCII NUL (a.k.a. \0) e da /, que é usada para separar nomes de diretórios. Isso significa que nomes de arquivo podem conter espaços, asteriscos, tabs e quebras de linha, entre outros, o que atrapalha a vida de muitos scripts. Por exemplo, se você usar um comando do tipo:

find / -size +4G >lista-de-arquivos-maiores-que-4-giga.txt

você corre o risco de uma alma perversa ter criado um arquivo com um \n nome, o que vai fazer com que esse nome pareça duas entradas distintas na lista de arquivos. A maior parte das ferramentas não é preparada para lidar com terminadores de linha diferentes de \n (embora diversas ferramentas do GNU tenham opções para lidar com \0). E se ao invés de passar texto puro entre os programas pudéssemos passar dados estruturados, em um formato compreendido por todas as ferramentas?

Como disse, essa idéia não é novidade. Talvez o projeto mais famoso a explorar essa possibilidade no Unix seja o TermKit, um projeto que objetiva liberar o shell do mundo do texto puro e dos emuladores de terminal. As ferramentas do TermKit se comunicam usando JSON e headers baseados em HTTP. A idéia é que, ao invés de transmitir bytes brutos, os dados que entram e saem dos processos carreguem consigo uma indicação de seu tipo (no header), de modo que as ferramentas saibam como lidar com o conteúdo que recebem. O TermKit foca na parte de interação com o usuário, e não provê um shell completo (programável).

Há uma thread longa sobre o assunto nos fóruns do xkcd.

Outro projeto nesse sentido é o xmlsh mencionado na última seção, que utiliza XML para a comunicação entre os processos.

No mundo não-Unix, um programa interessante é o PowerShell do Windows. No caso do PowerShell, os comandos realmente passam objetos entre si (e não representações serializadas). Isso é feito com uma pequena "trapaça": os comandos capazes de lidar com objetos não executam em processos externos, mas sim são instâncias de "cmdlets", objetos que fornecem uma interface para o shell, e que são instanciados dentro do próprio shell. Um exemplo retirado do artigo é o comando:

get-childitem | sort-object extension | select extension | where { $_.extension.length -eq 4 }

get-childitem é uma versão generalizada do ls, que funciona sobre diretórios e outras estruturas hierárquicas. sort-object recebe uma stream de objetos como entrada pelo pipeline, e os ordena pelo campo passado como argumento. select é similar ao cut do Unix, mas trabalha sobre objetos. where é um comando de filtro que recebe como argumento um predicado, i.e., uma função que é executada sobre cada elemento da entrada e decide se o elemento permanece na tabela final ou não.

O que eu acharia realmente ideal seria passar objetos entre processos, não apenas como um mecanismo interno do shell. Isso era possível na Lisp Machine, primariamente porque todos processos eram executados no mesmo espaço de endereçamento (i.e., os processos na verdade são threads). (Além disso, o fato de todos os programas do sistema terem uma linguagem comum (Lisp) garante que os objetos serão entendidos por todos os programas.) Permitir isso em um ambiente com processos isolados é uma tarefa mais complicada.

Mas estamos divergindo do ponto original e começando a sair do âmbito do tranqüilamente implementável. Por enquanto eu só quero criar um shell decentemente programável. Revolucionar a maneira como os programas se comunicam é tópico para outros posts.

O que mais eu faria diferente

Até agora adicionamos features de maneira semi-compatível com o sh. Mas podemos aproveitar a oportunidade para fazer uma limpeza geral no shell. Poucos são os fãs da sintaxe do sh [citation needed], então creio que poucos se objetarão terminantemente a um shell com sintaxe diferente.

Uma coisa que eu gostaria é de seguir o exemplo do shell do Inferno e usar comandos comuns e blocos de código ao invés de estruturas de controle com sintaxe especial. Assim, o usuário poderia definir estruturas de controle novas sem necessidade de alterar a sintaxe do shell. Exemplo hipotético:

function foreach (list body) {
    local i=0
    local length=$[length $list]

    while { $i < $length } {
        $body ${list[i]}
        i += 1
    }
}

# Exemplo de uso. (Sintaxe de bloco roubada do Ruby.)
foreach (*.txt) {|file|
    echo "Eis o arquivo $file:"
    cat $file
}

Tenho outras idéias para um shell, mas acho que esse post já está ficando longo demais. Talvez eu escreva sobre isso em outra ocasião (ou se alguém demonstrar interesse). Por ora ficamos por aqui.

Conclusão

Os pontos principais deste post são:

  • O shell é uma linguagem de programação; não existe um bom motivo para ele ser uma linguagem de programação pela metade;
  • É possível adicionar capacidades que favoreçam a programabilidade do shell sem prejudicar seu uso interativo (ainda que isso force o shell a usar uma sintaxe estranha para algumas coisas, de modo a acomodar mais naturalmente o uso interativo);
  • Todos são a favor da moção.

_____

* Mas o subprocesso pode passar a variável intacta para outros processos, o que eventualmente pode fazer a variável cair em outro shell, que ficará confuso achando que a variável se refere aos argumentos que recebeu. Uma solução é incluir no valor da variável o PID do processo a quem a variável se destina. Mas ainda há o risco de o processo fazer um exec() e executar um shell com o mesmo PID atual, ou de um PID se repetir. Hmmrrgh...

1 comentário / comment

Línguas mudam

2012-07-23 19:17 -0300. Tags: lang, rant, em-portugues

Ontem ocorreu a festinha de aniversário da minha irmã, e conseqüentemente uma reunião familiar. Como em mais de 50% das reuniões desse ramo da família, em algum ponto surgiu uma discussão sobre como as pessoas falam errado hoje em dia. O português "correto", dependendo da fase da lua, inclui:

21 anos de convivência me levaram a crer que discutir é inútil, embora eu ainda me arrisque a fazer um remark ou outro no meio das discussões. Vocês falam latim, pessoas? Esse tal de português correto a que vocês se referem nada mais é do que latim com dez ou quinze séculos de erros acumulados, penso eu comigo mesmo. E esse tal de latim nada mais é do que proto-indo-europeu com dez ou quinze séculos de erros acumulados. Mas eu fico na minha.

Desde pequeno eu observo que os tipos de erros cometidos por cada ramo da família são diferentes. Por exemplo, do lado paterno o para mim fazer (ao invés do para eu fazer do português "standard") é bastante comum. Do lado materno, isso praticamente não ocorre, mas em compensação há uma tendência geral a usar os pronomes retos ao invés dos oblíquos (ele viu nós). O ponto digno de observação é que os erros de cada grupo de falantes são consistentes. E uma vez que línguas nada mais são do que pura convenção, erros consistentes não são exatamente erros.

Convenção aqui é a palavra-chave: uma frase está "correta" se ela segue as convenções estabelecidas entre os falantes. Diferentes situações implicam diferentes convenções, entretanto: as convenções utilizadas para textos formais escritos são diferentes das convenções usadas pela língua formal falada, que são diferentes das convenções usadas na linguagem informal. A língua não é uma unidade uniforme, mas sim é composta por diferentes variantes, ou registros, que são usados conforme a situação. Exigir a presença das marcas de plural na língua formal escrita é perfeitamente válido; exigir que as pessoas pronunciem todas as marcas de plural em conversas informais é ridículo. (No caso da língua escrita em particular, especialmente em textos formais, o conservadorismo lingüístico é útil para garantir que textos escritos hoje possam ser compreendidos daqui a cinqüenta ou cem anos.)

Mas mesmo as variantes formais da língua sofrem variações com o tempo. Ninguém mais usa o vós, por exemplo. O pretérito mais-que-perfeito (eu fizera) está em vias de cair do cavalo também, embora algumas pessoas ainda gostem de usá-lo em textos literários. Hoje em dia há uma contenda quanto ao uso de pronome oblíquo em começo de frase (me parece correto, ao invés de parece-me correto). O pronome oblíquo inicial soa errado aos ouvidos de Portugal, e os gramatiqueiros prescritivistas brasileiros tendem a declarar a construção como inválida. No Brasil, entretanto, o pronome oblíquo inicial é comum na língua falada, mesmo nas variantes formais, e há uma tendência geral a se usar próclise sempre (i.e., colocar o pronome oblíquo sempre antes do verbo), e aparentemente essa é uma tendência já de séculos [citation needed]. Tentar forçar a língua escrita a um padrão estranho a todos os falantes da dita língua (o português do Brasil) é um tanto quanto tosco. É que nem tentar forçar os falantes de português do Brasil a escreverem estou a fazer, ao invés de estou fazendo. (Nesse caso, o português do Brasil é mais conservador, na verdade.)

Conseqüentemente, podemos concluir que daqui a uns duzentos ou trezentos anos provavelmente alguns usos do plural terão caído em desuso. "Mas como vamos viver sem o plural?", ouvem-se os gritos de horror de alguns. Bom, tecnicamente nós já estamos vivendo sem algumas marcas de plural na língua falada há muito tempo, e ninguém sofreu danos deletérios. Além disso, historicamente temos diversas instâncias de perdas de features gramaticais. Já ouviu falar do acusativo? Assim como em português nós marcamos o plural com uma flexão especial dos substantivos, adjetivos e pronomes, em latim havia uma flexão especial que se usava para marcar que uma palavra era o objeto direto de um verbo. Assim, puella poetam videt significava "a menina vê o poeta", mas puellam poeta videt significava "o poeta vê a menina". O -m final que costumava marcar o acusativo (o objeto direto) com o tempo passou de um m perfeitamente audível a uma simples nasalisação da vogal que o precede (de maneira similar, os m finais em português normalmente não são pronunciados como um m "de verdade"), e com o tempo simplesmente se perdeu. Como resultado, marca de acusativo em substantivos e adjetivos em português simplesmente não existe (embora resista em alguns pronomes, e.g., eu vs. me). "Como vamos viver sem acusativo?", ouviam-se os gritos de horror da antigüidade. Exatamente assim, respondemos nós. A marca do objeto direto por flexão deu lugar à indicação pela ordem da frase (i.e., a menina vê o poetao poeta vê a menina). E tem funcionado muito bem nos últimos dez séculos. Da mesma forma, a flexão de plural está dando lugar ao plural marcado apenas nos artigos e verbos, e em substantivos e adjetivos em contextos limitados.

"Mas então as línguas estão perdendo itens gramaticais o tempo todo? Daqui a pouco não vai sobrar nada da gramática!", ouvem-se os gritos de horror. Bom, não é bem assim. Ao mesmo tempo em que as línguas perdem features gramaticais, elas também adquirem features novas com o tempo. Por exemplo, na transição do latim para o português, a língua adquiriu um futuro do subjuntivo (quando eu fizer), que não existia em latim e é relativamente raro entre as línguas do mundo. O português também ganhou um igualmente raro infinitivo conjugado (para nós fazermos), que compartilha das mesmas formas do futuro do subjuntivo nos verbos regulares. O futuro do presente original do latim foi perdido e substituído por uma forma composta usando o verbo haver (amar hei (= hei de amar)), e posteriormente a forma composta foi readquirida como um tempo verbal (amarei). (Aliás, é por isso que a mesóclise é possível em português: o pronome pode ser intercalado no verbo porque o verbo é originalmente composto por duas palavras separadas.) O futuro está em vias de se perder de novo (amareivou amar), por sinal. É assim que funciona a porcaria.

Uma coisa que me deixa admirado nessa história de variantes lingüísticas é que muitas pessoas simplesmente não têm consciência de que usam mais de uma variante da língua. Ontem, durante a discussão, um dos presentes declarou que falava o português "correto". Só que isso não é verdade quanto a praticamente qualquer falante, se "correto" refere-se ao padrão literário da língua prescrito pelos gramatiqueiros. Ainda que algumas pessoas mais cuidadosas evitem omitir os plurais, a omissão dos -r finais de conjugações verbais (com exceção de pôr, for) é praticamente universal na língua falada, quando não se está dando ênfase ao verbo. O mesmo vale para a substituição de pronomes oblíquos de terceira pessoa por pronomes retos (eu encontrei ele na rua, ao invés de eu o encontrei na rua). Alguns "erros" são tão comuns que raramente são notados, mesmo quando a pessoa está cuidando para encontrar erros. Orações subordinadas são uma fonte particularmente freqüente de desvios:

Pronomes também têm adquirido uns usos curiosos ultimamente. Uma construção que eu vejo ser usada com bastante freqüência na língua falada são coisas do tipo o Instituto de Informática, ele fica no Campus do Vale (com um ele a mais referindo-se ao sujeito que acabou de ser mencionado). Algumas pessoas usam os pronomes de terceira pessoa como pronomes resumptivos (o cara que eu falei com ele). Alguns desses usos me dão coceira no rim, já que por alguma razão eu mesmo não os uso*. É normal, entretanto, que inovações lingüísticas se espalhem gradualmente, e portanto não tenham aceitação absoluta de imediato. Pode também acontecer de elas simplesmente se espalharem entre alguns grupos e não entre outros, produzindo dialetos divergentes.

No más.

[* Eu costumo cometer uma atrocidade diferente e dizer o cara que eu conversei com, roubando uma feature do inglês. Não acho provável que essa construção venha a adquirir uso amplo em português, entretanto.]

3 comentários / comments

Main menu

Recent posts

Recent comments

Tags

em-portugues (213) comp (147) prog (70) in-english (61) life (48) unix (38) pldesign (36) lang (32) random (28) about (28) mind (26) lisp (24) mundane (22) fenius (21) web (20) ramble (18) img (13) rant (12) hel (12) privacy (10) scheme (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.