O que exatamente é a metaprogramação?

128

Eu estava lendo um artigo no TheServerSide sobre programação de ployglot na plataforma Java . Alguns comentários no artigo se referem à metaprogramação como a capacidade de gerar código (talvez em tempo real).

É metaprogramação da capacidade de gerar código em tempo real ou é a capacidade de injetar métodos e atributos em objetos existentes em tempo de execução (como o que algumas linguagens dinâmicas como Python, Ruby e Groovy permitem).

Parag
fonte
7
Você pode estar interessado nesta resposta stackoverflow.com/questions/2565572/…
ewernli
@ewernli: Essa resposta é realmente melhor do que qualquer uma das respostas aqui!
JD

Respostas:

100

A metaprogramação refere-se a várias maneiras pelas quais um programa tem conhecimento de si mesmo ou pode se manipular.

Em linguagens como C #, a reflexão é uma forma de metaprogramação, pois o programa pode examinar informações sobre si mesmo. Por exemplo, retornando uma lista de todas as propriedades de um objeto.

Em linguagens como o ActionScript, você pode avaliar funções em tempo de execução para criar novos programas como eval ("x" + i). DoSomething () afetaria um objeto chamado x1 quando i for 1 e x2 quando i for 2.

Finalmente, outra forma comum de metaprogramação é quando o programa pode mudar de forma não-trivial. O LISP é bem conhecido por isso e é algo que Paul Graham defendeu cerca de uma década atrás. Vou ter que procurar alguns de seus ensaios específicos. Mas a idéia é que o programa mudaria outra parte do programa com base em seu estado. Isso permite um nível de flexibilidade para tomar decisões em tempo de execução que é muito difícil na maioria dos idiomas populares atualmente.

Também é importante notar que, nos bons e velhos tempos da programação em montagem direta, os programas que se alteravam em tempo de execução eram necessários e muito comuns.

Do ensaio de Paul Graham "What Made Lisp Different" :

Muitos idiomas têm algo chamado macro. Mas as macros Lisp são únicas. E, acredite ou não, o que eles fazem está relacionado aos parênteses. Os designers do Lisp não colocaram todos esses parênteses na linguagem apenas para serem diferentes. Para o programador Blub, o código Lisp parece estranho. Mas esses parênteses estão lá por uma razão. Eles são a evidência externa de uma diferença fundamental entre o Lisp e outras línguas.

O código Lisp é feito de objetos de dados Lisp. E não no sentido trivial de que os arquivos de origem contenham caracteres, e as strings são um dos tipos de dados suportados pelo idioma. O código Lisp, depois de lido pelo analisador, é feito de estruturas de dados que você pode percorrer.

Se você entende como os compiladores funcionam, o que realmente está acontecendo não é tanto o Lisp ter uma sintaxe estranha quanto o Lisp não ter a sintaxe. Você escreve programas nas árvores de análise que são geradas no compilador quando outros idiomas são analisados. Mas essas árvores de análise são totalmente acessíveis aos seus programas. Você pode escrever programas que os manipulam. No Lisp, esses programas são chamados de macros. Eles são programas que escrevem programas.

Programas que escrevem programas? Quando você gostaria de fazer isso? Não muito frequentemente, se você pensa em Cobol. O tempo todo, se você pensa em Lisp. Seria conveniente aqui se eu pudesse dar um exemplo de uma macro poderosa e dizer lá! que tal isso? Mas se eu soubesse, pareceria algo sem sentido para alguém que não conhecia Lisp; não há espaço aqui para explicar tudo o que você precisa saber para entender o que isso significava. No Ansi Common Lisp , tentei mover as coisas o mais rápido possível, e mesmo assim não cheguei às macros até a página 160.

