[SP-pm] Entendendo Protótipos

Luis Motta Campos luismottacampos at yahoo.co.uk
Tue Dec 23 02:40:56 PST 2008


breno wrote:
> protótipos são uma espécie de verificação de argumentos em tempo de
> compilação, e permitem que vc chame subrotinas sem os parêntesis sem
> gerar ambiguidade, como as funções built-in do Perl ("push", por
> exemplo). A declaração de protótipos não envolve o nome da variável
> onde vc pretende colocá-la, apenas o símbolo do tipo de variável ($,
> @, %, &) que espera receber como parâmetros.

ERRADO.

Protótipos não verificam nada sobre o conteúdo dos parâmetros passados.
Não verificam nem mesmo se os parâmetros passados realmente existem.

A terminologia clássica de computação usa o termo "protótipo" para
indicar verificação cruzada de parâmetros passados para uma função,
especialmente em linguagens fortemente tipadas.

PROTÓTIPOS EM PERL SÃO ALGO COMPLETAMENTE DIFERENTE.

Protótipos em Perl são uma transformaão implícita e forçada dos
argumentos passados para uma função. Como efeito colateral desejável, o
interpretador Perl apenas "consome" tantos argumentos quantos uma função
prototipada declara aceitar.

Exemplos:

Esta é a implementação didática da função push() que eu uso para ensinar
protótipos:

sub ipush ( \@@ ) {
  my $array_ref = shift;
  @$array_ref = ( @$array_ref, @_ );
}

Esta função se comporta exatamente como o push, inclusive em termos de
preservar a integridade de array do primeiro argumento.

O Perl tenta fazer coercção controlada do primeiro argumento para algo
que seja do "tipo" (no sentido de "forma") descrito por "\@" (uma
referência para um array). Os outros argumentos são "coagidos" a se
tornarem um array (o que não faz muita diferença prática a não ser nos
casos mais extremos).

Assim, você pode invocar ipush(), passar um array como primeiro
argumento e ter certeza de que ele vai ser preservado:

ipush @stack, 1..5;

Mas, se você tentar passar outra coisa para o ipush() como primeiro
argumento, a coerção falha e o Perl reclama que o "tipo" (no sentido de
"forma") é incompatível:

ipush \@stack, 1..10;

resulta em

Type of arg 1 to main::ipush must be array (not reference constructor)
at tmp/ipush.pl line 12, near "10;"

O que o Perl fez é completamente diferente do que as nossas mentes de
programador tendem a entender e modelar para a gente: o Perl TRANSFORMA
os argumentos para que eles combinem com o protótipo ANTES de os colocar
na pilha de argumentos (@_, para os sem formação técnica em Ciências da
Computação).

Com isso, eu posso, por exemplo, preservar arrays quando da passagem
deles para uma função (como no exemplo do ipush()), ou fazer outras
"mágicas", como implementar lenght() para arrays:

sub ilenght ( $ ) { return shift; }

Atenção que agora eu estou fazendo outro tipo de "coerção" do argumento.
Desta vez, eu "obrigo" arrays a ter contexto escalar. E todo mundo sabe
que um array em contexto escalar resolve para o número de elementos
existentes no array.

Assim,

my @alpha = qw( a b c d e f );
ilenght @alpha;

Retorna, obviamente, 6.

Agora,

ilenght qw( a b c d e f );

Retorna "f". Porquê? [1]

Finalmente, você pode implementar if() com protótipos:

sub IF ( $& ) {
  my ( $condition, $code ) = @_;
  # ...
}

O que aceita

IF $boolean, \&sub_name;

mas não gosta de

IF $boolean, { code_here; };

Deixo como exercício para o leitor interessado descobrir como criar um
protótipo que aceite blocos de código, interessante para criar
Linguagens Específicas para um Domínio de problema, ou DSL, da sigla em
inglês para /Domain/ /Specific/ /Language/.

Eu poderia falar muito mais sobre exemplos de protótipos, mas acho que
estes já são o bastante para vocês saberem que eu já dei as minhas
cabeçadas neste assunto.

Agora, vamos falar de boas práticas: vocês gostariam de ter de manter um
código cheio de efeitos colaterais irritantemente complexos como os que
você pode conseguir chamando múltiplas combinações de funções com
protótipos encadeadas? Eu não.

Protótipos são uma tecnologia poderosa. O problema é que tanto poder
termina por ajudar você a não tremer quando você tenta atirar no dedão
do pé. Eu, particularmente, prefiro usar técnicas que me impeçam de
tentar atirar no dedão do meu pé...

A minha recomendação é: a não ser que você tenha uma situação
extremamente difícil e altamente controlada, você deve fazer as
transformações à mão, durante a chamada de cada função que você
escrever. Assim, não vão existir efeitos colaterais implícitos para te
deixar louco debugando programas até as três da manhã.

Eu praticamente não uso protótipos de funções em lugar nenhum no meu
código. Especialmente quando eu estou dando manutenção em outros sistemas.

Espero que as coisas estejam mais claras agora. Perguntem, meus
queridos. Perguntem.

Putamplexos.
-- 
Luis Motta Campos is a software engineer,
Perl Programmer, foodie and photographer.


[1] uma lista em contexto escalar, ao contrário de um array, resolve
para o último elemento da lista.


More information about the SaoPaulo-pm mailing list