Despacho múltiplo

Despacho múltiplo ou multimétodos é um recurso de algumas linguagens de programação em que uma função ou método pode ser despachado dinamicamente com base no tipo de tempo de execução (dinâmico) ou, no caso mais geral, algum outro atributo de mais de um de seus argumentos . [1] Esta é uma generalização do polimorfismo de despacho único onde uma chamada de função ou método é despachada dinamicamente com base no tipo derivado do objeto no qual o método foi chamado. Despacho múltiplo roteia o despacho dinâmico para a função ou método de implementação usando as características combinadas de um ou mais argumentos.

Entendendo o despacho

Os desenvolvedores de software de computador geralmente organizam o código-fonte em blocos nomeados, também chamados de sub-rotinas , procedimentos, subprogramas, funções ou métodos. O código na função é executado chamando- o – executando um trecho de código que faz referência a seu nome . Isso transfere o controle temporariamente para a função chamada; quando a execução da função é concluída, o controle normalmente é transferido de volta para a instrução no chamador que segue a referência.

Os nomes das funções são geralmente selecionados de modo a serem descritivos do propósito da função. Às vezes é desejável dar o mesmo nome a várias funções, muitas vezes porque elas executam tarefas conceitualmente semelhantes, mas operam em diferentes tipos de dados de entrada. Nesses casos, a referência do nome no site da chamada da função não é suficiente para identificar o bloco de código a ser executado. Em vez disso, o número e o tipo dos argumentos para a chamada de função também são usados ​​para selecionar entre várias implementações de função.

Em linguagens de programação orientadas a objetos mais convencionais, ou seja, de despacho único , ao invocar um método ( enviar uma mensagem em Smalltalk , chamar uma função membro em C++ ), um de seus argumentos é tratado especialmente e usado para determinar qual dos (potencialmente muitas) classes de métodos com esse nome devem ser aplicadas. Em muitas línguas, o argumento especial é indicado sintaticamente; por exemplo, várias linguagens de programação colocam o argumento especial antes de um ponto ao fazer uma chamada de método: special.method(other, arguments, here), de modo que lion.sound()produziria um rugido, ao passo que sparrow.sound()produziria um chirp.

Por outro lado, em linguagens com despacho múltiplo, o método selecionado é simplesmente aquele cujos argumentos correspondem ao número e ao tipo da chamada da função. Não há nenhum argumento especial que possua a função/método realizado em uma determinada chamada.

O Common Lisp Object System (CLOS) é um exemplo antigo e bem conhecido de despacho múltiplo. Outro exemplo notável do uso de despacho múltiplo é a linguagem de programação Julia .

O despacho múltiplo deve ser diferenciado da sobrecarga de função , na qual informações de tipagem estática, como o tipo declarado ou inferido de um termo (ou tipo base em uma linguagem com subtipagem) são usadas para determinar qual das várias possibilidades será usada em um determinado local de chamada, e essa determinação é feita no tempo de compilação ou link (ou em algum outro momento antes do início da execução do programa) e, portanto, é invariável para uma determinada implantação ou execução do programa. Muitas linguagens, como C++, oferecem sobrecarga de função robusta, mas não oferecem despacho dinâmico múltiplo (C++ permite apenas despacho dinâmico único por meio do uso de funções virtuais).

Tipos de dados

Ao trabalhar com linguagens que podem discriminar tipos de dados em tempo de compilação , pode ocorrer a seleção entre as alternativas. O ato de criar tais funções alternativas para seleção de tempo de compilação é geralmente referido como sobrecarregar uma função.

Em linguagens de programação que adiam a identificação do tipo de dados até o tempo de execução (ou seja, ligação tardia ), a seleção entre funções alternativas deve ocorrer, com base nos tipos de argumentos de função determinados dinamicamente. As funções cujas implementações alternativas são selecionadas dessa maneira geralmente são chamadas de multimétodos .

Há algum custo de tempo de execução associado ao despacho dinâmico de chamadas de função. Em algumas linguagens, [ citação necessária ] a distinção entre sobrecarga e multimétodos pode ser confusa, com o compilador determinando se a seleção de tempo de compilação pode ser aplicada a uma determinada chamada de função ou se é necessário um despacho de tempo de execução mais lento.

Problemas

Há vários problemas conhecidos com o despacho dinâmico, tanto único quanto múltiplo. Embora muitos desses problemas sejam resolvidos para despacho único, que tem sido um recurso padrão em linguagens de programação orientadas a objetos há décadas, esses problemas se tornam mais complicados no caso de despacho múltiplo.

Expressividade e modularidade

Na linguagem de programação mais popular, o código-fonte é entregue e implantado em grânulos de funcionalidade que chamaremos aqui de pacotes ; a terminologia real para este conceito varia entre as línguas. Cada pacote pode conter várias definições de tipo, valor e função, os pacotes geralmente são compilados separadamente em linguagens com uma etapa de compilação e pode existir um relacionamento de dependência não cíclico. Um programa completo é um conjunto de pacotes, com um pacote principal que pode depender de vários outros pacotes, e todo o programa consiste no fechamento transitivo da relação de dependência.

O chamado problema de expressão refere-se à capacidade do código em um pacote dependente de estender comportamentos (funções ou tipos de dados) definidos em um pacote básico de dentro de um pacote de inclusão, sem modificar a fonte para o pacote básico. Linguagens OO tradicionais de despacho único tornam trivial adicionar novos tipos de dados, mas não novas funções; as linguagens funcionais tradicionais tendem a ter o efeito oposto, e o despacho múltiplo, se implementado corretamente, permite ambos. É desejável que uma implementação de despacho múltiplo tenha as seguintes propriedades:

  • É possível definir diferentes "casos" de um multimétodo de dentro de diferentes pacotes sem modificar a fonte de um pacote base.
  • A inclusão de outro pacote no programa não deve alterar o comportamento de uma determinada chamada multimétodo, quando a chamada não usa nenhum tipo de dados definido no pacote.
  • Por outro lado, se um tipo de dados for definido em um determinado pacote, e uma extensão multimétodo usando esse tipo também for definida no mesmo pacote, e um valor desse tipo for passado (através de uma referência de tipo base ou em uma função genérica) para outro pacote sem dependência desse pacote e, em seguida, o multimétodo é invocado com esse valor como argumento, o caso multimétodo definido no pacote que inclui o tipo deve ser empregado. Em outras palavras - dentro de um determinado programa, o mesmo multimétodo invocado com o mesmo conjunto de argumentos deve resolver para a mesma implementação, independentemente da localização do local da chamada e se uma determinada definição está ou não "em scope" ou "visible" no ponto da chamada do método.

