Programação genéricaProgramação genérica é um paradigma de programação no qual os algoritmos são escritos em uma gramática estendida de forma a adaptar-se através da especificação das partes variáveis que são definidas na instância do algoritmo. Especificamente, a gramática estendida eleva um elemento não variável ou uma construção implícita na gramática base para uma variável ou constante, permitindo a utilização do código genérico. É diferente da forma normal de programação na medida em que invoca de certa forma as facilidades de metaprogramação da linguagem. Como isso ocorre em uma extensão da linguagem, novas semânticas são introduzidas e a linguagem é enriquecida no processo. É relacionada com a metaprogramação, mas não envolve a geração de código fonte, pelo menos visivelmente ao programador. É diferente também da programação por macros, já que esta refere-se somente a busca e substituição de termos, não fazendo parte da gramática da linguagem, implementada somente na fase de pré-processamento do código. Para efeitos práticos, o paradigma permite que um parâmetro assuma diferentes tipos de dados desde que certas regras sejam mantidas, como sub-tipos e assinaturas. Por exemplo, para criar uma lista usando programação genérica, uma possível declaração seria Entre linguagens orientadas a objeto, C++, Linguagem D, BETA, Eiffel e versões de Java (1.5 e acima) fornecem o paradigma genérico. Visual Basic .NET, C# e Delphi.Net começaram a fornecer o paradigma a partir do .NET 2.0. Muito antes de todas as linguagens mencionadas, programação genérica já havia sido implementada na década de 1970 em linguagens como CLU e Ada. Mas somente o conceito de templates do C++ que popularizou o conceito. A técnica permite que algoritmos sejam escritos independente dos tipos de dados utilizados. Os autores do conceituado livro de 1995 Design Patterns[1] referem-se à programação genérica como tipos parametrizados, o que permite que um tipo possa ser definido sem especificar todos os outros tipos que ele utilizada. Os autores ainda descrevem que tal técnica é muito poderosa, especialmente quando combinada com o padrão Delegar. Utilização em C++: templatesTemplates são de grande utilidade para programadores C++, especialmente quando combinado com herança múltipla e sobrecarga de operadores. A biblioteca padrão do C++ fornece várias funções úteis dentro de uma rede de templates conectados. Templates em C++ podem também serem usados para funções diversas à programação genérica. Um exemplo é a meta programação por templates, um método para executar algoritmos em tempo de compilação ao invés de execução. Visão geralExistem dois tipos de templates. Uma função template se comporta como uma função que pode aceitar argumentos de vários tipos diferentes. Por exemplo, a biblioteca STL do C++ contém funções template template <typename T>
T max(T x, T y)
{
if (x < y)
return y;
else
return x;
}
Esse template pode ser chamado para diversos tipos de dado: cout << max(3, 7); // imprime 7 -> tipo de dado é ''int''
cout << max(3.239, 5.238); // imprime 5.238 -> tipo de dado é ''float''
O compilador determina, ao examinar os argumentos, que a primeira chamada possui assinatura Isso somente funciona pois os tipos Como exemplo contrário, o tipo padrão Uma classe template amplia o mesmo conceito para classes. Elas são usadas geralmente para criar containers genéricos. Por exemplo, a STL possui o container Vantagens e desvantagensAlgumas aplicações de templates, como a função # define max(a,b) ( (a) < (b) ? (b) : (a) )
Tanto macros quanto templates são instanciados em tempo de compilação. Macros são sempre expandidas no próprio local onde foram utilizadas; templates também podem ser expandidos no próprio local onde foram utilizadas, se o compilador considerar adequado. Logo, tanto macros de função quanto funções template não exigem mais processamento na execução. Apesar disso, templates são considerados uma evolução sobre macros nesse propósito. Eles verificam a tipagem de dados, evitam alguns dos erros mais comuns ao se utilizar macros excessivamente e são mais robustos que macros. Antigamente, os compiladores não tinham bom suporte para templates. No entanto, praticamente todos os compiladores atuais do mercado conseguem lidar com templates sem problemas. Ainda assim, existem duas desvantagens no uso de templates. Primeiro, quase todos os compiladores produzem mensagens de erros em templates que são confusas e de pouca ajuda, o que torna o desenvolvimento mais difícil. Segundo, quanto mais tipos de dados diferentes utilizados em templates, geralmente mais código é gerado pelo compilador (uma versão da função ou classe para cada tipo de dado), logo, o uso indiscriminado pode levar a executáveis excessivamente grandes. O primeiro problema mencionado será eliminado com a chegada dos Conceitos novo padrão C++ (C++0x, que está previsto para 2009). Através dos conceitos será possível estabelecer um mecanismo formal para verificação de tipos e operações esperadas por um parâmetro template. Com isso, mensagens de erro serão sempre claras e objetivas. Utilização em HaskellEm Haskell, algumas extensões à linguagem foram desenvolvidas para a programação genérica. Além disso a própria linguagem contém alguns aspectos do paradigma incluídos. Suporte na própria linguagemDado a declaração de um tipo de dado definido pelo usuário (uma árvore binária com o tipo de dado data BinTree a = Leaf a | Node (BinTree a) a (Bintree a)
deriving (Eq, Show)
A palavra-chave Extensão PolyPPolyP foi a primeira linguagem de extensão para programação genérica para Haskell. Nelas as funções são chamadas polytypic. A linguagem introduz uma construção especial no qual as funções polytypic podem ser definidas por induções estruturais sobre a estrutura do functor do padrão de um tipo de dado. Tipos de dado regulares (nativos) são um sub-conjunto dos tipos regulares do Haskell. Um exemplo é dado abaixo: flatten :: Regular d => d a -> [a]
flatten = cata fl
polytypic fl :: f a [a] -> [a]
case f of
g+h -> either fl fl
g*h -> \(x,y) -> fl x ++ fl y
() -> \x -> []
Par -> \x -> [x]
Rec -> \x -> x
d@g -> concat . flatten . pmap fl
Con t -> \x -> []
cata :: Regular d => (FunctorOf d a b -> b) -> d a -> b
Utilização em OCamlEm OCaml, a programação genérica é implementada através de polimorfismo. Apesar da linguagem ter tipagem estática, os tipos não precisam ser declarados (a não ser no caso em que se pretende restringir a entrada ou saída de uma função), então sempre que possível os códigos escritos irão se aplicar a vários tipos de dados. Como um exemplo, a função: let duplicar lista = lista @ lista Irá duplicar a lista de entrada, ou seja, para uma lista que na sintaxe de OCaml seja escrita como Esse tipo indefinido é chamado de tipo polimórfico. OCaml representa o polimorfismo por letra, de forma que a função acima irá resultar na definição: val duplicar : 'a list -> 'a list = <fun> Indicando que a lista de entrada e a lista de saída são de um tipo indefinido (mas que são do mesmo tipo; caso fossem de tipos diferentes, uma seria 'a list, a outra 'b list). Referências
Leitura adicional
Ligações externas
|