Programação genérica

Programação genérica é um estilo de programação de computador no qual algoritmos são escritos em termos de tipos de dados a serem especificados posteriormente , que são então instanciados quando necessário para tipos específicos fornecidos como parâmetros . Essa abordagem, pioneira na linguagem de programação ML em 1973, [1] [2] permite escrever funções ou tipos comuns que diferem apenas no conjunto de tipos nos quais operam quando usados, reduzindo assim o código duplicado .

A programação genérica foi introduzida no mainstream com Ada em 1977. Com modelos em C++ , a programação genérica tornou-se parte do repertório de design de biblioteca profissional. As técnicas foram ainda mais aprimoradas e tipos parametrizados foram introduzidos no influente livro Design Patterns de 1994. [3]

Novas técnicas foram introduzidas por Andrei Alexandrescu em seu livro Modern C++ Design: Generic Programming and Design Patterns Applied de 2001. Posteriormente, D implementou as mesmas ideias.

Essas entidades de software são conhecidas como genéricas em Ada , C# , Delphi , Eiffel , F# , Java , Nim , Python , Go , Rust , Swift , TypeScript e Visual Basic .NET . Elas são conhecidas como polimorfismo paramétrico em ML , Scala , Julia e Haskell . (A terminologia Haskell também usa o termo "genérico" para um conceito relacionado, mas um tanto diferente.)

O termo programação genérica foi originalmente cunhado por David Musser e Alexander Stepanov [4] em um sentido mais específico do que o acima, para descrever um paradigma de programação no qual os requisitos fundamentais sobre tipos de dados são abstraídos de exemplos concretos de algoritmos e estruturas de dados e formalizados como conceitos , com funções genéricas implementadas em termos desses conceitos, normalmente usando mecanismos de genericidade de linguagem conforme descrito acima.

Stepanov–Musser e outros paradigmas de programação genéricos

A programação genérica é definida em Musser & Stepanov (1989) da seguinte forma:

A programação genérica gira em torno da ideia de abstração de algoritmos concretos e eficientes para obter algoritmos genéricos que podem ser combinados com diferentes representações de dados para produzir uma ampla variedade de software útil.

—  Musser, David R.; Stepanov, Alexander A., ​​Programação Genérica [5]

O paradigma da "programação genérica" ​​é uma abordagem à decomposição de software em que os requisitos fundamentais sobre os tipos são abstraídos de exemplos concretos de algoritmos e estruturas de dados e formalizados como conceitos , analogamente à abstração de teorias algébricas na álgebra abstrata . [6] Os primeiros exemplos desta abordagem de programação foram implementados em Scheme e Ada, [7] embora o exemplo mais conhecido seja a Standard Template Library (STL), [8] [9] que desenvolveu uma teoria de iteradores que é usada para desacoplar estruturas de dados de sequência e os algoritmos que operam nelas.

Por exemplo, dadas N estruturas de dados de sequência, por exemplo, lista encadeada simples, vetor etc., e M algoritmos para operar nelas, por exemplo find, , sortetc., uma abordagem direta implementaria cada algoritmo especificamente para cada estrutura de dados, dando N × M combinações para implementar. No entanto, na abordagem de programação genérica, cada estrutura de dados retorna um modelo de um conceito de iterador (um tipo de valor simples que pode ser desreferenciado para recuperar o valor atual ou alterado para apontar para outro valor na sequência) e cada algoritmo é, em vez disso, escrito genericamente com argumentos de tais iteradores, por exemplo, um par de iteradores apontando para o início e o fim da subsequência ou intervalo a ser processado. Assim, apenas N + M combinações de estrutura de dados-algoritmo precisam ser implementadas. Vários conceitos de iterador são especificados no STL, cada um um refinamento de conceitos mais restritivos, por exemplo, iteradores de avanço apenas fornecem movimento para o próximo valor em uma sequência (por exemplo, adequado para uma lista vinculada simples ou um fluxo de dados de entrada), enquanto um iterador de acesso aleatório também fornece acesso direto de tempo constante a qualquer elemento da sequência (por exemplo, adequado para um vetor). Um ponto importante é que uma estrutura de dados retornará um modelo do conceito mais geral que pode ser implementado eficientemente — requisitos de complexidade computacional são explicitamente parte da definição do conceito. Isso limita as estruturas de dados às quais um determinado algoritmo pode ser aplicado e tais requisitos de complexidade são um determinante importante da escolha da estrutura de dados. A programação genérica foi aplicada de forma semelhante em outros domínios, por exemplo, algoritmos de grafos. [10]

Embora essa abordagem frequentemente use recursos de linguagem de genericidade de tempo de compilação e modelos, ela é independente de detalhes técnicos de linguagem específicos. O pioneiro da programação genérica Alexander Stepanov escreveu,

A programação genérica é sobre abstrair e classificar algoritmos e estruturas de dados. Ela se inspira em Knuth e não na teoria de tipos. Seu objetivo é a construção incremental de catálogos sistemáticos de algoritmos e estruturas de dados úteis, eficientes e abstratos. Tal empreendimento ainda é um sonho.

—  Alexander Stepanov, Breve História da STL [11] [12]

Acredito que as teorias de iteradores são tão centrais para a Ciência da Computação quanto as teorias de anéis ou espaços de Banach são centrais para a Matemática.

—  Alexander Stepanov, Uma entrevista com A. Stepanov [13]

Bjarne Stroustrup observou,

Seguindo Stepanov, podemos definir programação genérica sem mencionar os recursos da linguagem: elevar algoritmos e estruturas de dados de exemplos concretos para sua forma mais geral e abstrata.

—  Bjarne Stroustrup, Evoluindo uma linguagem no e para o mundo real: C++ 1991-2006 [12]

Outros paradigmas de programação que foram descritos como programação genérica incluem a programação genérica Datatype , conforme descrito em "Programação genérica – uma introdução". [14] A abordagem Scrap your boilerplate é uma abordagem de programação genérica leve para Haskell. [15]

Neste artigo, distinguimos os paradigmas de programação de alto nível da programação genérica , acima, dos mecanismos de genericidade da linguagem de programação de nível inferior usados ​​para implementá-los (consulte Suporte à linguagem de programação para genericidade). Para mais discussão e comparação de paradigmas de programação genérica, consulte. [16]

Suporte de linguagem de programação para genericidade

Recursos de genericidade existem em linguagens de alto nível desde pelo menos a década de 1970, em linguagens como ML , CLU e Ada , e foram posteriormente adotados por muitas linguagens baseadas e orientadas a objetos , incluindo BETA , C++ , D , Eiffel , Java e a extinta Trellis-Owl da DEC .

A genericidade é implementada e suportada de forma diferente em várias linguagens de programação; o termo "genérico" também tem sido usado de forma diferente em vários contextos de programação. Por exemplo, em Forth o compilador pode executar código enquanto compila e pode-se criar novas palavras-chave do compilador e novas implementações para essas palavras na hora. Ele tem poucas palavras que expõem o comportamento do compilador e, portanto, naturalmente oferece capacidades de genericidade que, no entanto, não são referidas como tal na maioria dos textos de Forth. Da mesma forma, linguagens dinamicamente tipadas, especialmente as interpretadas, geralmente oferecem genericidade por padrão, pois tanto a passagem de valores para funções quanto a atribuição de valores são indiferentes ao tipo e esse comportamento é frequentemente usado para abstração ou concisão de código, no entanto, isso não é tipicamente rotulado como genericidade , pois é uma consequência direta do sistema de tipagem dinâmica empregado pela linguagem. [ citação necessária ] O termo tem sido usado em programação funcional , especificamente em linguagens do tipo Haskell , que usam um sistema de tipo estrutural onde os tipos são sempre paramétricos e o código real nesses tipos é genérico. Esses usos ainda atendem a um propósito semelhante de economia de código e renderização de uma abstração.