Ambiguidade

É geralmente desejável que para qualquer invocação de um multimétodo, haja no máximo um "melhor" candidato entre os casos de implementação do multimétodo, e/ou que se não houver, que este seja resolvido de forma previsível e forma determinística, incluindo falha. O comportamento não determinístico é indesejável. Assumindo um conjunto de tipos com uma relação de subtipagem não circular, pode-se definir que uma implementação de um método múltiplo é "melhor" (mais específico) se todos os argumentos despachados dinamicamente no primeiro forem subtipos de todos os argumentos despachados dinamicamente especificados no segundo, e pelo menos um é um subtipo estrito. Com despacho único e na ausência de herança múltipla, esta condição é trivialmente satisfeita, mas com despacho múltiplo, é possível que dois ou mais candidatos satisfaçam uma determinada lista de argumentos reais, mas nenhum é mais específico do que o outro (um argumento dinâmico sendo o subtipo em um caso, outro sendo o subtipo no outro caso). Isso pode acontecer particularmente se dois pacotes diferentes, nenhum dependendo do outro, ambos estendem algum multimétodo com implementações relacionadas aos tipos de cada pacote e, em seguida, um terceiro pacote que inclui ambos (possivelmente indiretamente) invoca o multimétodo usando argumentos de ambos pacotes.

Possíveis resoluções incluem:

  • Tratar qualquer chamada ambígua como um erro. Isso pode ser detectado no tempo de compilação (ou antes da implantação), mas pode não ser detectado até o tempo de execução e produzir um erro de tempo de execução.
  • Ordenar os argumentos, por exemplo, o caso com o primeiro argumento mais específico é selecionado e os argumentos subsequentes não são considerados para resolução de ambigüidade, a menos que o primeiro argumento seja insuficiente para resolver o problema.
  • Construção de outras regras para resolver uma ambigüidade em uma direção ou outra. Às vezes, essas regras podem ser arbitrárias e surpreendentes. Nas regras para resolução de sobrecarga estática em C++, por exemplo, um tipo que corresponde exatamente é, compreensivelmente, considerado uma correspondência melhor do que um tipo que corresponde por meio de uma referência de tipo base ou um parâmetro genérico (modelo). No entanto, se as únicas correspondências possíveis forem por meio de um tipo base ou de um parâmetro genérico, o parâmetro genérico terá preferência sobre o tipo base, uma regra que às vezes produz um comportamento surpreendente.

Eficiência

Implementação eficiente de despacho único, inclusive em linguagens de programação que são compiladas separadamente para código de objeto e vinculadas a um vinculador de baixo nível (sem reconhecimento de linguagem), inclusive dinamicamente no momento de carregamento/início do programa ou mesmo sob a direção do aplicativo código, são bem conhecidos. A " tabela v" método desenvolvido em C++ e outras linguagens OO antigas (onde cada classe tem uma matriz de ponteiros de função correspondentes às funções virtuais dessa classe) é quase tão rápido quanto uma chamada de método estático, exigindo sobrecarga de O(1) e apenas uma pesquisa de memória adicional, mesmo no caso não otimizado. No entanto, o método vtable usa o nome da função e não o tipo de argumento como sua chave de pesquisa e não escala para o caso de despacho múltiplo. (Também depende do paradigma orientado a objeto dos métodos sendo recursos de classes, não entidades autônomas independentes de qualquer tipo de dados específico).

A implementação eficiente de despacho múltiplo continua sendo um problema de pesquisa em andamento.

Usar na prática

Para estimar a frequência com que o despacho múltiplo é usado na prática, Muschevici et al. [2] estudaram programas que usam despacho dinâmico. Eles analisaram nove aplicativos, a maioria compiladores, escritos em seis linguagens diferentes: Common Lisp Object System , Dylan , Cecil, MultiJava, Diesel e Nice. Seus resultados mostram que 13–32% das funções genéricas usam o tipo dinâmico de um argumento, enquanto 2,7–6,5% delas usam o tipo dinâmico de múltiplos argumentos. Os restantes 65-93% das funções genéricas têm um método concreto (overrider) e, portanto, não são considerados para usar os tipos dinâmicos de seus argumentos. Além disso, o estudo relata que 2 a 20% das funções genéricas tinham duas e 3 a 6% tinham três implementações de funções concretas. Os números diminuem rapidamente para funções com substituições mais concretas.

O despacho múltiplo é muito mais usado em Julia , onde o despacho múltiplo era um conceito de design central desde a origem da linguagem: coletando as mesmas estatísticas de Muschevici sobre o número médio de métodos por função genérica, descobriu-se que a biblioteca padrão Julia usa mais que o dobro da quantidade de sobrecarga do que nas outras linguagens analisadas por Muschevici, e mais de 10 vezes no caso de operadores binários . [3]

Os dados desses artigos estão resumidos na tabela a seguir, onde a taxa de despacho DRé o número médio de métodos por função genérica; a razão de escolha CRé a média do quadrado do número de métodos (para melhor medir a frequência de funções com um grande número de métodos); [2] [3] e o grau de especialização DoSé o número médio de argumentos especializados por tipo por método (ou seja, o número de argumentos que são despachados em):

Linguagem Número médio de métodos (DR) Taxa de escolha (CR) Grau de especialização (DoS)
Cecil [2] 2.33 63,30 1.06
Lisp comum ( CMU ) [2] 2.03 6.34 1.17
Lisp comum ( McCLIM ) [2] 2.32 15.43 1.17
Lisp comum ( Banco de aço ) [2] 2.37 26.57 1.11
Diesel [2] 2.07 31,65 0,71
Dylan (Gwydion) [2] 1,74 18.27 2.14
Dylan (OpenDylan) [2] 2.51 43,84 1.23
Júlia [3] 5.86 51,44 1.54
Julia (somente operadores) [3] 28.13 78.06 2.01
MultiJava [2] 1,50 8.92 1.02
legal [2] 1.36 3.46 0,33

Teoria

A teoria de múltiplas linguagens de despacho foi desenvolvida pela primeira vez por Castagna et al., definindo um modelo para funções sobrecarregadas com ligação tardia . [4] [5] Produziu a primeira formalização do problema de covariância e contravariância de linguagens orientadas a objetos [6] e uma solução para o problema de métodos binários. [7]

