[Brasil-PM] O problema do "smart matching"

breno breno em rio.pm.org
Segunda Abril 22 00:05:51 PDT 2013


Smart-matching é uma feature que surgiu na versão 10.0 do Perl 5, baseado
em seu equivalente do Perl 6:

http://perlcabal.org/syn/S03.html#Smart_matching

A idéia é simples: fazer a coisa certa ao compararmos 2 valores, sem
precisar ficar perdendo tempo formatando um deles para se adequar ao outro.
Em termos práticos, isso quer dizer que, se o item do lado direito da
operação "aceitar" o item do lado esquerdo, a operação é avaliada como
verdadeira. Senão, falsa. Por exemplo:

   my $x = 3;

   $x ~~ 3     # verdadeiro!
   $x ~~ 3.0  # verdadeiro!
   $x ~~ "3"   # verdadeiro!
   $x ~~ "   +3.000    "  # verdadeiro!
   $x ~~ "3D" # falso

O operador de smartmatch também é útil para saber se um item faz parte de
uma coleção:

  $x ~~ ( 1, 18, undef, 3 )  # verdadeiro!

Legal, né? De fato, lê-se "~~" como "em" (do inglês "in").

Então, qual é o problema?

O problema é que existem vários tipos de estruturas de dados em Perl e,
consequentemente, várias combinações possíveis para smartmatching. Pra
piorar a situação, a definição de "a coisa certa" a ser feita num
smartmatching varia muito de pessoa para pessoa, tanto que hoje é
praticamente impossível usar o ~~ para algo além de strings simples sem
olhar a tabela de equivalência dele na documentação - que diga-se de
passagem tem 288 linhas densas com regras, exceções e detalhes de uso.

O smartmatch tentou ser esperto demais, e isso acabou confundindo seus
usuários.

A primeira mudança aconteceu cedo, do 10.0 para o 10.1, quando o smartmatch
deixou de ser comutativo e passou a depender principalmente do tipo de dado
do lado direito da operação, entre outros pequenos ajustes.

Ainda assim, alguns comportamentos eram bem estranhos. Por exemplo, no
5.10.1, "undef ~~ 0" era avaliado como verdadeiro, assim como 'undef ~~
""'. Isso acontecia porque undef era transformado em 0 e em "" por coerção.
No entanto, a coerção não acontecia em todos os casos. Por exemplo, "undef
~~ [0]" era falso!

De lá pra cá, casos como esse geraram tanta discussão sobre qual seria o
comportamento correto do smartmatch que muitas mudanças foram (e continuam
sendo) feitas. Hoje o ~~ é controlado por uma dispatch table massiva
chamada em tempo de execução com mais de 20 rotas recursivas (sim,
recursivas) dependendo dos operandos. Você é capaz de dizer, por exemplo, o
que acontece, ou o que deveria acontecer, quando fazemos %hash ~~ @array?
Quer chutar, sem olhar o "perlop"? Nem eu.

O fato é que o comportamento do smartmatch para itens além do caso comum é
tão confuso que mesmo programadores experientes acabam optando por
simplesmente deixar pra lá e fingir que o smartmatch não existe. O que é
uma pena.

Depois de tantas discussões e mudanças de comportamento ao longo das
versões do perl, as funções que dependem do smartmatching (~~, given/when)
foram finalmente marcadas explicitamente como experimentais no perlsyn a
partir da versão 15.7, embora já tivessem sendo tratadas assim desde sua
introdução ao core, justamente por sua natureza extremamente volátil.

Muitos falam sobre tornar o ~~ obsoleto e retirá-lo do core, oferecendo-o
na forma de um plugin (https://github.com/doy/smartmatch,
https://github.com/doy/smartmatch-engine-core). Outros argumentam que a
retrocompatibilidade precisa ser mantida a qualquer custo, exceto em casos
de bugs. O problema dessa abordagem é que, como o smartmatch funciona hoje
de forma relativamente subjetiva, a definição do que é um bug e o que não é
também se torna subjetiva.

Felizmente, nosso atual Pumpking (e muitos outros desenvolvedores do core)
vem propondo uma solução intermediária ao problema, que visa simplificar o
smartmatching para que tenha um comportamento bem mais claro, previsível e
fácil de lembrar.

A nova proposta:
=============

* Se $a for qualquer coisa e $b for undef, "$a ~~ $b" significaria "$a em
undef?" e retorna verdadeiro se e somente se $a não estiver definido. Em
idioma Perl, dizer "$a ~~ undef" seria a mesma coisa que " ! defined $a ".

* Se $a for qualquer coisa e $b for um scalar simples (não referência, não
glob, não vstring) então "$a ~~ $b" seria uma comparação de igualdade.

* Se $a for qualquer coisa e $b for um coderef, dizer "$a ~~ $b"
significaria "$a em função $b?", ou seja, se o resultado da função $b
chamada com $a como parâmetro retorna verdadeiro. Em Perl, seria o mesmo
que dizer "$b->($a)".

* Se $a for qualquer coisa e $b for uma expressão regular, dizer "$a ~~ $b"
é o mesmo que "$a em regex $b?", ou seja: $a =~ $b.

* Se $a for qualquer coisa e $b for um objeto que faça overload do ~~,
dizer "$a ~~ $b" delega a verificação para o overload.

* Se $a for um objeto que faça overload do ~~ e $b for qualquer coisa, a
mesma lógica é usada (só que ao contrário).

* Qualquer outra comparação com ~~ gera exceção fatal.

Só isso. Essa proposta, se aceita, vai simplificar imensamente a
funcionalidade do smartmatching, bem como a documentação, que hoje é
espalhada em diversos exemplos e edge-cases no perlop e no perlsyn.

Essa idéia não vem sozinha, claro, especialmente pq os maiores defensores
do smartmatch argumentam sobre as vantagens de um operador "em" para
listas. A proposta desse novo smartmatch prevê também a inclusão de funções
de junction (junção) dentro do core do Perl 5, especificamente any() e
all(), que resolveriam o problema de forma bem mais elegante, já que
possibilitariam outras verificações entre listas além do smartmatching.

Já existe no CPAN uma implementação moderna de junctions em Perl 5:

https://metacpan.org/module/Syntax::Keyword::Junction

Esse módulo (e seus predecessores) estão servindo como teste de sintaxe e
funcionalidade, e se junctions entrarem para o core no futuro é bem
possível que assuma um formato muito parecido, senão igual, a esse. Para
saber mais sobre junctions, acesse o link acima.

Finalmente, se você curte o conceito de smartmatching, recomendo o módulo
do Leon Timmermans, Smart::Match, que propõe uma sintaxe mais explícita
para smartmatching dentro de seus programas.

Até lá, use o smartmatching com cuidado :-)


[]s

-b


P.S.: Pra participar mais ativamente da conversa sobre sobre o futuro do
smartmatching, assine a lista perl5porters (em inglês). Lembre-se de ler o
histórico antes de fazer perguntas, já que o tema já foi percorrido muitas
vezes, gerando discussões muitas vezes acaloradas. Pra facilitar quem quer
ficar no modo "somente leitura" do tema, o link abaixo mostra todas as
mensagens envolvendo smartmatching que circularam na p5p:

http://perl.markmail.org/search/?q=list%3Aorg.perl.perl5-porters+smart+matching
-------------- Próxima Parte ----------
Um anexo em HTML foi limpo...
URL: <http://mail.pm.org/pipermail/brasil-pm/attachments/20130422/6470bbce/attachment.html>


Mais detalhes sobre a lista de discussão Brasil-PM