Mas acho que posso dar um tipo de argumento que pode ser convincente. O código fonte do editor da Viaweb era provavelmente de 20 a 25% de macros. As macros são mais difíceis de escrever do que as funções comuns do Lisp, e é considerado um estilo ruim usá-las quando não são necessárias. Portanto, toda macro nesse código está lá porque precisa estar. O que isso significa é que pelo menos 20 a 25% do código deste programa está fazendo coisas que você não pode fazer facilmente em qualquer outro idioma. Por mais cético que o programador Blub possa estar sobre minhas reivindicações pelos poderes misteriosos de Lisp, isso deve deixá-lo curioso. Nós não estávamos escrevendo este código para nossa própria diversão. Éramos uma pequena startup, programando o máximo possível para colocar barreiras técnicas entre nós e nossos concorrentes.

Uma pessoa suspeita pode começar a se perguntar se havia alguma correlação aqui. Uma grande parte do nosso código estava fazendo coisas que são muito difíceis de fazer em outros idiomas. O software resultante fez coisas que o software de nossos concorrentes não podia fazer. Talvez houvesse algum tipo de conexão. Convido você a seguir esse tópico. Pode haver mais para aquele velho mancando de muletas do que aparenta.

DavGarcia
fonte
6
Não se esqueça da metaprogramação de modelos em C ++. A capacidade de executar expressões e tomar decisões em tempo de compilação e fazer com que os resultados sejam compilados estaticamente no executável final.
Remy Lebeau
1
Fiquei chocado in order to put technical barriers between us and our competitorse isso está correto.
Evan Hu
4
Programas que se manipulam são um subconjunto de todos os metaprogramas. Metaprogramação em geral significa apenas programas que manipulam programas.
JD
55

Ótima pergunta. Lamento ver que atualmente nenhuma das respostas realmente responde sua pergunta corretamente. Talvez eu possa ajudar ...

A definição de metaprogramação é realmente bastante simples: significa programas que manipulam programas.

Sua resposta aceita diz programas que se manipulam. Esses são realmente metaprogramas, mas são um subconjunto de todos os metaprogramas.

Tudo:

  • Analisadores
  • Linguagens específicas do domínio (DSLs)
  • Linguagens específicas do domínio incorporado (EDSLs)
  • Compiladores
  • Intérpretes
  • Reescritores de termos
  • Provadores de teoremas

são metaprogramas. Portanto, o compilador GCC é um metaprograma, o intérprete CPython é um metaprograma, o sistema de álgebra computacional Mathematica é um metaprograma, o provador do teorema de Coq é um metaprograma e assim por diante.

Outras respostas afirmaram que os metaprogramas são programas que geram outros programas. Esses são realmente metaprogramas, mas, novamente, são um subconjunto de todos os metaprogramas. A biblioteca de Transformada de Fourier mais rápida do oeste (FFTW) é um exemplo desse metaprograma. O código fonte é escrito principalmente em OCaml e gera bits de código C (chamados codelets) que são combinados para criar rotinas de Transformada Rápida de Fourier de alto desempenho otimizadas para máquinas específicas. Essa biblioteca é realmente usada para fornecer as rotinas de FFT no Matlab. As pessoas escrevem programas para gerar métodos numéricos há décadas, desde os primeiros dias do FORTRAN .

A primeira linguagem de programação que integrou o suporte à metaprogramação foi a linguagem LISt Processor (LISP) no final da década de 1950. O LISP 1.5 incluiu vários recursos que facilitaram a metaprogramação. Primeiro, o tipo de dados principal do LISP são listas aninhadas, ou seja, árvores como (a (b c) d), o que significa que qualquer código LISP pode ser expresso nativamente como uma estrutura de dados. Isso é conhecido como homoiconicidade. Em segundo lugar, o código LISP pode ser convertido em dados facilmente usando QUOTE. Por exemplo, (+ 1 2 3)adiciona 1 + 2 + 3 e (QUOTE (+ 1 2 3))cria uma expressão que adiciona 1 + 2 + 3 quando avaliada. Em terceiro lugar, o LISP forneceu um avaliador meta-circular que permite usar o interpretador host ou o compilador para avaliar o código LISP em tempo de execução, incluindo o código LISP gerado em tempo de execução. Os descendentes de LISP incluem Scheme e Clojure. Em todas essas linguagens, a metaprogramação é mais comumente vista na forma de programas que se modificam, geralmente usando macros.