Exemplos

A distinção entre despacho múltiplo e único pode ficar mais clara com um exemplo. Imagine um jogo que tenha, entre seus objetos (visíveis pelo usuário), naves espaciais e asteroides. Quando dois objetos colidem, o programa pode precisar fazer coisas diferentes de acordo com o que acabou de atingir o quê.

Idiomas com despacho múltiplo integrado

C #

C# introduziu suporte para multimétodos dinâmicos na versão 4 [8] (abril de 2010) usando a palavra-chave 'dynamic'. O exemplo a seguir demonstra multimétodos. Como muitas outras linguagens de tipagem estática, o C# também dá suporte à sobrecarga de métodos estáticos. [9] A Microsoft espera que os desenvolvedores escolham digitação estática em vez de digitação dinâmica na maioria dos cenários. [10] A palavra-chave 'dynamic' oferece suporte à interoperabilidade com objetos COM e linguagens .NET de tipo dinâmico.

Csharp ColliderLibrary.svg

O exemplo abaixo usa recursos introduzidos em C# 9 e C# 10.

usando ColliderLibrary estático ;  

Consola . WriteLine ( Collide ( novo Asteroide ( 101 ), nova nave espacial ( 300 ))); Consola . WriteLine ( Collide ( novo Asteroide ( 10 ), nova nave espacial ( 10 ))); Consola . WriteLine ( Collide ( nova nave espacial ( 101 ), nova nave espacial ( 10 )));   
   
   

string Collide ( SpaceObject x , SpaceObject y ) => x . Tamanho > 100 && y . Tamanho > 100 ? "Grande boom!" : CollideWith ( x como dinâmico , y como dinâmico ); // Despacho dinâmico para o método CollideWith     
          
         
               

class ColliderLibrary { public static string CollideWith ( Asteroid x , Asteroid y ) => "a/a" ; public static string CollideWith ( Asteroid x , Spaceship y ) => "a/s" ; public static string CollideWith ( Nave espacial x , Asteroide y ) => "s/a" ; string estática pública 

            
            
            
       CollideWith ( Nave espacial x , Nave espacial y ) => "s/s" ; }     


registro abstrato SpaceObject ( int Size ); registro Asteróide ( int Tamanho ) : SpaceObject ( Tamanho ); registro Nave espacial ( int Tamanho ) : SpaceObject ( Tamanho );   
    
    

Saída:

Grande boom! 
a/s 
s/s

Groovy

Groovy é uma linguagem JVM interutilizável/compatível com Java de propósito geral , que, ao contrário de Java, usa ligação tardia/despacho múltiplo. [11]

/* 
    Implementação Groovy do exemplo C# acima 
    Late binding funciona da mesma forma ao usar métodos não estáticos ou compilar classe/métodos estaticamente 
    (anotação @CompileStatic) 
*/ 
class Program { static void main ( String [] args ) { println Collider . colide ( novo Asteroide ( 101 ), nova nave espacial ( 300 )) println Collider . colidir ( novo Asteróide ( 10  
        
            
          ), nova nave espacial ( 10 )) println Collider . colide ( nova nave espacial ( 101 ), nova nave espacial ( 10 )) } }  
            
    


class Collider { static String collide ( SpaceObject x , SpaceObject y ) { ( x . size > 100 && y . size > 100 ) ? "big-boom" : collideWith ( x , y ) // Despacho dinâmico para o método colideWith }  
          
                     
    

    private static String collideWith ( Asteroid x , Asteroid y ) { "a/a" } private static String collideWith ( Asteroid x , Spaceship y ) { "a/s" } private static String collideWith ( Spaceship x , Asteroid y ) { "s /a" } private static String colideWith ( Nave espacial x         
             
             
        , Nave espacial y ) { "s/s" } }    


class SpaceObject { tamanho int SpaceObject ( tamanho int ) { this . tamanho = tamanho } }  
     
          


Classe @InheritConstructors Asteroid estende SpaceObject {} Classe @InheritConstructors Nave espacial estende SpaceObject {}     
     

Common Lisp

Em uma linguagem com despacho múltiplo, como Common Lisp , pode parecer mais com isso (exemplo de Common Lisp mostrado):

( defmethod colidir-com (( x asteróide ) ( y asteróide )) ;; lidar com asteróide atingindo asteróide ) ( defmethod colidir-com (( x asteróide ) ( y nave espacial )) ;; lidar com asteróide atingindo a espaçonave ) ( defmethod colidir-com com (( x nave espacial ) ( y asteróide )) ;; lidar com nave espacial atingindo asteróide ) ( defmethod colide-com (( x     
  
  
     
  
  
     
  
  
   nave espacial ) ( y nave espacial )) ;; lidar com nave espacial atingindo nave espacial )  
  
  

e da mesma forma para os outros métodos. Testes explícitos e "conversão dinâmica" não são usados.

Na presença de despacho múltiplo, a ideia tradicional de métodos como sendo definidos em classes e contidos em objetos torna-se menos atraente - cada método de colisão acima é anexado a duas classes diferentes, não a uma. Portanto, a sintaxe especial para invocação de método geralmente desaparece, de modo que a invocação de método se parece exatamente com a invocação de função comum e os métodos são agrupados não em classes, mas em funções genéricas .

Júlia

Julia incorporou despacho múltiplo e é fundamental para o design da linguagem. [3] A versão Julia do exemplo acima pode ser semelhante a:

tipo abstrato SpaceObject fim   

struct  Asteroid <: tamanho do SpaceObject :: Int end struct Nave espacial <: tamanho do SpaceObject :: Int end  
        

   
                      
              

collide_with ( :: Asteroid , :: Spaceship ) = "a/s" collide_with ( :: Spaceship , :: Asteroid ) = "s/a" collide_with ( :: Spaceship , :: Spaceship ) = "s/s" collide_with ( :: Asteróide , :: Asteróide ) = "a/a"   
   
   
   

colidir ( x :: SpaceObject , y :: SpaceObject ) = ( x . size > 100 && y . size > 100 ) ? "Grande boom!" : colide_com ( x , y )              

Saída:

julia> colidem ( Asteróide ( 101 ), Nave espacial ( 300 )) "Big boom!"  


julia> colidir ( Asteróide ( 10 ), Nave espacial ( 10 )) "a/s"  


julia> colidir ( Nave espacial ( 101 ), Nave espacial ( 10 )) "s/s"  

Raku

Raku , como Perl, usa ideias comprovadas de outras linguagens, e os sistemas de tipo demonstraram oferecer vantagens atraentes na análise de código do lado do compilador e semântica poderosa do lado do usuário por meio de despacho múltiplo.

Tem multimétodos e multisubs. Como a maioria dos operadores são sub-rotinas, ele também possui vários operadores despachados.

Juntamente com as restrições de tipo usuais, também possui restrições de where que permitem fazer sub-rotinas muito especializadas.

subconjunto  Massa  de  Real  onde  0 ^..^ Inf ; 
função  Stellar-Object {
     has  Mass  $.mass  é  obrigatório ;
    nome do método  () retorna Str {...}; 
}
class  Asteroid  does  Stellar-Object {
     nome do método  () { 'um asteroide' }
}
class  Spaceship  does  Stellar-Object {
     has  Str  $.name = 'some unnamed spaceship' ;
}
meu  Str  @destroyed = < obliterado  destruído  mutilado >;
meu  Str  @danificado = «  danificado  'colidiu com'  'foi danificado por'  »;

# Adicionamos vários candidatos aos operadores de comparação numérica porque os estamos comparando numericamente, 
# mas não faz sentido ter os objetos coagidos para um tipo numérico. 
# ( Se eles coagissem, não precisaríamos necessariamente adicionar esses operadores. ) 
# Também poderíamos ter definido operadores inteiramente novos dessa mesma maneira. 
multi  sub  infixo: « <=> » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . massa <=> $b . mass }
 multi  sub  infix: « < » ( Stellar-Object:D  $a , Stellar-Object:D $b ){ $a . massa < $ b . mass }
 multi  sub  infix: « > » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . massa > $b . mass }
 multi  sub  infixo: « == » ( Stellar-Object:D  $a , Stellar-Object:D  $b ) { $a . massa == $b . massa }