Arrays e structs podem ser vistos como tipos genéricos predefinidos. Cada uso de um tipo array ou struct instancia um novo tipo concreto, ou reutiliza um tipo instanciado anteriormente. Tipos de elementos array e tipos de elementos struct são tipos parametrizados, que são usados ​​para instanciar o tipo genérico correspondente. Tudo isso é geralmente embutido no compilador e a sintaxe difere de outras construções genéricas. Algumas linguagens de programação extensíveis tentam unificar tipos genéricos embutidos e definidos pelo usuário.

Segue-se um amplo levantamento de mecanismos de genericidade em linguagens de programação. Para um levantamento específico comparando a adequação de mecanismos para programação genérica, veja. [17]

Em linguagens orientadas a objetos

Ao criar classes de contêiner em linguagens estaticamente tipadas, é inconveniente escrever implementações específicas para cada tipo de dado contido, especialmente se o código para cada tipo de dado for virtualmente idêntico. Por exemplo, em C++, essa duplicação de código pode ser contornada definindo um modelo de classe:

template < typename T > class List { // Conteúdo da classe. }; 
  
  


Lista < Animal > lista_de_animais ; Lista < Carro > lista_de_carros ; 
 

Acima, Thá um espaço reservado para qualquer tipo especificado quando a lista é criada. Esses "contêineres-do-tipo-T", comumente chamados de templates , permitem que uma classe seja reutilizada com diferentes tipos de dados, desde que certos contratos, como subtipos e assinatura , sejam mantidos. Esse mecanismo de genericidade não deve ser confundido com polimorfismo de inclusão , que é o uso algorítmico de subclasses intercambiáveis: por exemplo, uma lista de objetos do tipo Moving_Objectcontendo objetos do tipo Animale Car. Templates também podem ser usados ​​para funções independentes de tipo, como no Swapexemplo abaixo:

// "&" denota um 
modelo de referência < typename T > void Swap ( T & a , T & b ) { // Uma função semelhante, mas mais segura e potencialmente mais rápida // é definida no cabeçalho da biblioteca padrão <utility> T temp = b ; b = a ; a = temp ; } 
      
                        
     
    
    


std :: string world = "Mundo!" ; std :: string hello = "Olá, " ; Swap ( world , hello ); std :: cout << world << hello << '\ n ' ; // A saída é "Olá, Mundo!".   
   
 
        

O templateconstructo C++ usado acima é amplamente citado [ citação necessária ] como o constructo de genericidade que popularizou a noção entre programadores e designers de linguagem e suporta muitos idiomas de programação genéricos. A linguagem de programação D também oferece modelos totalmente genéricos com base no precedente C++, mas com uma sintaxe simplificada. A linguagem de programação Java fornece facilidades de genericidade sintaticamente baseadas em C++ desde a introdução do Java Platform, Standard Edition (J2SE) 5.0.

C# 2.0, Oxygene 1.5 (antigo Chrome) e Visual Basic .NET 2005 têm construções que exploram o suporte a genéricos presentes no Microsoft .NET Framework desde a versão 2.0.

Genéricos em Ada

Ada tem genéricos desde que foi projetada pela primeira vez em 1977–1980. A biblioteca padrão usa genéricos para fornecer muitos serviços. Ada 2005 adiciona uma biblioteca de contêiner genérico abrangente à biblioteca padrão, que foi inspirada pela biblioteca de modelo padrão do C++ .

Uma unidade genérica é um pacote ou um subprograma que aceita um ou mais parâmetros formais genéricos . [18]

Um parâmetro formal genérico é um valor, uma variável, uma constante, um tipo, um subprograma ou mesmo uma instância de outra unidade genérica designada. Para tipos formais genéricos, a sintaxe distingue entre tipos discretos, de ponto flutuante, de ponto fixo, de acesso (ponteiro), etc. Alguns parâmetros formais podem ter valores padrão.

Para instanciar uma unidade genérica, o programador passa parâmetros reais para cada formal. A instância genérica então se comporta como qualquer outra unidade. É possível instanciar unidades genéricas em tempo de execução , por exemplo, dentro de um loop.

Exemplo

A especificação de um pacote genérico:

 genérico 
    Max_Size  :  Natural ;  -- um 
    tipo de valor formal genérico  Element_Type  é  privado ;  -- um tipo formal genérico; aceita qualquer 
 pacote de tipo não limitado  Stacks  é 
    tipo  Size_Type  é  intervalo  0  ..  Max_Size ; 
    tipo  Stack  é  limitado  privado ; 
    procedimento  Create  ( S  : out  Stack ; 
                      Initial_Size  : in  Size_Type  :=  Max_Size ); 
    procedimento  Push  ( Into  : in  out  Stack ;  Element  : in  Element_Type ); 
    procedimento  Pop  ( From  : in  out  Stack ;  Element  : out  Element_Type ); 
    Overflow  :  exceção ; 
    Underflow  :  exceção ; 
 privado 
    subtipo  Index_Type  é  intervalo Size_Type  1 .. Max_Size ; tipo Vector é array ( Index_Type intervalo <>) de Element_Type ; tipo Stack ( Allocated_Size : Size_Type := 0 ) é registro Top : Index_Type ; Armazenamento : Vector ( 1 .. Allocated_Size ); fim do registro ; fim das pilhas ;   
            
           
         
            
    
  

Instanciando o pacote genérico:

 tipo  Bookmark_Type  é  novo  Natural ; 
 -- registra um local no documento de texto que estamos editando

 pacote  Bookmark_Stacks  é novo  Stacks  ( Max_Size  => 20 , 
                                        Element_Type  => Bookmark_Type ); 
 -- Permite que o usuário pule entre os locais gravados em um documento

Usando uma instância de um pacote genérico:

 tipo  Document_Type  é  registro 
    Conteúdo  :  Ada . Strings . Unbounded . Unbounded_String ; 
    Marcadores  :  Bookmark_Stacks . Stack ; 
 fim do registro ;

 procedure  Edit  ( Document_Name  : in  String )  is 
   Document  :  Document_Type ; 
 begin 
   -- Inicializa a pilha de marcadores: 
   Bookmark_Stacks . Create  ( S  =>  Document . Bookmarks ,  Initial_Size  =>  10 ); 
   -- Agora, abra o arquivo Document_Name e leia-o em... 
 end  Edit ;
Vantagens e limites

A sintaxe da linguagem permite a especificação precisa de restrições em parâmetros formais genéricos. Por exemplo, é possível especificar que um tipo formal genérico aceitará apenas um tipo modular como o real. Também é possível expressar restrições entre parâmetros formais genéricos; por exemplo:

 
    tipo  genérico Index_Type  é  (<>);  -- deve ser um tipo discreto 
    type  Element_Type  é  privado ;  -- pode ser qualquer tipo não limitado 
    type  Array_Type  é  array  ( Index_Type  range  <>)  de  Element_Type ;

Neste exemplo, Array_Type é restringido por Index_Type e Element_Type. Ao instanciar a unidade, o programador deve passar um tipo de array real que satisfaça essas restrições.

A desvantagem desse controle refinado é uma sintaxe complicada, mas, como todos os parâmetros formais genéricos são completamente definidos na especificação, o compilador pode instanciar genéricos sem olhar para o corpo do genérico.

Diferentemente de C++, Ada não permite instâncias genéricas especializadas e requer que todos os genéricos sejam instanciados explicitamente. Essas regras têm várias consequências:

  • o compilador pode implementar genéricos compartilhados : o código objeto para uma unidade genérica pode ser compartilhado entre todas as instâncias (a menos que o programador solicite inlining de subprogramas, é claro). Como consequências adicionais:
    • não há possibilidade de inchaço de código (inchaço de código é comum em C++ e requer cuidado especial, conforme explicado abaixo).
    • é possível instanciar genéricos em tempo de execução e em tempo de compilação, já que nenhum novo código de objeto é necessário para uma nova instância.
    • objetos reais correspondentes a um objeto formal genérico são sempre considerados não estáticos dentro do genérico; veja Objetos formais genéricos no Wikibook para detalhes e consequências.
  • todas as instâncias de um genérico são exatamente as mesmas, é mais fácil revisar e entender programas escritos por outros; não há "casos especiais" a serem levados em consideração.
  • sendo todas as instanciações explícitas, não há instanciações ocultas que possam dificultar a compreensão do programa.
  • Ada não permite computação arbitrária em tempo de compilação, porque operações em argumentos genéricos são executadas em tempo de execução.

Modelos em C++

C++ usa templates para habilitar técnicas de programação genéricas. A C++ Standard Library inclui a Standard Template Library ou STL que fornece uma estrutura de templates para algoritmos e estruturas de dados comuns. Templates em C++ também podem ser usados ​​para metaprogramação de template , que é uma maneira de pré-avaliar parte do código em tempo de compilação em vez de tempo de execução . Usando especialização de template, os Templates C++ são Turing completos .

Visão geral técnica

Existem muitos tipos de modelos, sendo os mais comuns modelos de função e modelos de classe. Um modelo de função é um padrão para criar funções comuns com base nos tipos de parametrização fornecidos quando instanciados. Por exemplo, a C++ Standard Template Library contém o modelo de função max(x, y)que cria funções que retornam x ou y, o que for maior. max()poderia ser definido assim:

modelo < nome do tipo T > T max ( T x , T y ) { retornar x < y ? y : x ; } 
     
         

Especializações deste modelo de função, instanciações com tipos específicos, podem ser chamadas como uma função comum:

std :: cout << max ( 3 , 7 ); // Saídas 7.     

O compilador examina os argumentos usados ​​para chamar maxe determina que esta é uma chamada para max(int, int). Ele então instancia uma versão da função onde o tipo de parametrização Té int, fazendo o equivalente da seguinte função:

int max ( int x , int y ) { retornar x < y ? y : x ; }     
         

Isso funciona se os argumentos xe yforem inteiros, strings ou qualquer outro tipo para o qual a expressão x < yseja sensata, ou mais especificamente, para qualquer tipo para o qual operator<seja definido. A herança comum não é necessária para o conjunto de tipos que podem ser usados ​​e, portanto, é muito semelhante à tipagem de pato . Um programa que define um tipo de dados personalizado pode usar sobrecarga de operadores para definir o significado de <para esse tipo, permitindo assim seu uso com o max()modelo de função. Embora isso possa parecer um benefício menor neste exemplo isolado, no contexto de uma biblioteca abrangente como a STL, permite que o programador obtenha ampla funcionalidade para um novo tipo de dados, apenas definindo alguns operadores para ele. A mera definição <permite que um tipo seja usado com os algoritmos padrão sort(), stable_sort()e binary_search()ou seja colocado dentro de estruturas de dados como sets, heaps e matrizes associativas .

Os modelos C++ são completamente seguros em tempo de compilação. Como demonstração, o tipo padrão complexnão define o <operador, porque não há uma ordem estrita em números complexos . Portanto, max(x, y)falhará com um erro de compilação, se x e y forem complexvalores. Da mesma forma, outros modelos que dependem de <não podem ser aplicados a complexdados a menos que uma comparação (na forma de um functor ou função) seja fornecida. Por exemplo: A complexnão pode ser usado como chave para a mapa menos que uma comparação seja fornecida. Infelizmente, os compiladores historicamente geram mensagens de erro um tanto esotéricas, longas e inúteis para esse tipo de erro. Garantir que um determinado objeto adira a um protocolo de método pode aliviar esse problema. Linguagens que usam compareem vez de <também podem usar complexvalores como chaves.

Outro tipo de template, um template de classe, estende o mesmo conceito para classes. Uma especialização de template de classe é uma classe. Templates de classe são frequentemente usados ​​para fazer contêineres genéricos. Por exemplo, o STL tem um contêiner de lista encadeada . Para fazer uma lista encadeada de inteiros, escreve-se list<int>. Uma lista de strings é denotada list<string>. A listtem um conjunto de funções padrão associadas a ele, que funcionam para quaisquer tipos de parametrização compatíveis.

Especialização de modelo

Um recurso poderoso dos templates do C++ é a especialização de template . Isso permite que implementações alternativas sejam fornecidas com base em certas características do tipo parametrizado que está sendo instanciado. A especialização de template tem dois propósitos: permitir certas formas de otimização e reduzir o inchaço do código.

Por exemplo, considere uma sort()função de modelo. Uma das principais atividades que tal função faz é trocar ou trocar os valores em duas posições do contêiner. Se os valores forem grandes (em termos do número de bytes necessários para armazenar cada um deles), então geralmente é mais rápido primeiro construir uma lista separada de ponteiros para os objetos, classificar esses ponteiros e então construir a sequência classificada final. Se os valores forem bem pequenos, entretanto, geralmente é mais rápido apenas trocar os valores no local conforme necessário. Além disso, se o tipo parametrizado já for de algum tipo de ponteiro, então não há necessidade de construir uma matriz de ponteiros separada. A especialização de modelo permite que o criador do modelo escreva implementações diferentes e especifique as características que o(s) tipo(s) parametrizado(s) deve(m) ter para que cada implementação seja usada.

Diferentemente de modelos de função, modelos de classe podem ser parcialmente especializados . Isso significa que uma versão alternativa do código do modelo de classe pode ser fornecida quando alguns dos parâmetros do modelo são conhecidos, enquanto deixa outros parâmetros do modelo genéricos. Isso pode ser usado, por exemplo, para criar uma implementação padrão (a especialização primária ) que assume que copiar um tipo de parametrização é caro e então criar especializações parciais para tipos que são baratos para copiar, aumentando assim a eficiência geral. Clientes de tal modelo de classe apenas usam especializações dele sem precisar saber se o compilador usou a especialização primária ou alguma especialização parcial em cada caso. Modelos de classe também podem ser totalmente especializados, o que significa que uma implementação alternativa pode ser fornecida quando todos os tipos de parametrização são conhecidos.

Vantagens e desvantagens

Alguns usos de templates, como a max()função, eram preenchidos anteriormente por macros de pré-processador do tipo função (um legado da linguagem C ). Por exemplo, aqui está uma possível implementação de tal macro:

#define max(a,b) ((a) < (b) ? (b) : (a))

Macros são expandidas (copiadas e coladas) pelo pré-processador , antes da compilação adequada; templates são funções reais. Macros são sempre expandidas inline; templates também podem ser funções inline quando o compilador considera apropriado.

No entanto, os modelos são geralmente considerados uma melhoria em relação às macros para esses propósitos. Os modelos são seguros em relação ao tipo. Os modelos evitam alguns dos erros comuns encontrados em códigos que fazem uso pesado de macros do tipo função, como avaliar parâmetros com efeitos colaterais duas vezes. Talvez o mais importante seja que os modelos foram projetados para serem aplicáveis ​​a problemas muito maiores do que as macros.

Há quatro desvantagens principais no uso de modelos: recursos suportados, suporte ao compilador, mensagens de erro ruins (geralmente com SFINAE pré-C++20 ) e inchaço de código :

  1. Os modelos em C++ não têm muitos recursos, o que torna a implementação e o uso deles de forma direta muitas vezes impossível. Em vez disso, os programadores têm que confiar em truques complicados, o que leva a um código inchado, difícil de entender e difícil de manter. Os desenvolvimentos atuais nos padrões C++ exacerbam esse problema ao fazer uso pesado desses truques e construir muitos recursos novos para modelos neles ou com eles em mente.
  2. Historicamente, muitos compiladores tinham suporte ruim para templates, portanto, o uso de templates poderia ter tornado o código um pouco menos portátil. O suporte também pode ser ruim quando um compilador C++ está sendo usado com um linker que não é compatível com C++, ou ao tentar usar templates em limites de bibliotecas compartilhadas .
  3. Os compiladores podem produzir mensagens de erro confusas, longas e, às vezes, inúteis quando são detectados erros no código que usa SFINAE. [19] Isso pode dificultar o desenvolvimento de modelos.
  4. Finalmente, o uso de modelos requer que o compilador gere uma instância separada da classe ou função modelo para cada parâmetro de tipo usado com ela. (Isso é necessário porque os tipos em C++ não são todos do mesmo tamanho, e os tamanhos dos campos de dados são importantes para o funcionamento das classes.) Então, o uso indiscriminado de modelos pode levar ao inchaço do código , resultando em executáveis ​​excessivamente grandes. No entanto, o uso criterioso da especialização e derivação de modelos pode reduzir drasticamente esse inchaço do código em alguns casos:

    Então, a derivação pode ser usada para reduzir o problema de código replicado porque modelos são usados? Isso envolveria derivar um modelo de uma classe comum. Essa técnica provou ser bem-sucedida em conter o inchaço do código no uso real. Pessoas que não usam uma técnica como essa descobriram que o código replicado pode custar megabytes de espaço de código, mesmo em programas de tamanho moderado.

    —  Bjarne Stroustrup , O Design e a Evolução do C++, 1994 [20]
  5. Classes ou funções modeladas podem exigir uma especialização explícita da classe template, o que exigiria a reescrita de uma classe inteira para parâmetros de template específicos usados ​​por ela.

As instanciações extras geradas por modelos também podem fazer com que alguns depuradores tenham dificuldade em trabalhar graciosamente com modelos. Por exemplo, definir um ponto de interrupção de depuração dentro de um modelo de um arquivo de origem pode perder a definição do ponto de interrupção na instanciação real desejada ou pode definir um ponto de interrupção em todos os lugares em que o modelo é instanciado.

Além disso, o código-fonte de implementação para o modelo deve estar completamente disponível (por exemplo, incluído em um cabeçalho) para a unidade de tradução (arquivo-fonte) que o utiliza. Modelos, incluindo grande parte da Biblioteca Padrão, se não forem incluídos em arquivos de cabeçalho, não podem ser compilados. (Isso contrasta com o código não-modelado, que pode ser compilado para binário, fornecendo apenas um arquivo de cabeçalho de declarações para o código que o utiliza.) Isso pode ser uma desvantagem ao expor o código de implementação, o que remove algumas abstrações, e pode restringir seu uso em projetos de código fechado. [ citação necessária ]

Modelos em D

A linguagem D suporta templates baseados em design em C++. A maioria dos idiomas de template C++ funcionam em D sem alteração, mas D adiciona alguma funcionalidade:

  • Os parâmetros de modelo em D não são restritos apenas a tipos e valores primitivos (como era em C++ antes do C++20), mas também permitem valores arbitrários em tempo de compilação (como strings e literais de struct) e aliases para identificadores arbitrários, incluindo outros modelos ou instanciações de modelos.
  • As restrições de modelo e a declaração fornecem uma alternativa aos conceitosstatic if C++ e C++ , respectivamente .if constexpr
  • A is(...)expressão permite instanciação especulativa para verificar as características de um objeto em tempo de compilação.
  • A autopalavra-chave e a typeofexpressão permitem inferência de tipo para declarações de variáveis ​​e valores de retorno de função, o que por sua vez permite "tipos Voldemort" (tipos que não têm um nome global). [21]

Os modelos em D usam uma sintaxe diferente do que em C++: enquanto em C++ os parâmetros de modelo são envolvidos em colchetes angulares ( Template<param1, param2>), D usa um sinal de exclamação e parênteses: Template!(param1, param2). Isso evita as dificuldades de análise sintática de C++ devido à ambiguidade com operadores de comparação. Se houver apenas um parâmetro, os parênteses podem ser omitidos.

Convencionalmente, D combina os recursos acima para fornecer polimorfismo em tempo de compilação usando programação genérica baseada em trait. Por exemplo, um intervalo de entrada é definido como qualquer tipo que satisfaça as verificações realizadas por isInputRange, que é definido como segue:

template isInputRange ( R ) { enum bool isInputRange = is ( typeof ( ( inout int = 0 ) { R r = R . init ; // pode definir um objeto de intervalo if ( r . empty ) {} // pode testar se está vazio r . popFront (); // pode invocar popFront() auto h = r . front ; // pode obter a frente do intervalo })); } 

        
       
    
                
             
             
            
    

Uma função que aceita apenas intervalos de entrada pode então usar o modelo acima em uma restrição de modelo:

auto fun ( Range )( Range range ) if ( isInputRange ! Range ) { // ... }  
     

    

Geração de código

Além da metaprogramação de modelos, D também fornece vários recursos para permitir a geração de código em tempo de compilação:

  • A importexpressão permite ler um arquivo do disco e usar seu conteúdo como uma expressão de string.
  • A reflexão em tempo de compilação permite enumerar e inspecionar declarações e seus membros durante a compilação.
  • Atributos definidos pelo usuário permitem que os usuários anexem identificadores arbitrários às declarações, que podem então ser enumeradas usando reflexão em tempo de compilação.
  • A execução de função em tempo de compilação (CTFE) permite que um subconjunto de D (restrito a operações seguras) seja interpretado durante a compilação.
  • Os mixins de string permitem avaliar e compilar o conteúdo de uma expressão de string como código D que se torna parte do programa.

A combinação do acima permite gerar código com base em declarações existentes. Por exemplo, frameworks de serialização D podem enumerar os membros de um tipo e gerar funções especializadas para cada tipo serializado para executar serialização e desserialização. Atributos definidos pelo usuário podem indicar ainda mais regras de serialização.

A importexpressão e a execução da função de tempo de compilação também permitem implementar eficientemente linguagens específicas de domínio . Por exemplo, dada uma função que pega uma string contendo um modelo HTML e retorna código-fonte D equivalente, é possível usá-la da seguinte maneira:

// Importe o conteúdo de example.htt como uma constante de manifesto de string. 
enum htmlTemplate = import ( "example.htt" );   

// Transpila o modelo HTML para código D. 
enum htmlDCode = htmlTemplateToD ( htmlTemplate );   

// Cole o conteúdo de htmlDCode como código D. 
mixin ( htmlDCode );

Genericidade em Eiffel

Classes genéricas têm sido parte de Eiffel desde o método original e o design da linguagem. As publicações de fundação de Eiffel, [22] [23] usam o termo genericidade para descrever a criação e o uso de classes genéricas.

Genericidade básica e irrestrita

As classes genéricas são declaradas com seu nome de classe e uma lista de um ou mais parâmetros genéricos formais . No código a seguir, a classe LISTtem um parâmetro genérico formalG

class 
LIST [ G ] ... feature -- Acessar item : G -- O item atualmente apontado pelo cursor ... feature -- Alteração de elemento put ( new_item : G ) -- Adicionar `new_item' no final da lista ...     
            
   
     
            
            
   
      
            
            

Os parâmetros genéricos formais são marcadores de posição para nomes de classes arbitrários que serão fornecidos quando uma declaração da classe genérica for feita, conforme mostrado nas duas derivações genéricas abaixo, onde ACCOUNTe DEPOSITsão outros nomes de classes. ACCOUNTe DEPOSITsão considerados parâmetros genéricos reais , pois fornecem nomes de classes reais para substituir Gno uso real.

    list_of_accounts : LIST [ CONTA ] -- Lista de contas  
            

    list_of_deposits : LIST [ DEPOSIT ] -- Lista de depósitos  
            

Dentro do sistema de tipos Eiffel, embora class LIST [G]seja considerada uma classe, ela não é considerada um tipo. No entanto, uma derivação genérica de LIST [G]such as LIST [ACCOUNT]é considerada um tipo.

Genericidade restrita

Para a classe de lista mostrada acima, um parâmetro genérico real substituindo for Gpode ser qualquer outra classe disponível. Para restringir o conjunto de classes das quais parâmetros genéricos reais válidos podem ser escolhidos, uma restrição genérica pode ser especificada. Na declaração de class SORTED_LISTabaixo, a restrição genérica determina que qualquer parâmetro genérico real válido será uma classe que herda de class COMPARABLE. A restrição genérica garante que os elementos de a SORTED_LISTpodem de fato ser classificados.

classe 
SORTED_LIST [ G -> COMPARABLE ]       

Genéricos em Java

O suporte para os genéricos , ou "containers-of-type-T" foi adicionado à linguagem de programação Java em 2004 como parte do J2SE 5.0. Em Java, os genéricos são verificados apenas em tempo de compilação para correção de tipo. As informações de tipo genérico são então removidas por meio de um processo chamado type erasure , para manter a compatibilidade com implementações antigas da JVM , tornando-as indisponíveis em tempo de execução. [24] Por exemplo, a List<String>é convertido para o tipo bruto List. O compilador insere conversões de tipo para converter os elementos para o Stringtipo quando eles são recuperados da lista, reduzindo o desempenho em comparação a outras implementações, como modelos C++.

Genericidade em .NET [C#, VB.NET]

Os genéricos foram adicionados como parte do .NET Framework 2.0 em novembro de 2005, com base em um protótipo de pesquisa da Microsoft Research iniciado em 1999. [25] Embora semelhantes aos genéricos em Java, os genéricos .NET não aplicam apagamento de tipo , [26] : 208–209  , mas implementam genéricos como um mecanismo de primeira classe no tempo de execução usando reificação . Essa escolha de design fornece funcionalidade adicional, como permitir reflexão com preservação de tipos genéricos e aliviar alguns dos limites de apagamento (como não conseguir criar matrizes genéricas). [27] [28] Isso também significa que não há impacto no desempenho de conversões de tempo de execução e conversões de boxing normalmente caras . Quando tipos primitivos e de valor são usados ​​como argumentos genéricos, eles obtêm implementações especializadas, permitindo coleções e métodos genéricos eficientes. Como em C++ e Java, tipos genéricos aninhados como Dictionary<string, List<int>> são tipos válidos, no entanto, são desaconselhados para assinaturas de membros em regras de design de análise de código. [29]

O .NET permite seis variedades de restrições de tipo genérico usando a wherepalavra-chave, incluindo a restrição de tipos genéricos para serem tipos de valor, para serem classes, para terem construtores e para implementar interfaces. [30] Abaixo está um exemplo com uma restrição de interface:

usando Sistema ; 

classe Amostra 
{
    estático vazio Main ()  
    {
        int [] matriz = { 0 , 1 , 2 , 3 };        
        MakeAtLeast < int > ( array , 2 ); // Alterar array para { 2, 2, 2, 3 }  
        foreach ( int i em matriz )    
            Console . WriteLine ( i ); // Imprime resultados. 
        Console . ReadKey ( verdadeiro );
    }

    estático vazio MakeAtLeast < T > ( T [] lista , T mais baixo ) onde T : IComparable < T >         
    {
        para ( int i = 0 ; i < lista . Comprimento ; i ++ )        
            se ( lista [ i ]. CompareTo ( menor ) < 0 )   
                lista [ i ] = mais baixo ;  
    }
}

O MakeAtLeast()método permite operação em arrays, com elementos do tipo genérico T. A restrição de tipo do método indica que o método é aplicável a qualquer tipo Tque implemente a IComparable<T>interface genérica. Isso garante um erro de tempo de compilação , se o método for chamado se o tipo não suportar comparação. A interface fornece o método genérico CompareTo(T).

ArrayO método acima também poderia ser escrito sem tipos genéricos, simplesmente usando o tipo não genérico . No entanto, como os arrays são contravariantes , a conversão não seria segura para o tipo , e o compilador não seria capaz de encontrar certos erros possíveis que seriam capturados ao usar tipos genéricos. Além disso, o método precisaria acessar os itens do array como objects, e exigiria conversão para comparar dois elementos. (Para tipos de valor como intesse, é necessária uma conversão de boxing , embora isso possa ser contornado usando a Comparer<T>classe, como é feito nas classes de coleção padrão.)

Um comportamento notável de membros estáticos em uma classe .NET genérica é a instanciação de membros estáticos por tipo de tempo de execução (veja o exemplo abaixo).

    // Uma classe genérica 
public class GenTest < T > { // Uma variável estática - será criada para cada tipo na reflexão static CountedInstances OnePerType = new CountedInstances ();      
    
        
             

        // um membro de dados 
privado T _t ;          

        // construtor simples 
public GenTest ( T t ) { _t = t ; } }          
        
              
        
    

    // uma classe 
public class CountedInstances { //Variável estática - será incrementada uma vez por instância public static int Counter ;      
    
        
           

        //construtor simples 
public CountedInstances () { //aumenta o contador em um durante a instanciação do objeto CountedInstances . Counter ++ ; } }         
        
            
            
        
    

// ponto de entrada do código principal 
// no final da execução, CountedInstances.Counter = 2 
GenTest < int > g1 = new GenTest < int > ( 1 ); GenTest < int > g11 = new GenTest < int > ( 11 ); GenTest < int > g111 = new GenTest < int > ( 111 ); GenTest < double > g2 = new GenTest < double > ( 1.0 );    
    
    
    

Genericidade em Delphi

O dialeto Object Pascal do Delphi adquiriu genéricos na versão Delphi 2007, inicialmente apenas com o compilador .NET (agora descontinuado) antes de ser adicionado ao código nativo na versão Delphi 2009. A semântica e as capacidades dos genéricos do Delphi são amplamente modeladas naquelas que os genéricos tinham no .NET 2.0, embora a implementação seja, por necessidade, bem diferente. Aqui está uma tradução mais ou menos direta do primeiro exemplo de C# mostrado acima:

programa Amostra ; 

{$APPTYPE CONSOLE}

usa 
Genéricos . Padrões ; //para IComparer<>   

tipo 
TUtils = classe procedimento de classe MakeAtLeast < T > ( Arr : TArray < T >; const Mais Baixo : ​​T ; Comparador : IComparer < T > ) ; sobrecarga ; procedimento de classe MakeAtLeast < T > ( Arr : TArray < T >; const Mais Baixo : ​​T ) ; sobrecarga ; fim ;    
          
        
           
  

procedimento de classe TUtils . MakeAtLeast < T > ( Arr : TArray < T > ; const Mais baixo : ​​T ; Comparer : IComparer < T > ) ; var I : Inteiro ; começo se Comparer = nil então Comparer := TComparer < T > . Padrão ; para I := Baixo ( Arr ) para Alto ( Arr ) faça se Comparer . Compare ( Arr [ I ] , Mais baixo ) < 0 então Arr [ I ] := Mais baixo ; fim ;      
   

   

         
        
         
        


procedimento de classe TUtils . MakeAtLeast < T > ( Arr : TArray < T >; const Mais baixo : ​​T ) ; começar MakeAtLeast < T > ( Arr , Mais baixo , nulo ) ; fim ;      

    


var 
Ints : TArray < Inteiro >; Valor : Inteiro ; begin Ints := TArray < Inteiro >. Create ( 0 , 1 , 2 , 3 ) ; TUtils . MakeAtLeast < Inteiro > ( Ints , 2 ) ; for Valor em Ints do WriteLn ( Valor ) ; ReadLn ; end .   
   

       
   
      
    
  

Assim como em C#, métodos e tipos inteiros podem ter um ou mais parâmetros de tipo. No exemplo, TArray é um tipo genérico (definido pela linguagem) e MakeAtLeast um método genérico. As restrições disponíveis são muito semelhantes às restrições disponíveis em C#: qualquer tipo de valor, qualquer classe, uma classe ou interface específica e uma classe com um construtor sem parâmetros. Várias restrições agem como uma união aditiva.

Genericidade em Free Pascal

O Free Pascal implementou genéricos antes do Delphi, e com sintaxe e semântica diferentes. No entanto, desde a versão 2.6.0 do FPC, a sintaxe estilo Delphi está disponível ao usar o modo de linguagem {$mode Delphi}. Assim, o código Free Pascal suporta genéricos em qualquer estilo.

Exemplo de Delphi e Free Pascal:


// Unidade A do estilo Delphi ; 

{$ifdef fpc} 
{$mode delphi} {$endif}  


interface

tipo 
TGenericClass < T > = função de classe Foo ( const AValue : T ) : T ; fim ;    
        
  

implementação

função TGenericClass < T >. Foo ( const AValor : T ) : T ; resultado inicial : = AValue + AValue ; fim ;    

      


fim .


// Unidade B do estilo ObjFPC do Pascal grátis ; 

{$ifdef fpc} 
{$modo objfpc} {$endif}  


interface

tipo 
genérico TGenericClass < T > = classe função Foo ( const AValue : T ) : T ; fim ;     
        
  

implementação

função TGenericClass . Foo ( const AValor : T ) : T ; resultado inicial : = AValue + AValue ; fim ;    

      


fim .

// exemplo de uso, 
programa estilo Delphi TestGenDelphi ; 

{$ifdef fpc} 
{$mode delphi} {$endif}  


usa 
A , B ;  

var 
GC1 : A . TGenericClass < Inteiro >; GC2 : B . TGenericClass < String >; begin GC1 := A . TGenericClass < Inteiro >. Create ; GC2 := B . TGenericClass < String >. Create ; WriteLn ( GC1 . Foo ( 100 )) ; // 200 WriteLn ( GC2 . Foo ( 'olá' )) ; // oláolá GC1 . Livre ; GC2 . Livre ; end .   
   

    
    
   
   
  
  



// exemplo de uso, programa estilo ObjFPC TestGenDelphi ; 

{$ifdef fpc} 
{$modo objfpc} {$endif}  


usa 
A , B ;  


// necessário no tipo 
ObjFPC TAGenericClassInt = specialty A. TGenericClass < Integer >; TBGenericClassString = specialty B. TGenericClass < String >; var GC1 : TAGenericClassInt ; GC2 : TBGenericClassString ; begin GC1 : = TAGenericClassInt . Create ; GC2 := TBGenericClassString . Create ; WriteLn ( GC1 . Foo ( 100 )) ; // 200 WriteLn ( GC2 . Foo ( ' hello' )) ; // hellohello GC1 . Free ; GC2 . Free ; end .     
     

   
   

    
    
   
   
  
  

Linguagens funcionais

Genericidade em Haskell

O mecanismo de classe de tipo de Haskell suporta programação genérica. Seis das classes de tipo predefinidas em Haskell (incluindo Eq, os tipos que podem ser comparados para igualdade, e Show, os tipos cujos valores podem ser renderizados como strings) têm a propriedade especial de suportar instâncias derivadas. Isso significa que um programador que define um novo tipo pode declarar que esse tipo deve ser uma instância de uma dessas classes de tipo especiais, sem fornecer implementações dos métodos de classe, como geralmente é necessário ao declarar instâncias de classe. Todos os métodos necessários serão "derivados" – ou seja, construídos automaticamente – com base na estrutura do tipo. Por exemplo, a seguinte declaração de um tipo de árvores binárias declara que ele deve ser uma instância das classes Eqe Show:

dados BinTree a = Folha a | ( BinTree a ) a ( BinTree a ) derivando ( Eq , Mostrar )            
        

Isso resulta em uma função de igualdade ( ==) e uma função de representação de string ( show) sendo definidas automaticamente para qualquer tipo do formulário, BinTree Tdesde que Tele suporte essas operações.

O suporte para instâncias derivadas de Eqe Showtorna seus métodos ==e showgenéricos de uma forma qualitativamente diferente de funções parametricamente polimórficas: essas "funções" (mais precisamente, famílias de funções indexadas por tipo) podem ser aplicadas a valores de vários tipos e, embora se comportem de forma diferente para cada tipo de argumento, pouco trabalho é necessário para adicionar suporte para um novo tipo. Ralf Hinze (2004) mostrou que um efeito semelhante pode ser alcançado para classes de tipos definidas pelo usuário por certas técnicas de programação. Outros pesquisadores propuseram abordagens para este e outros tipos de genericidade no contexto de Haskell e extensões para Haskell (discutido abaixo).

Pólipo

PolyP foi a primeira extensão de linguagem de programação genérica para Haskell . Em PolyP, funções genéricas são chamadas de polytypic . A linguagem introduz uma construção especial na qual tais funções polytypic podem ser definidas via indução estrutural sobre a estrutura do functor padrão de um tipo de dado regular. Tipos de dados regulares em PolyP são um subconjunto de tipos de dados Haskell. Um tipo de dado regular t deve ser do tipo * → * , e se a for o argumento de tipo formal na definição, então todas as chamadas recursivas para t devem ter o formato ta . Essas restrições excluem tipos de dados de tipo superior e tipos de dados aninhados, onde as chamadas recursivas são de um formato diferente. A função flatten em PolyP é fornecida aqui como um exemplo:

   achatar :: Regular d => d a -> [ a ] ​​achatar = cata fl        
      

   politípico fl :: f a [ a ] ​​-> [ a ] ​​caso f de g + h -> qualquer 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               
Haskell Genérico

Generic Haskell é outra extensão para Haskell , desenvolvida na Universidade de Utrecht , na Holanda . As extensões que ele fornece são:

  • Valores indexados por tipo são definidos como um valor indexado sobre os vários construtores de tipo Haskell (unidade, tipos primitivos, somas, produtos e construtores de tipo definidos pelo usuário). Além disso, também podemos especificar o comportamento de valores indexados por tipo para um construtor específico usando constructor cases e reutilizar uma definição genérica em outra usando default cases .

O valor indexado por tipo resultante pode ser especializado para qualquer tipo.

  • Tipos indexados por tipo são tipos indexados sobre tipos, definidos ao fornecer um caso para ambos * e k → k' . Instâncias são obtidas ao aplicar o tipo indexado por tipo a um tipo.
  • Definições genéricas podem ser usadas aplicando-as a um tipo ou espécie. Isso é chamado de aplicação genérica . O resultado é um tipo ou valor, dependendo de qual tipo de definição genérica é aplicada.
  • A abstração genérica permite que definições genéricas sejam definidas pela abstração de um parâmetro de tipo (de um determinado tipo).
  • Tipos indexados por tipo são tipos que são indexados sobre os construtores de tipo. Eles podem ser usados ​​para dar tipos a valores genéricos mais envolvidos. Os tipos indexados por tipo resultantes podem ser especializados para qualquer tipo.

Como exemplo, a função de igualdade em Haskell Genérico: [31]

   tipo Eq {[ * ]} t1 t2 = t1 -> t2 -> Bool tipo Eq {[ k -> l ]} t1 t2 = forall u1 u2 . Eq {[ k ]} u1 u2 -> Eq {[ l ]} ( t1 u1 ) ( t2 u2 )            
                              

   equação { | t :: k | } :: Eq {[ k ]} t t eq { | Unidade | } _ _ = Verdadeiro eq { | :+: | } eqA eqB ( Inl a1 ) ( Inl a2 ) = eqA a1 a2 eq { | :+: | } eqA eqB ( Inr b1 ) ( Inr b2 ) = eqB b1 b2 eq { | :+: | } eqA eqB _ _ = Falso eq { | :*: | } eqA eqB ( a1 :*: b1 ) ( a2 :*: b2 ) = eqA a1 a2 && eqB b1 b2 eq { | Internacional | } = ( == ) eq { | Caráter | } = ( == ) eq { | Bool | } = ( == )            
          
                
                
            
                      
        
        
        

Limpar

Clean oferece programação genérica baseada em PolyP e o Haskell Genérico como suportado pelo GHC ≥ 6.0. Ele parametriza por tipo como aqueles, mas oferece sobrecarga.

Outras línguas

As linguagens da família ML suportam programação genérica por meio de polimorfismo paramétrico e módulos genéricos chamados functores. Tanto o Standard ML quanto o OCaml fornecem functores, que são semelhantes aos modelos de classe e aos pacotes genéricos do Ada. Abstrações sintáticas de esquema também têm uma conexão com a genericidade – elas são, na verdade, um superconjunto de modelos C++.

Um módulo Verilog pode receber um ou mais parâmetros, aos quais seus valores reais são atribuídos na instanciação do módulo. Um exemplo é uma matriz de registro genérica onde a largura da matriz é dada por meio de um parâmetro. Tal matriz, combinada com um vetor de fio genérico, pode fazer um buffer genérico ou módulo de memória com uma largura de bit arbitrária a partir de uma única implementação de módulo. [32]

VHDL , sendo derivado de Ada, também possui habilidades genéricas. [33]

C suporta "expressões genéricas de tipo" usando a _Genericpalavra-chave: [34]

#define cbrt(x) _Genérico((x), longo duplo: cbrtl, \ 
                              padrão: cbrt, \ 
                              float: cbrtf)(x)

Veja também

Referências

  1. ^ Lee, Kent D. (15 de dezembro de 2008). Linguagens de programação: uma abordagem de aprendizagem ativa. Springer Science & Business Media. pp. 9–10. ISBN  978-0-387-79422-8.
  2. ^ Milner, R.; Morris, L.; Newey, M. (1975). "Uma lógica para funções computáveis ​​com tipos reflexivos e polimórficos". Anais da conferência sobre prova e melhoria de programas .
  3. ^ Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Padrões de Design . Addison-Wesley. ISBN  0-201-63361-2.
  4. ^ Musser e Stepanov 1989.
  5. ^ Musser, David R.; Stepanov, Alexander A. Programação genérica (PDF) .
  6. ^ Alexander Stepanov; Paul McJones (19 de junho de 2009). Elementos de programação . Addison-Wesley Professional. ISBN 978-0-321-63537-2.
  7. ^ Musser, David R.; Stepanov, Alexander A. (1987). "Uma biblioteca de algoritmos genéricos em Ada". Anais da conferência internacional anual ACM SIGAda de 1987 sobre Ada - SIGAda '87 . pp. 216–225. CiteSeerX 10.1.1.588.7431 . doi :10.1145/317500.317529. ISBN  0897912438. S2CID  795406.
  8. ^ Alexander Stepanov e Meng Lee: The Standard Template Library. HP Laboratories Technical Report 95-11(R.1), 14 de novembro de 1995
  9. ^ Matthew H. Austern: Programação genérica e o STL: usando e estendendo a C++ Standard Template Library. Addison-Wesley Longman Publishing Co., Inc. Boston, MA, EUA 1998
  10. ^ Jeremy G. Siek, Lie-Quan Lee, Andrew Lumsdaine: The Boost Graph Library: Guia do usuário e manual de referência. Addison-Wesley 2001
  11. ^ Stepanov, Alexander. Breve História do STL (PDF) .
  12. ^ ab Stroustrup, Bjarne. Evoluindo uma linguagem no e para o mundo real: C++ 1991-2006 (PDF) . doi :10.1145/1238844.1238848. S2CID  7518369.
  13. ^ Lo Russo, Graziano. "Uma entrevista com A. Stepanov".
  14. ^ Backhouse, Roland; Jansson, Patrik; Jeuring, Johan; Meertens, Lambert (1999). Programação genérica – uma introdução (PDF) .
  15. ^ Lämmel, Ralf; Peyton Jones, Simon (janeiro de 2003). "Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming" (PDF) . Microsoft . Recuperado em 16 de outubro de 2016 .
  16. ^ Dos Reis, Gabriel; Ja ̈rvi, Jaakko (2005). "O que é Programação Genérica? (pré-impressão LCSD'05)" (PDF) . Arquivado do original (PDF) em 25 de dezembro de 2005.
  17. ^ R. Garcia; J. Ja ̈rvi; A. Lumsdaine; J. Siek; J. Willcock (2005). "Um estudo comparativo estendido de suporte de linguagem para programação genérica (pré-impressão)". CiteSeerX 10.1.1.110.122 . 
  18. ^ "Unidades Genéricas". www.adaic.org . Recuperado em 25 de abril de 2024 .
  19. ^ Stroustrup, Dos Reis (2003): Conceitos - Escolhas de design para verificação de argumentos de modelo
  20. ^ Stroustrup, Bjarne (1994). "15.5 Evitando a replicação de código". O design e a evolução do C++ . Reading, Massachusetts: Addison-Wesley. pp. 346–348. Bibcode : 1994dec..book.....S. ISBN 978-81-317-1608-3.
  21. ^ Bright, Walter. "Voldemort digita em D". Dr. Dobbs . Recuperado em 3 de junho de 2015 .
  22. ^ Construção de software orientada a objetos, Prentice Hall, 1988, e Construção de software orientada a objetos, segunda edição, Prentice Hall, 1997.
  23. ^ Eiffel: A Linguagem, Prentice Hall, 1991.
  24. ^ Bloch 2018, p. 126, §Item 28: Prefira listas a matrizes.
  25. ^ Histórico dos Genéricos .NET/C#: Algumas Fotos de Fevereiro de 1999
  26. ^ Albahari 2022.
  27. ^ C#: Ontem, Hoje e Amanhã: Uma Entrevista com Anders Hejlsberg
  28. ^ Genéricos em C#, Java e C++
  29. ^ Análise de código CA1006: Não aninhe tipos genéricos em assinaturas de membros
  30. ^ Restrições em parâmetros de tipo (Guia de programação C#)
  31. ^ Guia do usuário genérico do Haskell
  32. ^ Verilog por Exemplo, Seção O Resto para Referência . Blaine C. Readler, Full Arc Press, 2011. ISBN 978-0-9834973-0-1 
  33. ^ https://www.ics.uci.edu/~jmoorkan/vhdlref/generics.html Referência VHDL
  34. ^ WG14 N1516 Projeto de comitê — 4 de outubro de 2010

Fontes

  • Albahari, Joseph (2022). C# 10 in a Nutshell (Primeira ed.). O'Reilly. ISBN 978-1-098-12195-2.
  • Bloch, Joshua (2018). "Effective Java: Programming Language Guide" (terceira edição). Addison-Wesley. ISBN 978-0134685991.
  • Musser, DR ; Stepanov, AA (1989). "Programação genérica". Em P. Gianni (ed.). Computação simbólica e algébrica: simpósio internacional ISSAC 1988 . Notas de aula em ciência da computação. Vol. 358. pp. 13–25. doi :10.1007/3-540-51084-2_2. ISBN 978-3-540-51084-0.
  • Stroustrup, Bjarne (2007). Evoluindo uma linguagem no e para o mundo real: C++ 1991-2006 (PDF) . ACM HOPL 2007.
  • Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1994). Padrões de Projeto: Elementos de Software Reutilizável Orientado a Objetos . Addison-Wesley. Bibcode :1995dper.book.....G. ISBN 0-201-63361-2.

Leitura adicional

  • Gabriel Dos Reis e Jaakko Järvi, O que é programação genérica?, LCSD 2005 Arquivado em 28 de agosto de 2019 no Wayback Machine .
  • Gibbons, Jeremy (2007). Backhouse, R.; Jeuring, J. (eds.). Programação genérica de tipo de dados . Spring School on Datatype-Generic Programming 2006. Notas de aula em ciência da computação. Vol. 4719. Heidelberg: Springer. pp. 1–71. CiteSeerX  10.1.1.159.1228 .
  • Meyer, Bertrand (1986). "Genericidade versus herança". Anais de conferência sobre sistemas de programação orientados a objetos, linguagens e aplicações - OOPSLA '86 . pp. 391–405. doi :10.1145/28697.28738. ISBN 0897912047. S2CID  285030.
  • programação-genérica.org
  • Alexander A. Stepanov, Collected Papers of Alexander A. Stepanov (criador do STL )
C++, D
  • Walter Bright, Modelos revisitados.
  • David Vandevoorde, Nicolai M Josuttis, Modelos C++: o guia completo , 2003 Addison-Wesley. ISBN0-201-73484-2 
C#, .NET
  • Jason Clark, "Introdução de genéricos no Microsoft CLR", setembro de 2003, MSDN Magazine , Microsoft.
  • Jason Clark, "Mais sobre genéricos no Microsoft CLR", outubro de 2003, MSDN Magazine , Microsoft.
  • M. Aamir Maniar, Generics.Net. Uma biblioteca genérica de código aberto para C#.
Delphi, Objeto Pascal
  • Nick Hodges, "Delphi 2009 Reviewers Guide", outubro de 2008, Embarcadero Developer Network , Embarcadero.
  • Craig Stuntz, "Delphi 2009 Genéricos e Restrições de Tipo", outubro de 2008
  • Dr. Bob, "Delphi 2009 Genéricos"
  • Free Pascal : Guia de referência do Free Pascal Capítulo 8: Genéricos, Michaël Van Canneyt, 2007
  • Delphi para Win32: Genéricos com Delphi 2009 Win32, Sébastien DOERAENE, 2008
  • Delphi para .NET: Delphi Generics, Felix COLIBRI, 2008
Torre Eiffel
  • Documento de especificação ISO/ECMA da Eiffel
Haskell
  • Johan Jeuring, Sean Leather, José Pedro Magalhães e Alexey Rodriguez Yakushev. Bibliotecas para programação genérica em Haskell. Universidade de Utrecht.
  • Dæv Clarke, Johan Jeuring e Andres Löh, Guia do usuário do Haskell genérico
  • Ralf Hinze, "Genéricos para as massas", em Anais da Conferência Internacional ACM SIGPLAN sobre Programação Funcional (ICFP), 2004.
  • Simon Peyton Jones , editor, The Haskell 98 Language Report, revisado em 2002.
  • Ralf Lämmel e Simon Peyton Jones , "Scrap Your Boilerplate: A Practical Design Pattern for Generic Programming," em Anais do ACM SIGPLAN International Workshop on Types in Language Design and Implementation (TLDI'03), 2003. (Veja também o site dedicado a esta pesquisa)
  • Andres Löh, Exploring Generic Haskell, tese de doutorado, 2004 Universidade de Utrecht . ISBN 90-393-3765-9 
  • Haskell Genérico: uma linguagem para programação genérica
Java
  • Gilad Bracha, Genéricos na Linguagem de Programação Java, 2004.
  • Maurice Naftalin e Philip Wadler, Java Generics e coleções, 2006, O'Reilly Media, Inc. ISBN 0-596-52775-6 
  • Peter Sestoft, Java Precisely, Segunda Edição, 2005 MIT Press. ISBN 0-262-69325-9 
  • Programação genérica em Java, 2004 Sun Microsystems, Inc.
  • Angelika Langer, Perguntas frequentes sobre Java Generics
Retrieved from "https://en.wikipedia.org/w/index.php?title=Generic_programming&oldid=1241725683"