Na década de 1970, Robin Milner desenvolveu uma MetaLanguage (ML) que evoluiu para a família ML de linguagens de programação que inclui ML e OCaml padrão e influenciou fortemente Haskell e F # . Esses idiomas facilitam a expressão de outros idiomas. Nessas linguagens, os metaprogramas são mais comumente vistos na forma de lexers, analisadores, intérpretes e compiladores.

Em 1994, Erwin Unruh descobriu que o sistema de modelos C ++ estava completo em Turing e poderia ser usado para executar programas arbitrários em tempo de compilação . A metaprogramação de modelos C ++ trouxe a metaprogramação para as massas não lavadas que (a) a usaram para muitas coisas diferentes, incluindo a geração de métodos numéricos na biblioteca Blitz ++ .

JD
fonte
33

Bem, a metaprogramação é apenas programação, mas é basicamente "escrever código que escreve código" .

A capacidade que você menciona, quando um programa pode observar e modificar sua própria estrutura e comportamento, é chamada de reflexão e é um tipo de metaprogramação.

Linguagens dinamicamente tipadas, possuem recursos poderosos de reflexão em tempo de execução, possibilitados pela natureza interpretada dessas linguagens ...

Linguagens de tipo estático também têm técnicas poderosas de metaprogramação, por exemplo, a metaprogramação de modelos C ++ ...

CMS
fonte
14

Esta é apenas a minha opinião pessoal, que é provavelmente a definição mais liberal de metaprogramação.

Eu acho que inclui:

  1. Compilar geração de código ou geração de código de tempo de execução (ou ambas)
  2. Pensamento orientado a aspectos ou programação orientada a aspectos
  3. Pensamento seco

Eu acho que você pode chegar lá usando qualquer um destes e em combinação:

  1. Reflexão
  2. DSLs (idiomas específicos do domínio)
  3. Atributos (.NET) ou Anotações (Java)
  4. Genéricos (.NET / Java)
  5. Modelos (C ++)
  6. method_missing (Ruby)
  7. encerramentos / funções de primeira classe / delegados
  8. AOP - Programação Orientada a Aspectos
BuddyJoe
fonte
resposta muito concisa e atenciosa. me deu um bom menu de coisas para investigar. obrigado!
Swyx
6

A metaprogramação está escrevendo um programa que gera outro programa. Isso é algo em que idiomas como o Lisp são realmente bons. É muito mais fácil fazer isso em uma linguagem que suporta macros reais (não macros C ++, mas que podem manipular o código que elas produzem), como Ruby, Lisp, Scheme, etc., do que em uma linguagem como Java.

Uma implementação é criar uma "linguagem específica de domínio", que é uma maneira de aprimorar uma linguagem de programação para realizar uma tarefa específica. Pode ser incrivelmente poderoso se feito corretamente. O Ruby on Rails é um bom exemplo desse tipo de programação.

Se você estiver interessado em explorar esse método, consulte a Estrutura e interpretação de programas de computador, que é um dos livros principais que abordam o assunto.

Steve Rowe
fonte
5

Metaprogramação é a gravação de programas de computador que gravam ou manipulam outros programas (ou eles mesmos) como dados, ou que fazem parte do trabalho em tempo de execução que, de outra forma, seria feito em tempo de compilação. Em muitos casos, isso permite que os programadores realizem mais tarefas no mesmo período de tempo necessário para escrever todo o código manualmente, ou oferece aos programas maior flexibilidade para lidar com novas situações com eficiência sem recompilação. ( Fonte .)

Basicamente, é escrever código que gera mais código, que é executado para atingir algum objetivo. Isso geralmente é feito no mesmo idioma (usando javascript para criar uma string javascript e depois eval) ou para emitir outro idioma (usando o .NET para criar um arquivo em lotes do Windows).

EndangeredMassa
fonte
4

A wikipedia tem um bom artigo sobre o assunto. Não é necessário fazer modificações no tempo de execução para que algo se qualifique como metaprogramação. Por exemplo, muitas pessoas usam modelos C ++ para fazer metaprogramação em tempo de compilação.

Mr Fooz
fonte