A Declaração WITH-DO Perfeita


A utilização do WITH-DO pode deixar o código mais simples de ler, pois haverá menos declarações, atribuições e inicializações de variáveis. No entanto, a sintaxe atual não é perfeita. Dependendo do seu uso, o benefício pode ser o inverso.

Unsplash image Photo by Alex wong on Unsplash

Introdução

A declaração WITH-DO pode ser utilizada mas sua sintaxe não é perfeita.

Vai depender muito do design do seu código para que você possa utilizar o WITH-DO sem problemas.

Por exemplo. Classes com muitos atributos ou métodos poderá aumentar a probabilidade de haver alguma colisão de nomes entre outras classes e/ou variáveis locais.

Esse artigo é a minha proposta para melhorar a sintaxe do WITH-DO nos compiladores Object Pascal.

A Sintaxe Perfeita

O grande problema ao utilizarmos WITH-DO é a ambiguidade que ele pode trazer ao código.

Considere o código abaixo:

type
   TFoo = class
      X: Integer;
   end;

procedure Execute;
var
  X: Integer;
begin
  with TFoo.Create do
  try
     X := 10;
  finally
     Free;
  end;
end;

A linha X := 10; atribui 10 ao atributo de TFoo ou a variável local X?

Outro problema é o seguinte: se renomearmos TFoo.X para TFoo.Z poderíamos esperar um erro de compilação, no entanto o código iria continuar funcionando devido ao compilador localizar o identificador X como uma variável local.

A ambiguidade pode ser ainda maior quando utilizamos múltiplas instancias na declaração WITH-DO, como a seguir:

with TFoo.Create, TBar.Create do
try
  X := 10;
  Z := 20;
finally
  Free; // belongs to TFoo or TBar?
  Free; // belongs to TFoo or TBar?
end;

Esse são apenas alguns casos do uso irrestrito do WITH-DO. E, por essas e outras razões, que muitos desenvolvedores consideram o WITH-DO um mal no código.

No entanto, o que não está correto não é seu uso, mas sua sintaxe.

Na minha opinião, seria muito melhor se a sintaxe fosse algo parecido com o exemplo abaixo:

procedure Execute;
begin
  with F =: TFoo.New, B =: TBar.New do
  begin
    F.Execute;
    B.Execute;
  end;
end;

No código acima não há variáveis declaradas explicitamente, no entanto as instâncias são referenciadas por algum tipo de alias.

Afim de diferenciar essa nova sintaxe, para não haver ambiguidades com a sintaxe já existente de atribuição, escolhi utilizar uma “atribuição invertida” neste formato “=:”.

Alguns puristas do Pascal iriam dizer que o código acima não é muito “Pascalish” por quê tudo no Pascal deve ser explicitamente declarado antes e, neste caso, as variáveis não foram declaradas previamente. Eu concordo. Porém acho que deveríamos considerar essa exceção.

A sintaxe para a captura de exceções, por exemplo, tem (quase) a mesma sintaxe proposta qui e todos convivem bem com isso.

Por exemplo:

procedure Execute;
var
  X: Integer;
begin
  try
     X := 10 / 0;
  except
    on E: EDivByZero do
       ShowMessage(E.Message);
  end;
end;

A variável E não foi previamente declarada na seção de variáveis locais. Isso vai contra a filosofia Pascal, onde devemos declarar tudo antes de utilizar. Mas, tudo bem. Essa é uma exceção a regra que é bem vinda.

Voltando ao primeiro exemplo, você pode ter sentido falta às chamadas aos destrutores das instâncias. Bem, eu utilizei a função New justamente para não haver essa necessidade — considerando que esses métodos retornam uma instância de interface.

Mas se você não quiser utilizar essa técnica, basta reescrever assim:

with F =: TFoo.Create, B =: TBar.Create do
try
  F.Execute;
  B.Execute;
finally
  F.Free;
  B.Free;
end;

Na linguagem C# eles tem a declaração using que é bem parecido com o que estou propondo — a criação de alias para as instâncias. No entanto, lá o objetivo é o compilador chamar o método Dispose de cada instância no fim do bloco, independentemente se houver ou não uma exceção. Para que essa “mágica” aconteça, a instância deve implementar a interface IDisposable.

Eu acho essa abordagem desnecessária para o Pascal — no entanto, possível — pois o código fica mais explicito com o uso do try-finally se você não estiver utilizando instâncias de interfaces.

Conclusão

Acredito que o uso de WITH-DO proposto aqui iria nos ajudar a implementar um código mais simples.

A nova sintaxe com a atribuição invertida não iria entrar em conflito com a sintaxe atual.

Poderíamos criar blocos de código independentes dentro de métodos. Visto que, no exemplos acima, as variáveis F e B só poderiam ser utilizadas dentro do WITH-DO e não em todo o escopo do método.

Só iríamos declarar variáveis “globais ao método” na sessão de variáveis locais ao método, mas todas as outras instâncias seriam inicializadas em blocos WITH-DO para restringir ainda mais o escopo e visibilidade às variáveis.

Até logo.

Posts Relacionados

  • Memória Segura Utilizando Instâncias de Interfaces

  • Classes Mutáveis vs Objetos Imutáveis

  • Implementando Interfaces Utilizando Diferente Assinaturas de Métodos

  • Usando Paths ao invés de Diretivas de Compilação

  • Trabalhando com Exceções em Requisições HTTP

  • Tipo object Continua Vivo

  • Array de Objetos

  • Variáveis Locais Deveriam ter Nomes Curtos

  • Como Dividir e Organizar o Código em Formulários com Muitos Widgets

  • Pascal Deveria ser Modernizado?