# Defina um novo despachante múltiplo e adicione algumas restrições de tipo aos parâmetros. 
# Se não definissemos, teríamos obtido um genérico que não tivesse restrições. 
proto  sub  colidir ( Stellar-Object:D $, Stellar-Object:D $ ) {*}

# Não há necessidade de repetir os tipos aqui, pois são os mesmos do protótipo. 
# A restrição 'where' tecnicamente só se aplica a $b e não a toda a assinatura. 
# Observe que a restrição 'where' usa o candidato a operador `<` que adicionamos anteriormente. 
multi  sub  collide ( $a , $b  onde  $a < $b ) {
     diga  "$a.name() foi @destruído.pick() por $b.name()" ;
}
multi  sub  collide ( $a , $b  where  $a > $b ) {
     # reenvia para o candidato anterior com os 
    mesmos argumentos trocados por  $b , $a ;
}

# Isso tem que ser depois dos dois primeiros porque os outros 
# têm restrições 'where', que são verificadas na 
# ordem em que os subs foram escritos. (Este sempre corresponderia.) 
multi  sub  collide ( $a , $b ) {
     # randomiza a ordem 
    my ( $n1 , $n2 ) = ( $a . name , $b . name ). escolher (*);
    diga  "$n1 @danificado.pick() $n2" ;
}

# Os dois candidatos a seguir podem estar em qualquer lugar após o proto, 
# porque eles têm tipos mais especializados do que os três anteriores.

# Se os navios tiverem massa desigual, um dos dois primeiros candidatos será chamado. 
multi  sub  colidir ( Nave espacial  $a , Nave espacial  $b  onde  $a == $b ){
     my ( $n1 , $n2 ) = ( $a . name , $b . name ). escolher (*);
    diga  "$n1 colidiu com $n2, e ambos os navios foram " ,
    ( @destroyed . pick , 'esquerda danificada' ). escolher ;
}

# Você pode descompactar os atributos em variáveis ​​dentro da assinatura. 
# Você pode até ter uma restrição neles `(:mass($a) where 10)`. 
multi  sub  collide ( Asteroid $ (: mass ( $a )), Asteroid $ (: mass ( $b )) ){
     diga  "dois asteroides colidiram e se combinaram em um asteroide maior de massa { $a + $b }" ;
}

minha  nave espacial  $Enterprise .= new (: mass ( 1 ),: name ( 'The Enterprise' ));
colidir  Asteróide . novo (: massa ( .1 )), $Enterprise ;
colidir  $Enterprise , Nave espacial . novo (: massa ( .1 ));
colidir  $Enterprise , Asteróide . novo (: massa ( 1 ));
colidir  $Enterprise, nave espacial . novo (: massa ( 1 ));
colidir  Asteróide . novo (: massa ( 10 )), Asteróide . novo (: massa ( 5 ));

Estendendo idiomas com bibliotecas de múltiplos despachos

JavaScript

Em idiomas que não suportam despacho múltiplo na definição de idioma ou nível sintático, muitas vezes é possível adicionar despacho múltiplo usando uma extensão de biblioteca . JavaScript e TypeScript não suportam multimétodos no nível da sintaxe, mas é possível adicionar múltiplos dispatch por meio de uma biblioteca. Por exemplo, o pacote multimétodo [12] fornece uma implementação de múltiplas funções genéricas de despacho.

Versão tipada dinamicamente em JavaScript:

import { multi , method } de '@arrows/multimethod'      

classe Asteróide {} classe Nave espacial {}  
  

const collideWith = multi ( method ([ Asteroid , Asteroid ], ( x , y ) => { // lidar com asteroide atingindo asteroid }), method ([ Asteroid , Spaceship ], ( x , y ) => { // lidar com nave espacial atingindo asteróide }), método ([ Nave espacial , Asteroide ], ( x , y ) => {   
       
    
  
       
    
  
       
    // lida com nave espacial atingindo asteroide 
}), method ([ Nave espacial , Nave espacial ], ( x , y ) => { // lida com nave espacial atingindo nave espacial }), )  
       
    
  

Versão tipada estaticamente no TypeScript:

import { multi , method , Multi } de '@arrows/multimethod'       

classe Asteróide {} classe Nave espacial {}  
  

type CollideWith = Multi & { ( x : Asteroid , y : Asteroid ) : void ( x : Asteroid , y : Spaceship ) : void ( x : Spaceship , y : Asteroid ) : void ( x : Spaceship , y : Spaceship ) : void }     
      
      
      
      


const collideWith : CollideWith = multi ( method ([ Asteroid , Asteroid ], ( x , y ) => { // lidar com asteroid hit asteroid }), method ([ Asteroid , Spaceship ], ( x , y ) => { / / lidar com nave espacial atingindo asteróide }), método ([ Nave espacial , Asteroide ], ( x , y )    
       
    
  
       
    
  
      => { // lidar com nave espacial atingindo asteroide }), método ([ Nave espacial , Nave espacial ], ( x , y ) => { // lidar com nave espacial atingindo nave espacial }), ) 
    
  
       
    
  

Pitão

O envio múltiplo pode ser adicionado ao Python usando uma extensão de biblioteca . Por exemplo, usando o módulo multimethod.py [13] e também com o módulo multimethods.py [14] que fornece multimétodos no estilo CLOS para Python sem alterar a sintaxe subjacente ou as palavras-chave da linguagem.

from  multimethods  import  Dispatch 
from  game_objects  import  Asteroid ,  Spaceship 
from  game_behaviors  import  as_func ,  ss_func ,  sa_func

colidir  =  Despacho () 
colidir . add_rule (( Asteróide ,  Nave espacial ),  as_func ) 
colidem . add_rule (( Nave espacial ,  Nave espacial ),  ss_func ) 
colidem . add_rule (( Nave espacial ,  Asteróide ),  sa_func )


def  aa_func ( a ,  b ): 
"""Comportamento quando asteroide atinge asteroide.""" # ...definir novo comportamento...    
    


colidir . add_rule (( Asteróide ,  Asteróide ),  aa_func )
# ...mais tarde... 
colidem ( coisa1 ,  coisa2 )

Funcionalmente, isso é muito semelhante ao exemplo CLOS, mas a sintaxe é Python convencional.

Usando decoradores Python 2.4 , Guido van Rossum produziu um exemplo de implementação de multimétodos [15] com uma sintaxe simplificada:

@multimethod ( Asteroid ,  Asteroid ) 
def  colide ( a ,  b ): 
"""Comportamento quando asteroide atinge outro asteroide.""" # ...defina um novo comportamento... @multimethod ( Asteroid , Spaceship ) def colide ( a , b ): """Comportamento quando asteróide atinge uma nave espacial.""" # ...defina um novo comportamento... # ... defina outras regras multimétodos ...    
    
 
  
    
    

e então define o decorador multimétodo.

O pacote PEAK-Rules fornece despacho múltiplo com uma sintaxe semelhante ao exemplo acima. [16] Posteriormente, foi substituído por PyProtocols. [17]

A biblioteca Reg também suporta despacho múltiplo e predicado. [18]

Com a introdução de dicas de tipo , o envio múltiplo é possível com uma sintaxe ainda mais simples. Por exemplo, usando plum-dispatch,

de  despacho de importação de ameixa  


@dispatch 
def  colide ( a :  Asteroid ,  b :  Asteroid ): 
"""Comportamento quando asteroide atinge outro asteroide.""" # ...define novo comportamento...    
    
    
@dispatch 
def  colidir ( a :  Asteróide ,  b :  Nave espacial ): 
"""Comportamento quando um asteróide atinge uma nave espacial.""" # ...defina um novo comportamento...    
    
    
# ...definir outras regras...

Emulando despacho múltiplo

C

C não possui despacho dinâmico, portanto deve ser implementado manualmente de alguma forma. Freqüentemente, uma enumeração é usada para identificar o subtipo de um objeto. O despacho dinâmico pode ser feito procurando esse valor em uma tabela de ramificação de ponteiro de função . Aqui está um exemplo simples em C:

typedef void ( * CollisionCase )( void );  

void colision_AA ( void ) { /* lidar com a colisão Asteroid-Asteroid */ }; void colision_AS ( void ) { /* lida com a colisão de asteróide-nave espacial */ }; void colision_SA ( void ) { /* lida com a colisão entre espaçonave e asteroide */ }; void colision_SS ( void ) { /* lidar com a colisão entre espaçonaves*/ };    
    
    
    

typedef enum { THING_ASTEROID = 0 , THING_SPACESHIP , THING_COUNT /* não é um tipo de coisa em si, mas usado para encontrar o número de coisas */ } Coisa ;  
      
    
     
 

Casos de colisão Casos de colisão [ THING_COUNT ][ THING_COUNT ] = { { & colisão_AA , & colisão_AS }, { & colisão_SA , & colisão_SS } };   
     
     


void colidir ( Coisa a , Coisa b ) { ( * colisionCases [ a ][ b ])(); }     
    


int main ( void ) { colidir ( THING_SPACESHIP , THING_ASTEROID ); }  
     

Com a biblioteca C Object System, [19] C suporta despacho dinâmico semelhante ao CLOS. É totalmente extensível e não requer nenhum tratamento manual dos métodos. As mensagens dinâmicas (métodos) são despachadas pelo despachante do COS, que é mais rápido que o Objective-C. Aqui está um exemplo em COS:

#include <stdio.h> #include <cos/Object.h> #include <cos/gen/object.h> 
 
 

// Aulas

defclass ( Asteroid ) // membros de dados endclass 



defclass ( Nave espacial ) // membros de dados endclass 



// genéricos

defgeneric ( _Bool , colide_com , _1 , _2 );    

// multimétodos

defmethod ( _Bool , colide_with , Asteroid , Asteroid ) // lida com asteroides atingindo asteroides endmethod    
 


defmethod ( _Bool , colide_with , Asteroid , Spaceship ) // lida com o asteroide atingindo a nave endmethod    
 


defmethod ( _Bool , colide_with , Spaceship , Asteroid ) // lida com nave espacial atingindo asteroide endmethod    
 


defmethod ( _Bool , colide_with , Nave espacial , Nave espacial ) // lida com nave espacial atingindo nave espacial endmethod    
 


// exemplo de uso

int main ( void ) { OBJ a = gnew ( Asteróide ); OBJ s = gnew ( Nave espacial ); 

     
     

  printf ( "<a,a> = %d \n " , colide_com ( a , a )); printf ( "<a,s> = %d \n " , colide_com ( a , s )); printf ( "<s,a> = %d \n " , colide_com ( s , a )); printf ( "<s,s> = %d \n " , colide_com ( s , s ));  
    
    
    

  grelease ( a ); 
grelease ( s ); }  

C++

A partir de 2021 , o C++ suporta nativamente apenas um único envio, embora a adição de vários métodos (despacho múltiplo) tenha sido proposta por Bjarne Stroustrup (e colaboradores) em 2007. [20] Os métodos para contornar esse limite são análogos: use o padrão visitante , elenco dinâmico ou uma biblioteca:

 // Exemplo usando comparação de tipo de tempo de execução via dynamic_cast

 struct Coisa { virtual void colideCom ( Coisa e outro ) = 0 ; };  
          
 

 struct Asteroid : Thing { void collideWith ( Thing & other ) { // dynamic_cast para um tipo de ponteiro retorna NULL se a conversão falhar // (dynamic_cast para um tipo de referência lançaria uma exceção em caso de falha) if ( auto asteroid = dynamic_cast < Asteroid * > ( & other )) { // lida com a colisão Asteroid-Asteroid } else if ( auto spaceship = dynamic_cast < Spaceship *>    
        
         
         
              
             
               ( & other )) { // lida com colisão de asteróide-nave espacial } else { // manipulação de colisão padrão aqui } } }; 
             
           
             
         
     
 

 struct Nave espacial : Coisa { void colideWith ( Coisa & outro ) { if ( auto asteroid = dynamic_cast < Asteroid *> ( & other )) { // lida com a colisão entre nave espacial } else if ( auto spaceship = dynamic_cast < Nave espacial *> ( & other )) { // lida com a colisão entre espaçonaves } else    
        
              
             
                
             
           { 
// tratamento de colisão padrão aqui } } };             
         
     
 

ou tabela de pesquisa de ponteiro para método:

#include <cstdint> #include <typeinfo> #include <unordered_map> 
 
 

classe Coisa { protegida : Coisa ( std :: uint32_t cid ) : tid ( cid ) {} const std :: uint32_t tid ; // tipo de id  
  
        
       

    typedef void ( Coisa ::* CollisionHandler ) ( Coisa e outro ); typedef std :: unordered_map < std :: uint64_t , CollisionHandler > CollisionHandlerMap ;   
       

    static void addHandler ( std :: uint32_t id1 , std :: uint32_t id2 , manipulador CollisionHandler ) { colisionCases . inserir ( CollisionHandlerMap :: value_type ( chave ( id1 , id2 ), manipulador )); } static std :: chave uint64_t ( std :: uint32_t id1 , std :: uint32_t        
          
    
         id2 ) { return std :: uint64_t ( id1 ) << 32 | id2 ; } 
             
    

    CollisionHandlerMap estáticos ColisionCases ;  

  public : 
void colideWith ( coisa e outro ) { auto handler = colisionCases . encontrar ( chave ( tid , outro . tid )); if ( manipulador != colisãoCases . end ()) { ( this ->* manipulador -> segundo )( outro ); // chamada de ponteiro para método } else { // tratamento de colisão padrão }       
            
            
             
          
            
        
    } 
};

class Asteroid : public Thing { void asteroid_collision ( Thing & other ) { /*lidar com colisão Asteroid-Asteroid*/ } void spaceship_collision ( Thing & other ) { /*lidar com colisão Asteroid-Spaceship*/ }    
           
         

  public : 
Asteroid () : Thing ( cid ) {} static void initCases (); static const std :: uint32_t cid ; };      
      
       


class Spaceship : public Thing { void asteroid_collision ( Thing & other ) { /*lidar com a colisão entre espaçonaves e asteroides*/ } void spaceship_collision ( Thing & other ) { /*manipular a colisão entre naves espaciais*/ }    
          
         

  public : 
Nave espacial () : Coisa ( cid ) {} static void initCases (); static const std :: uint32_t cid ; // id da classe };      
      
        


Coisa :: CollisionHandlerMap Coisa :: colisionCases ; const std :: uint32_t Asteróide :: cid = typeid ( Asteróide ). hash_code (); const std :: uint32_t Nave espacial :: cid = typeid ( Nave espacial ). hash_code (); 
    
    

void Asteroid::initCases () { addHandler ( cid , cid , CollisionHandler ( & Asteroid :: asteroid_collision )); addHandler ( cid , Nave :: cid , CollisionHandler ( & Asteroid :: spaceship_collision )); }  
      
      


void Espaçonave::initCases () { addHandler ( cid , Asteróide :: cid , CollisionHandler ( & Espaçonave :: asteroid_collision )); addHandler ( cid , cid , CollisionHandler ( & Spaceship :: spaceship_collision )); }  
      
      


int main () { Asteróide :: initCases (); Nave :: initCases ();  
    
    

    Asteroide a1 , a2 ; Nave espacial s1 , s2 ;   
      

    a1 . colideCom ( a2 ); 
a1 . colideCom ( s1 );    

    s1 . colideCom ( s2 ); 
s1 . colideCom ( a1 ); }    

A biblioteca YOMM2 [21] fornece uma implementação rápida e ortogonal de multimétodos abertos.

A sintaxe para declaração de métodos abertos é inspirada em uma proposta de implementação nativa de C++. A biblioteca requer que o usuário registre todas as classes utilizadas como argumentos virtuais (e suas subclasses), mas não requer modificações no código existente. Os métodos são implementados como funções C++ inline comuns; eles podem ser sobrecarregados e podem ser passados ​​por ponteiro. Não há limite para o número de argumentos virtuais e eles podem ser misturados arbitrariamente com argumentos não virtuais.

A biblioteca usa uma combinação de técnicas (tabelas de despacho compactadas, tabela de hash de inteiro livre de colisão) para implementar chamadas de método em tempo constante, mitigando o uso de memória. Despachar uma chamada para um método aberto com um único argumento virtual leva apenas 15 a 30% mais tempo do que chamar uma função de membro virtual comum, quando um compilador de otimização moderno é usado.

O exemplo Asteroids pode ser implementado da seguinte forma:

#include <yorel/yomm2/keywords.hpp> #include <memory> 
 

classe Coisa { public : virtual ~ Coisa () {} };  
  
      


classe Asteróide : Public Thing { };     


class Nave espacial : coisa pública { };     


register_classes ( Coisa , Nave Espacial , Asteroide );  

declare_method ( void , colideWith , ( virtual_ < Thing &> , virtual_ < Thing &> ));   

define_method ( void , collideWith , ( Thing & left , Thing & right )) { // tratamento de colisão padrão }      
    


define_method ( void , colideWith , ( Asteroid & left , Asteroid & right )) { // lida com a colisão Asteroid-Asteroid }      
    


define_method ( void , collideWith , ( Asteroid & left , Spaceship & right )) { // lida com a colisão Asteroid-Spaceship }      
    


define_method ( void , colideWith , ( Nave espacial e esquerda , Asteroide e direita )) { // lida com a colisão entre nave espacial e asteroide }      
    


define_method ( void , colideWith , ( Nave espacial & esquerda , Nave espacial & direita )) { // lida com a colisão entre espaçonaves }      
    


int main () { yorel :: yomm2 :: update_methods ();  
    

    std :: unique_ptr < Coisa > a1 ( std :: make_unique < Asteroid > ()), a2 ( std :: make_unique < Asteroid > ()); std :: unique_ptr < Coisa > s1 ( std :: make_unique < Nave espacial > ()), s2 ( std :: make_unique < Nave espacial > ()); // nota: tipos parcialmente apagados 
        
     
        
    

    colideCom ( * a1 , * a2 ); // Colisão asteróide-asteróide colideCom ( * a1 , * s1 ); // Colisão de espaçonave-asteróide colideCom ( * s1 , * a1 ); // Colisão de espaçonave-asteroide colideCom ( * s1 , * s2 ); // Colisão entre espaçonaves  
      
      
      

    retorna 0 ; } 

Stroustrup menciona em The Design and Evolution of C++ que gostou do conceito de multimétodos e considerou implementá-lo em C++, mas afirma ter sido incapaz de encontrar uma implementação de exemplo eficiente (comparável a funções virtuais) e resolver alguns possíveis problemas de ambiguidade de tipo. Ele então afirma que, embora ainda seja bom ter o recurso, ele pode ser implementado aproximadamente usando despacho duplo ou uma tabela de pesquisa baseada em tipo, conforme descrito no exemplo C/C++ acima, portanto, é um recurso de baixa prioridade para futuras revisões de linguagem. [22]

D

A partir de 2021 , assim como muitas outras linguagens de programação orientadas a objetos, D suporta nativamente apenas um único envio. Entretanto, é possível emular multimétodos abertos como uma função de biblioteca em D. A biblioteca openmethods [23] é um exemplo.

// Declaração 
Matrix plus ( virtual ! Matrix , virtual ! Matrix );  

// A substituição para dois objetos DenseMatrix 
@method 
Matrix _plus ( DenseMatrix a , DenseMatrix b ) { const int nr = a . linhas ; const int nc = a . colunas ; afirmar ( a . nr == b . nr ); afirmar ( a . nc == b . nc ); resultado automático = novo    

      
      
    
    
      DenseMatrix ; 
resultado . nr = nr ; resultado . nc = nc ; resultado . elems . comprimento = a . elems . comprimento ; resultado . elementos [] = a . elementos [] + b . elementos []; resultado de retorno ; }    
    
    
      
   


// A substituição para dois objetos DiagonalMatrix 
@method 
Matrix _plus ( DiagonalMatrix a , DiagonalMatrix b ) { assert ( a . rows == b . rows ); duplo [] soma ; soma . comprimento = a . elems . comprimento ; soma [] = a . elementos [] + b . elementos []; devolver novo    

    
   
    
      
    Matriz Diagonal ( soma ); 
}

Java

Em uma linguagem com apenas despacho único, como Java , despacho múltiplo pode ser emulado com vários níveis de despacho único:

Classe UML Java single dispatch.svg

interface  Collideable { void colideWith ( final Collideable outro ); 
       

    /* Esses métodos precisariam de nomes diferentes em uma linguagem sem sobrecarga de método. */ 
void colideWith ( final Asteroid asteroid ); void colideWith ( final Nave espacial ); }       
       


class  Asteroid implements Collideable { public void collideWith ( final Collideable other ) { // Chama collideWith no outro objeto. outro . colideCom ( este ); }   
         
        
        
   

    public void colideWith ( final Asteroid asteroid ) { // Lida com a colisão Asteroid-Asteroid. }     
        
    

    public void collideWith ( final Spaceship spaceship ) { // Lida com colisão de asteróide-espaçonave. } }     
        
    


class  Spaceship implements Collideable { public void collideWith ( final Collideable other ) { // Chama collideWith no outro objeto. outro . colideCom ( este ); }   
         
        
        
    

    public void collideWith ( final Asteroid asteroid ) { // Lida com a colisão entre espaçonave e asteroide. }     
        
    

    public void collideWith ( final Spaceship spaceship ) { // Lida com a colisão entre espaçonaves. } }     
        
    

As verificações de tempo de execução instanceofem um ou ambos os níveis também podem ser usadas.

Suporte em linguagens de programação

paradigma primário

Compatível com multimétodos gerais

Através de extensões

  • Qualquer linguagem .NET (através da biblioteca MultiMethods.NET)
  • C (através da biblioteca C Object System)
  • C# (através da biblioteca multimethod-sharp)
  • C++ (através da biblioteca yomm2, multimétodos e omm)
  • D (através da biblioteca openmethods)
  • Fator (através do vocabulário multimétodos padrão)
  • Java (usando a extensão MultiJava)
  • JavaScript (através do pacote @arrows/multimethod)
  • Perl (através do módulo Class::Multimethods)
  • Python (via PEAK-Rules, RuleDispatch, gnosis.magic.multimethods, PyMultimethods, multipledispatch ou plum-dispatch)
  • Raquete (via multimethod-lib)
  • Ruby (através da biblioteca The Multiple Dispatch Library and Multimethod Package and Vlx-Multimethods Package)
  • Esquema (por exemplo, TinyCLOS)
  • TypeScript (através do pacote @arrows/multimethod)

Veja também

Referências

  1. ^ Ranka, Sanjay; Banerjee, Arunava; Biswas, Kanad Kishore; Dua, Sumeet; Mishra, Prabhat; Moona, Rajat (2010-07-26). Computação Contemporânea: Segunda Conferência Internacional, IC3 2010, Noida, Índia, 9 a 11 de agosto de 2010. Anais. Springer. ISBN 9783642148248.
  2. ^ abcdefghijk Muschevici, Radu; Potanin, Alex; Tempero, Ewan; Nobre, James (2008). Despacho múltiplo na prática. Proceedings of the 23rd ACM SIGPLAN Conference on Object-oriented Programming Systems Languages ​​and Applications . OOPSLA '08. Nashville, TN, EUA: ACM. pp. 563–582. doi : 10.1145/1449764.1449808. ISBN 9781605582153. S2CID  7605233.
  3. ^ abcde Bezanson, Jeff; Edelman, Alan; Karpinski, Stefan; Shah, Viral B. (7 de fevereiro de 2017). "Julia: Uma nova abordagem para computação numérica". Revisão SIAM . 59 (1): 65–98. arXiv : 1411.1607 . doi : 10.1137/141000671. S2CID  13026838.
  4. ^ Castagna, Giuseppe; Ghelli, Giorgio & Longo, Giuseppe (1995). "Um cálculo para funções sobrecarregadas com subtipagem". Informação e Computação . 117 (1): 115–135. doi : 10.1006/inco.1995.1033 . Recuperado 2013-04-19 .
  5. ^ Castagna, Giuseppe (1996). Programação Orientada a Objetos: Uma Fundação Unificada. Progresso em Ciência da Computação Teórica. Birkhäuser. pág. 384. ISBN 978-0-8176-3905-1.
  6. ^ Castagna, Giuseppe (1995). "Covariância e contravariância: conflito sem causa". ACM Transações em Linguagens e Sistemas de Programação . 17 (3): 431–447. CiteSeerX 10.1.1.115.5992 . doi : 10.1145/203095.203096. S2CID  15402223. 
  7. ^ Bruce, Kim; Cardelli, Luca; Castagna, Giuseppe; Fermentos, Gary T.; Pierce, Benjamim (1995). "Em métodos binários". Teoria e Prática de Sistemas de Objetos . 1 (3): 221–242. doi :10.1002/j.1096-9942.1995.tb00019.x . Recuperado 2013-04-19 .
  8. ^ "Usando o tipo dinâmico (guia de programação C#)" . Recuperado 2020-05-14 .
  9. ^ "Conceitos básicos" . Recuperado 2020-05-14 .
  10. ^ "Dynamic .NET - Compreendendo a palavra-chave dinâmica em C# 4" . Recuperado 2020-05-14 .
  11. ^ Groovy - Multi-métodos
  12. ^ @arrows/multimethod Despacho múltiplo em JavaScript/TypeScript com resolução de despacho configurável por Maciej Cąderek.
  13. ^ Coady, Aric, multimétodo: despacho de múltiplos argumentos. , recuperado 28/01/2021
  14. ^ multimethods.py Arquivado em 2005-03-09 no Wayback Machine , Despacho múltiplo em Python com resolução de despacho configurável por David Mertz, et al.
  15. ^ "Multimétodos de cinco minutos em Python" .
  16. ^ "PEAK-Regras 0.5a1.dev" . Índice de pacotes Python . Acesso em 21 de março de 2014 .
  17. ^ "PyProtocols" . Kit de aplicativos corporativos Python . Acesso em 26 de abril de 2019 .
  18. ^ "Registro". Leia os documentos . Acesso em 26 de abril de 2019 .
  19. ^ "C Object System: Uma estrutura que traz C ao nível de outras linguagens de programação de alto nível e além: CObjectSystem/COS". GitHub . 19-02-2019.
  20. ^ "Relatório sobre suporte de linguagem para métodos múltiplos e métodos abertos para C ++" (PDF) . 2007-03-11. Despacho múltiplo – a seleção de uma função a ser invocada com base no tipo dinâmico de dois ou mais argumentos – é uma solução para vários problemas clássicos em programação orientada a objetos.{{cite web}}: CS1 maint: url-status ( link )
  21. ^ yomm2, rápido, multimétodos abertos ortogonais para C++ por Jean-Louis Leroy.
  22. ^ Stroustrup, Bjarne (1994). "Seção 13.8". O Design e a Evolução do C++ . Indianápolis, IN, EUA: Addison Wesley. Bibcode :1994dec..livro.....S. ISBN 978-0-201-54330-8.
  23. ^ openmethods, Open Multi-Methods for D por Jean-Louis Leroy.
  24. ^ "Métodos". O Manual Júlia . Julialang. Arquivado do original em 17 de julho de 2016 . Acesso em 11 de maio de 2014 .
  25. ^ "Multimétodos em C# 4.0 com 'Dynamic'" . Recuperado 2009-08-20 .
  26. ^ "Cecil Language" . Recuperado 2008-04-13 .
  27. ^ "Multimétodos em Clojure" . Recuperado 2008-09-04 .
  28. ^ Steele, Guy L. (1990). "28". LISP comum: a linguagem . Bedford, MA, EUA: Digital Press. ISBN 978-1-55558-041-4.
  29. ^ "Antecedentes e Objetivos" . Recuperado 2008-04-13 .
  30. ^ "Elixir Lang | Introdução | Módulos e funções" . Recuperado 2017-11-10 .
  31. ^ "The Fortress Language Specification, Versão 1.0" (PDF) . Arquivado do original (PDF) em 2013-01-20 . Recuperado 2010-04-23 .
  32. ^ "Multimétodos em Groovy" . Recuperado 2008-04-13 .
  33. ^ "Métodos - LassoGuide 9.2" . Recuperado 2014-11-11 .
  34. ^ "Padrão de visitante versus multimétodos" . Recuperado 2008-04-13 .
  35. ^ "Nim Manual: Multi-métodos" . Recuperado 2022-05-03 .
  36. ^ "Perl 6 FAQ" . Recuperado 2008-04-13 .
  37. ^ "Como funcionam os métodos S4" (PDF) . Recuperado 2008-04-13 .
  38. ^ "Despacho Múltiplo em Seed7" . Recuperado em 23/04/2011 .
  39. ^ "Manual do sistema TADS 3" . Recuperado 2012-03-19 .
  40. ^ "Despacho Múltiplo VB.Net" . Recuperado 2020-03-31 .
  41. ^ "Novos recursos em C#4.0 e VB.Net 10.0". 4 de novembro de 2010 . Recuperado 2020-03-31 .
  42. ^ "Notas para especialistas em linguagem de programação" . Recuperado 2016-08-21 .
  43. ^ "Despacho múltiplo" .

links externos

  • Stroustrup, Bjarne; Solodkyy, Yuriy; Pirkelbauer, Peter (2007). Abra Multi-Métodos para C++ (PDF) . ACM 6ª Conferência Internacional sobre Programação Generativa e Engenharia de Componentes.
  • "Despacho múltiplo dinâmico". docs.racket-lang.org . Recuperado 2018-03-12 .