Programação automática: escreve código que escreve código [fechado]

105

Depois de ler o livro The Pragmatic Programmer , um dos argumentos que achei mais interessantes foi "escrever código que escreve código".

Tentei pesquisar na internet mais explicações ou artigos sobre o assunto e, embora tenha encontrado bons artigos sobre o assunto, ainda não encontrei nenhuma implementação de código específica ou bons exemplos.

Eu sinto que ainda não é um argumento tão comum, algo que carece de documentação ou que não é adotado por tantas pessoas, e eu gostaria de saber mais sobre isso.

O que você acha do assunto? É algo que realmente aumentará sua produtividade? Quais são alguns bons recursos sobre o assunto, entre livros, blogs, apresentações de slides, etc?


Alguns exemplos de código seriam muito apreciados para me permitir entender melhor sua implementação.


Aqui está a página wiki sobre o assunto com várias técnicas de programação relevantes, como Meta Programação, Programação Generativa e Geração de Código.

Jose Faeti
fonte
32
Eu fiz uma vez escrever o código que escreveu o código que escreveu código ... :)
Benjol
9
@Benjol: Você estava escrevendo em Lisp?
compman
11
Além disso, as linguagens do servidor fazem isso o tempo todo, gerando HTML, CSS e JavaScript. Você pode ter um script do lado do servidor que cria um script do lado do servidor que cria html com javascript que cria mais html, e ninguém vai ficar de olho nele por causa de sua popularidade .
precisa saber é o seguinte
8
Se ainda não o fez, consulte esta série de artigos do IBM developerWorks: " A arte da metaprogramação " Parte 1 , Parte 2 e Parte 3 .
John Tobler
3
O AtomWeaver ( atomweaver.com ) é um bom exemplo de programação automática: primeiro, você cria miniprogramas reutilizáveis ​​em Lua. Em seguida, modele seu sistema reutilizando esses ativos. O AtomWeaver então cria um programa Lua que contém seus "mini-geradores" para gerar o código-fonte final do sistema. Você pode ajustar seu modelo e gerar novamente.
Rui Curado

Respostas:

49

No mundo Lisp, é bastante comum ver o código que escreve código que escreve código (e assim por diante). Portanto, qualquer projeto Lisp ou Scheme de tamanho decente servirá como um bom exemplo de código. Eu recomendo olhar para o compilador Racket e as fontes de tempo de execução, bem como o Bigloo , suas bibliotecas são simplesmente brilhantes.

Quanto à produtividade: estou usando a metaprogramação como uma técnica dominante em quase todo o meu trabalho de desenvolvimento, e isso claramente ajuda bastante, reduzindo o tamanho do código e aumentando sua legibilidade. A chave está no uso de idiomas específicos do domínio , e a metaprogramação é uma das maneiras mais eficientes de implementá-los.

SK-logic
fonte
67

Prefiro ir um pouco mais longe e, em vez de escrever código que escreve código, escreve código que gera objetos, métodos, funções. Isso pode ser conseguido com macros Lisp ou recursos de modificação de programa dinâmico Ruby, por exemplo.

A pequena diferença é que você não termina com os arquivos de origem que foram gerados automaticamente. Geralmente, esses arquivos não são legíveis por humanos e não podem ser modificados; portanto, por que se preocupar com eles? Não gosto da ideia de aumentar minha base de código com algo que não posso controlar.

Um livro que gostei de ler sobre o assunto foi Metaprogramação de Ruby (se você conhece o idioma Ruby)


Edite após a seguinte pergunta no comentário:

Por que seria útil ainda ter que codificar o código gerador? Devo escrever um código capaz de gerar coisas diferentes, dependendo da entrada do usuário, para poder reutilizá-lo repetidamente?

Primeiro, a metaprogramação não é uma meta, mas uma ferramenta. Não use metaprogramação porque "é legal" ou "o X disse que todo desenvolvedor deveria usá-lo".

Eu acho que uma boa razão para usar a metaprogramação é generalizar algum padrão comum (padrão como algo que se repete) que você encontrou em seu código e que nenhuma outra técnica de programação usual (herança, padrões de design etc.) pode alcançar.

Como dito por Jordan , um caso de uso típico é manipulação de banco de dados e ORM (Object Relation Mapping). Mais uma vez, no Ruby, você deve observar o ActiveRecord, que é um ótimo exemplo de metaprogramação aplicada ao ORM.

Como nota final:

Não pense "Quero aplicar a metaprogramação, onde posso aplicá-la no meu código?".

Pense "Eu vejo esse padrão que se repete em todo o meu código, não consigo encontrar uma maneira de refatorar o código em algo menor e mais reutilizável. Talvez a metaprogramação possa me ajudar?"

David
fonte
3
@ Jose: Geralmente, você gera código por meio de modelos. Há velocidade apache (N-), por exemplo, ou os modelos T4 do visual studio. Então você só tem um programa que alimenta os metadados nos seus modelos e cria novos arquivos a partir de então. É muito fácil e eu estou fazendo isso o tempo todo para gerar UI-esqueletos, entidades etc.
Falcon
2
@ Joseph Faeti, dê uma olhada nas macros do Lisp (ou Clojure ou Nemerle, dependendo das preferências da sua plataforma).
SK-logic
1
Eu acrescentaria que a metaprogramação pode substituir algum padrão como política ou estado, mas sem custo de tempo de execução. Isso não é apenas para problemas que não podem ser alcançados com refatoração comum, mas também é uma alternativa melhor.
deadalnix
1
@ Jose Faeti: Vejo que você conhece algum Python. Ele também possui recursos de metaprogramação, embora eu realmente não os tenha usado. Dê uma olhada no Dangerously Advanced Python PDF
Kit
3
@ Falcon: IMO que é a pior maneira de gerar código; é uma solução alternativa muito ruim para idiomas sem nenhum recurso de metaprogramação interno. Em vez de gerar Java ou C #, seria melhor escrever esse código em uma linguagem JVM ou .NET de nível superior.
kevin Cline
19

Melhor ainda, use o código que outra pessoa escreveu que escreve seu código para você.

A automação de código geralmente é boa para ORMs e outros códigos de interação com o banco de dados e, é claro, para a criação de código repetitivo, mas semelhante.

Claro, se você estiver criando muitas classes parecidas, talvez possa ter conseguido a mesma coisa em uma linguagem dinâmica muito antes, mas discordo.

Isso é adotado por muitas pessoas, embora você frequentemente encontre o software rotulado como gerador de código.

Veja empresas e produtos como CodeSmith e MyGeneration ou navegue neste artigo da Wikipedia: http://en.wikipedia.org/wiki/Comparison_of_code_generation_tools

Jordânia
fonte
6
Não é melhor. Seu precioso código tão pouco não pode ser gerenciado adequadamente por outra ferramenta de geração de código de outro sujeito, pois esse outro sujeito não sabe nada sobre suas especificidades. O uso mais produtivo da metaprogramação é a implementação de linguagens específicas de domínio - e, como o nome sugere, elas são específicas para o seu domínio de problemas, não podem ser implementadas por mais ninguém além de você.
SK-logic
@ SK-logic: e o código gerado pelo ORM? É gerado por outra ferramenta / biblioteca e ainda atende a muitas necessidades do projeto.
David
@ David, para ser honesto, não estou muito convencido com os ORMs genéricos. Eu tive muitos problemas com eles no passado, muitas vezes recorrendo à implementação de meus ORMs específicos.
SK-logic
1
@ Jordan, todas essas ferramentas são muito específicas (e pior - baseadas em texto , ou seja, inferiores ao design). Estou falando da metaprogramação adequada.
SK-logic
1
@AtillaOzgur, eles podem ser "muito bons", é verdade. Mas eles não são melhores que os eDSLs. A geração de código independente é obviamente muito mais limitada e muito menos flexível do que a macroprogramação.
SK-logic
16

Um dos exemplos clássicos é lex e yacc. Seu principal objetivo é evitar a labuta de escrever qualquer tipo de analisador. Ao longo do caminho, eles tornam muito mais rápido a criação de analisadores complexos com muitas regras e estados e também evitam todos os erros de surpresa cometidos por pessoas que criam seus próprios.

Essa também é a idéia por trás de c, que é uma ferramenta para escrever assembler. O mesmo vale para qualquer idioma de alto nível que você queira nomear. Para ferramentas que escrevem código para você, existem alguns paradigmas simples.

Um IDE adequado ajuda fornecendo documentação na ponta dos dedos, preenchimento automático inteligente e trechos de código. Os IDE também incluem vários modelos, para que você não precise iniciar um programa do zero. Existem programas para fazer um diagrama de uml e classificar as aulas em um idioma de alto nível.

Por fim, você pode escrever suas próprias ferramentas para geração de código no seu conjunto de problemas. Foi assim que lex e yacc começaram. Qualquer tipo de idioma específico do domínio existe exatamente por esse motivo. Você cria alguns blocos de construção que descrevem sua solução em um código mais fácil de entender, agrupando atividades comuns ou seções complicadas com comandos simples. Você não está procurando uma solução para todos os problemas, apenas uma definição mais fácil do específico com o qual está lidando.

Em certo sentido, tudo o que você faz acima da camada binária é automação de código.

Spencer Rathbun
fonte
Essa é uma visão realmente agradável. No geral, é apenas mais um dos muitos métodos que os programadores tentam usar para facilitar suas operações e se concentrar em um nível mais alto de codificação, em vez de nos detalhes do código de sintaxe.
Jose Faeti
1
@ Jose Faeti O artigo da wikipedia en.wikipedia.org/wiki/Automatic_programming possui links para várias ferramentas diferentes, se você estiver interessado em mais alguns detalhes. Eu também sugeriria ler sobre lex e yacc, pois há um pouco mais de documentação e descrição para eles.
Spencer Rathbun
Em linguagens suficientemente poderosas (por exemplo, C ++ em vez de C), ferramentas externas como lex e yacc são desnecessárias.
Kevin cline
O YACC não escreve "nenhum tipo de analisador". Ele grava um tipo específico de analisador (LALR) que é muito difícil de acertar sem ajuda automatizada. Há outro tipo de analisador (descida recursiva) que é muito mais fácil de escrever e acertar, e correspondentemente mais fácil de ler e entender o que está acontecendo.
Mason Wheeler
@MasonWheeler O tipo de analisador se referia às gramáticas que podem ser criadas para resolver problemas, em um sentido amplo e não exato. Lê-lo um ano depois, não é tão claro quanto eu gostaria. Porém, não tenho certeza se concordo com você, pois é mais fácil escrever e usar os analisadores LL (*).
Spencer Rathbun
13

Metaprogramação

A metaprogramação é uma técnica controversa em muitas lojas. O motivo é que, como qualquer ferramenta poderosa, a magnitude da ajuda ou do dano é grande.

Prós

  • Mais expressivo, menos código para escrever e manter (geralmente por uma ordem de magnitude ou mais)
  • Consistência, comportamento mais consistente sobre a classe de problemas que você resolve com o código
  • Produtividade, menos código para uma solução para um espaço maior de problemas

Contras

  • Complexidade, pode ser muito complicado, embora exista menos código
  • Segurança, algum tipo de segurança e análise estática em geral serão sacrificadas
  • Os bugs afetam mais, pequenos erros terão um impacto maior

Sou um grande fã de metaprogramação, mas faço isso há muito tempo. Para mim, a troca de tamanho de código reduzido e comportamento consistente mais do que compensar os riscos. Menos código significa menos erros, menos código para manter, e geralmente posso adicionar grandes partes de funcionalidade rapidamente.

No entanto, isso não significa que eu acho que todos os programadores deveriam se envolver nele. Eu já vi e tive que corrigir grandes problemas criados pela metaprogramação. Geralmente a partir de quando pessoas que não entendem o conceito e tentam estender a funcionalidade, ou apenas consertam um bug. É preciso uma mentalidade específica que seja no mínimo orientada para os detalhes. A questão de usar técnicas de metaprogramação deve ser uma decisão da equipe . Se você tem membros da equipe que não entendem, não tem temperamento para isso ou é contra, nenhuma equipe deve usar a metaprogramação.

dietbuddha
fonte
Obrigado pelas considerações úteis! Você poderia me sugerir uma tarefa realmente simples e básica que poderei implementar usando a metaprogramação, o que me poupará tempo em relação à codificação normal, um pequeno exemplo de código?
Jose Faeti
haha me faz lembrar de um erro que tive vários anos atrás no GCC. 162 linhas para colocar a mensagem de erro na minha tela. Metaprogramação recursiva FTW!
deadalnix
6
A complexidade da metaprogramação é superestimada. Não há absolutamente nada complicado, desde que você esteja usando as ferramentas certas. E as DSLs são muito mais fáceis de depurar e manter do que o código padrão comum. Além disso, não consigo entender por que alguém deveria sacrificar a segurança de tipos - é exatamente o contrário, as DSLs também podem ter sistemas de tipos altamente eficientes e específicos de domínio.
SK-logic
2
@ SK-logic: Nem todos os idiomas suportam bem a metaprogramação. Às vezes, coisas como segurança de tipo são sacrificadas (ou seja, C) . A metaprogramação também não é apenas DSLs. Inclui coisas como programação no estilo de expedição, genéricos, currying, inspeção de objetos, aplicativos dinâmicos, etc. Quanto à complexidade, acho fácil para nós (pessoas com experiência em metaprogramação) dizer que não é complicado. Vi outras dificuldades em entender todos os casos em que o código será executado. Depende principalmente da experiência e da técnica envolvida.
dietbuddha
@dietbuddha, você poderia explicar por que sacrificar a segurança do tipo DSL própria, não importa como é implementada? Você pode escrever um intérprete ad hoc em um C puro com um sistema de tipos forte (consulte Abraços, por exemplo). Você pode escrever um gerador de código direcionado a C, que faz toda a digitação, sem depender do sistema de tipo de idioma de destino. Para complexidade: a maioria das pessoas está fazendo isso de uma maneira desnecessariamente complexa, enquanto todas as mesmas metodologias de design podem ser aplicadas à geração de código e à programação "normal". Quase nenhum conhecimento novo é necessário.
SK-logic
9

A maioria dos códigos escreve códigos. Por exemplo, o código php ajuda a escrever html. A biblioteca php pdo ajuda a escrever chamadas SQL. As funções de E / S do arquivo gravam código para se comunicar com o sistema operacional. Mesmo uma chamada de função regular é uma referência a outro bloco de código que é executado. Portanto, suas chamadas de funções estão escrevendo código.

Em termos gerais, podemos pensar em computação como escrever códigos que escrevem códigos de forma recursiva, formando uma pilha que termina quando se depara com a realidade física dos códigos conectados ao hardware.

Ben Haley
fonte
3
Eu não chamaria html de linguagem de programação. É uma sintaxe para documentos
Simon Bergot
3
@ Simon é um ponto interessante. Há toda uma variedade de poderes expressivos para os diferentes códigos que usamos. O código pode gravar em um idioma mais fraco, em um idioma mais forte ou em seu próprio idioma.
Ben Haley
5

Como você faz isso varia de acordo com seus requisitos. Supondo que você esteja usando a geração de código estático, você poderá escrever toda a infraestrutura ou usar um gerador existente, como CodeSmith ou MyGeneration. Usando estes, você só precisa escrever os modelos necessários.

Meu último projeto envolveu algumas telas básicas do ASP.NET CRUD (a geração de código é boa para isso). O processo foi definir entidades como metadados em arquivos xml. Escreva modelos para cobrir os vários artefatos necessários (classes de entidade, repositórios, classes de serviço, controles asp.net, páginas asp.net etc.). Execute o processo de geração e denomine a saída.

Há alguma sobrecarga na escrita dos modelos, mas eles podem ser reutilizados para projetos semelhantes subsequentes. Da mesma forma, as alterações nos dados subjacentes são tratadas alterando os metadados e executando novamente a geração, tornando as alterações mais simples e rápidas de implementar.

Quanto ao teste. Como esse é um sistema de modelo, você precisará gastar algum tempo validando inicialmente a saída do processo, se o modelo estiver errado, toda a saída desse modelo estará igualmente errada. Quando estiver satisfeito com isso, você também poderá usar os geradores de código para criar testes básicos a partir dos metadados xml, que poderão ser estendidos para cobrir casos especiais. No entanto, lembre-se de que você ainda pode precisar entregar testes de código para atender a coisas específicas, a geração de código reduz seu trabalho e não o elimina completamente.

CdMnky
fonte
5

Em nossa empresa, usamos algumas ferramentas que realmente geram classes C ++ ou C # com dados baixados da Internet. Essas classes são contêineres de dados e contêm um grande número de objetos nas listas.

Holli
fonte
Algo como os trechos de código encontrados em algum IDE como o Visual Studio, por exemplo?
Jose Faeti
@ Joseph Nossa ferramenta é apenas um aplicativo para converter saída HTML em uma classe. Portanto, em vez de baixar os dados sempre que o aplicativo é iniciado, fazemos o download uma vez e fazemos uma classe com eles.
quer
5

A metaprogramação faz parte da programação há muito tempo. Considere não apenas ferramentas como os designers SWIG ou WYSIWYG, que criam código, mas também ferramentas na linguagem como o pré-processador de C, ou mesmo os modelos de C ++ e os genéricos de C # / Java - para não mencionar o Reflection.

De fato, você poderia argumentar que todo compilador é apenas outro metaprograma - eles recebem o texto do programa e a máquina de saída ou o código da VM. E vida sem compiladores? Owch.

DeadMG
fonte
Isso mesmo, mas como você pode implementá-lo em sua própria linguagem de programação para aumentar sua produtividade? É disso que estou sentindo falta.
Jose Faeti
5

Aqui está um exemplo concreto do meu passado.

Eu estava trabalhando em um site que tinha cerca de 50 MB de código-fonte Delphi usando o BDE para acesso a dados. Eles queriam mudar para o uso do Direct Oracle Access para permitir uma atualização do Oracle além da versão mais alta suportada pelo BDE (8i, se bem me lembro).

Então, em vez de fazer com que uma equipe de codificadores trabalhe em todos os formulários e módulos de dados alterando todos os componentes manualmente, escrevi um script PERL que:

  1. Analisou o DFM (arquivo de formulário) e identificou todos os objetos TQuery, TTable, TStoredProcedure & TDatabase - armazenando os itens em uma lista.

  2. Analisou o PAS (código) e identificou o uso dos objetos - os TQueries estavam fazendo atualizações ou seleções? Além disso, ele identificou qualquer objeto criado no código, em vez de soltá-lo em um formulário no IDE.

  3. Reescreva o DFM e o PAS alterando os tipos de objetos adequadamente (por exemplo, TTable -> TOracleDataSet com a propriedade SQL definida como "selecione * de" etc) e as chamadas de método. Além disso, chamadas de método extras foram adicionadas, se apropriado, para fechar, abrir e definir parâmetros.

Em resumo, três semanas de trabalho aprimorando o script para trabalhar em diferentes aplicativos escritos por equipes diferentes com diferentes estilos de codificação, em vez da estimativa original de mais de 5 desenvolvedores trabalhando por 6 meses.

E o motivo pelo qual pensei em usar essa abordagem foi através da leitura de O Programador Pragmático

mcottle
fonte
Isso é ótimo, agora estou no Perl há alguns dias e já criei algumas ferramentas de produtividade para gerar espaços de trabalho básicos para o desenvolvimento da Web, com todos os diretórios, arquivos etc. apenas digitando "criar espaço de trabalho"! :)
Jose FAETI
1
@ Joseph Essa é a ideia. Use linguagens de script para automatizar coisas repetitivas. Pode ser um caso único em que você obtém um aumento de produtividade de 8x ou, como no seu caso, algo demorado que você fará repetidas vezes.
Mcottle
4

Você pede exemplos ....

Ao trabalhar com SQL, você não deve alterar o banco de dados diretamente, mas deve executar scripts que fazem as alterações desejadas, incluindo alterações estruturais no banco de dados (adição de tabelas, colunas, chaves primárias, restrições e assim por diante) . Com bastante frequência, você precisará executar a mesma ação em várias tabelas ou colunas ao mesmo tempo, e fazê-las uma a uma seria tedioso, um script curto que gera um script maior que faz o que você quer pode ser real. economia de tempo.

Por exemplo, antes de o tipo de dados DATE ser introduzido no MS SQl Server, a única opção para uma coluna de data era DATETIME, que possui uma parte do tempo - uma parte do tempo que torna mais difícil lidar com os dados. Ao fazer upgrade para uma versão com o tipo de dados Data, convém atualizar as colunas em que a hora é sempre 00:00. Em um banco de dados com dezenas ou mesmo centenas de colunas DateTime, isso consumiria bastante tempo. Mas é fácil escrever um script que consulta todas as tabelas, verificando todas as colunas com um tipo de dados DATETIME para ver se é a hora das 00:00 e se não criar uma instrução ALTER para que a tabela / coluna seja alterada o tipo de dados para DATE. Presto, código que escreve código.

jmoreno
fonte
3

Dê uma olhada nas macros CL (lábios comuns). Na minha opinião, é exatamente isso que você deseja. Lábios é perfeito na metaprogramação.

Também sugiro Nemerle se você deseja ter recursos .NET com suporte perfeito à metaprogramação (incluindo macros)

Mas se você deseja um verdadeiro mecanismo de geração de código, dê uma olhada no Apache Thrift

cnd
fonte
3

Estou apenas trabalhando em uma ferramenta desse tipo. Em nosso caso específico, geramos o código VB.NET com base na camada de dados nas assinaturas das funções no banco de dados.

É difícil começar a trabalhar e com a geração de código, pois você não tem idéia de como o código deve ser gerado, mas depois de ter um conjunto de regras estabelecido, o código que deve ser gerado sempre pode ser gerado com base nessas regras. , trabalhar com esse código não é tão difícil. Obviamente, dependendo da complexidade da geração de código e do número de regras, a tarefa pode se tornar mais difícil. Mas, em essência, a geração automática de código é usada para tarefas de codificação repetitivas e não para códigos avançados que variam muito.

Testar a saída é duplo. Primeiro, você precisa garantir que o código seja compilado, e isso é fácil. Então você deve se certificar de que a saída faça o que você quis fazer com base nos parâmetros em que foi gerada .. e a dificuldade disso varia na complexidade do código que você gera.

Minha recomendação sincera é que, se você sentir vontade de escrever código de maneira repetitiva e puder gastar tempo, tente pensar se o que está fazendo não pode ser feito pelo código gerado. E se sim (se for um código repetitivo do que quase sempre é o caso) pense quantas vezes você precisará estender, modifique levemente esse código e também quantas vezes você precisará escrever esse tipo exato de código. Se a resposta para qualquer uma dessas opções for "muitas", considere seriamente criar um gerador para esse código .

Espero que ajude,
IPP

Ioan Paul Pirau
fonte
Obrigado pela resposta! Como as regras do seu exemplo são implementadas realmente?
Jose Faeti
1
Não posso contar todas as regras, mas posso dar alguns exemplos. Analisamos a interface exposta por um banco de dados oracle e levamos em consideração as assinaturas das funções na interface oracle. Com base na assinatura, geramos o nome da função da camada de dados. sabemos que sempre obtemos do db uma tabela de dados oracle como resultado, que analisamos e salvamos em uma matriz do tipo de objeto especial que usamos para armazenar nossos dados. Além disso, com base nos parâmetros de entrada / saída da assinatura da função db que adicionar correspondentes parâmetros de entrada e de saída para as funções que geram e assim por diante ..
Ioan Paul Pirau
3

Eu tenho um módulo PHP que gera uma página da web que contém código JavaScript que gera HTML. São três camadas bem ali. Rapaz foi tão difícil de ler!

Em uma aula de programação, tivemos que escrever um programa que pegasse uma string de fórmula do usuário e a analisasse e exibisse o valor. O solucionador mais impressionante simplesmente pegou a entrada do usuário, colocou-a em main () {printf ("% d", ...);} e executou um script para compilar, vincular e executá-la. Ele não escreveu um analisador! Hoje você pode fazer isso em uma instrução SQL SELECT.

É uma ferramenta com a qual você deve brincar e depois guardá-la para algum dia futuro, quando for útil.

Andy Canfield
fonte
Na verdade, é a mesma coisa que eu estava tentando implementar! :) Mas então eu decidi codificá-lo com Perl offline e está funcionando muito bem. Estou pensando em adicionar vários recursos!
Jose Faeti 6/09/11
Estou escrevendo código com até 20 camadas de linguagem para transformações de linguagem, sem problemas. Não é mais complicado do que ter uma profundidade de pilha de chamadas de 20 camadas. Por isso, discordo veementemente que é uma ferramenta para " guardá-lo para algum dia futuro em que seja útil " - a geração de código é sempre útil.
SK-logic
3

Eu desenvolvi soluções simples de meta-programação com o Prolog . Onde o aplicativo principal (em C ++, por exemplo) converte uma definição abstrata de um problema em um aplicativo Prolog em tempo de execução, o qual é delegado. Geralmente, escrever funcionalidades equivalentes em C ++ levaria uma eternidade.

Eu acho que esse cenário é um excelente caso a favor do argumento código-escrita-código .

user16410
fonte
3

O que você acha do assunto?

A metaprogramação é mais comumente associada a linguagens não dinâmicas, uma vez que é mais difícil obter determinados comportamentos (como implementar um ORM) sem muitas linhas de código não produtivas e não inteligentes.

Mas mesmo em linguagens mais dinâmicas como PHP, a geração de código pode realmente salvar a vida e aumentar a produtividade em grande quantidade. Nas estruturas modernas, é muito comum ter andaimes que geram a maioria do modelo, formulário, teste e ações comuns para um determinado objeto de negócios que você declara. É uma das razões pelas quais estruturas como o symfony ou o RoR têm tanto sucesso que essas ferramentas de geração de código tornam o código consistente muito rapidamente e aumentam a produtividade dos programadores.

Nos sites, a maior parte da interação gira em torno de quatro ações principais:

  • Crie um elemento
  • Recuperar um conjunto de elementos (com possível filtragem)
  • Atualize um elemento com novos atributos
  • Excluir um conjunto de elementos

Pelo menos tudo o que gira em torno dessas quatro ações principais poderia e deveria ser alcançado usando ferramentas de geração de código para obter a máxima produtividade.

Na minha empresa, usamos o symfony, e seu administrador-gerador é uma ferramenta excepcional, que até gera código em tempo de execução (e o armazena em cache), o que significa que nem precisamos usar nenhum tipo de tarefa ou ferramenta externa para Para gerar um novo código, precisamos apenas limpar nosso cache. Eu aconselho fortemente o uso desse tipo de ferramenta para operações CRUD.

Mas, fazer o que os colaboradores incríveis do symfony fizeram, não é uma tarefa fácil. Eu mesmo implementei algumas tarefas de geração de código e fazer algo verdadeiramente consistente e com uma ampla implementação para cobrir a maioria dos casos extremos não é fácil.

É algo que realmente aumentará sua produtividade?

Acredito que a metaprogramação é muito muito importante em níveis mais baixos de trabalho (estruturas, cache, compiladores etc.), mas algo que devemos abordar com extrema cautela se estivermos fazendo coisas na camada de negócios.

Usar a geração de código é, sem dúvida, um grande impulsionador da produtividade. Implementando suas próprias ferramentas de geração de código, não muito, a menos que você mesmo esteja criando uma estrutura.

Quais são alguns bons recursos sobre o assunto, entre livros, blogs, apresentações de slides, etc?

O melhor recurso para entender a programação é sempre um código fonte bom e bem comentado. Eu diria que analisar os geradores de administração RubyOnRails e Symfony é uma boa idéia.

luisfaceira
fonte
3

Embora muitas respostas aqui se refiram ao que é comumente conhecido como metaprogramação, havia de fato um campo associado à IA conhecido como programação automática que tratava de programas que compreendiam ou sintetizavam programas [1].

Qualquer compilador (ou metaprograma, gerador de código, tradutor, sistema macro, ...) trabalha com transformações, gerando uma saída de uma entrada executando seu algoritmo fixo de transformação. Mas um compilador ou metaprograma tradicional não, dada uma definição, descrição ou exemplo do que é uma classificação (por exemplo, [5, 3, 9] => [3,5,9]), cria um algoritmo de classificação. Tais problemas são do interesse deste campo "programação automática".

[1] - Relatório de andamento dos sistemas de compreensão de programas ftp://db.stanford.edu/pub/cstr/reports/cs/.../CS-TR-74-444.pdf

Thiago Silva
fonte
2

Meta programação pode ser muito difícil de manter. A princípio, parece elegante, mas quando você começa a encontrar casos extremos, os erros são detectados tardiamente (no código que foi gerado) e tudo se torna um pesadelo para usar / depurar.

Eu escrevi principalmente código python e, na minha experiência, a meta programação é sempre uma má escolha com essa linguagem. Você sempre pode refatorar coisas para fazer isso com recursos de idioma normal chatos. O resultado é menos desagradável, mas mais fácil de conviver.

Simon Bergot
fonte
qualquer tipo de código pode ser muito difícil de manter. E pode ser muito fácil se feito da maneira certa. A metaprogramação pode aumentar a manutenção em ordens de magnitude de fato. Sua experiência com python é, provavelmente, irrelevante para a metaprogramação real, pois o Python não é muito adequado para esse modo de pensar, com seu AST desajeitado e profundo demais. Mas mesmo com o Python, usei a biblioteca Tempita com alta eficiência e nunca tive nenhum problema de manutenção, mesmo com uma equipe com quase nenhuma experiência anterior em Python.
SK-logic
Estou interessado no seu ponto de vista sobre python AST. Você já usou tempita para fins de meta programação?
Simon Bergot
este ( docs.python.org/library/ast.html ) é um AST ad hoc e o analisador fornece uma árvore sobrecarregada e não otimizada, o que torna uma análise problemática (especialmente com a falta de correspondência adequada de padrões no Python). Gerar esse AST também não é muito conveniente. Eu usei tempita para produzir código Python e C (isto é, metaprogramação baseada em texto puro), funcionou bem para essa tarefa específica (geração de código padrão). Eu também costumava usar o Python para gerar código C a partir de algumas descrições de alto nível XML.
SK-logic
2

O OP pede recursos.

Você pode achar interessante o nosso DMS Software Reengineering Toolkit . É uma ferramenta de metaprogramação pura, destinada a permitir a criação de ferramentas personalizadas de análise e transformação de programas.

[Para seguir um comentário à pergunta do OP, quando usado para criar uma ferramenta de transformação específica, o DMS é uma linha de produtos que escreve código, que escreve código:]

O DMS consegue isso por ser independente (mas não independente) das linguagens de programação de destino. O DMS fornece os serviços padrão necessários para uma ampla variedade de tarefas de metaprogramação, assim como um sistema operacional fornece uma ampla variedade de serviços para tarefas de programação padrão. Esses serviços incluem análise forte, construção automática de árvores de sintaxe abstact, correspondência de padrões e reescrita em árvores, bibliotecas de tabelas de símbolos que gerenciam facilmente idiomas com regras de escopo desagradáveis, como herança múltipla, fluxo de controle, fluxo de dados, fluxo de dados, apontar e chamar análise de grafos. Nada disso é significativo na ausência de linguagens específicas para processar; portanto, o DMS aceita definições de linguagem que estão vinculadas a essas peças gerais de máquinas, gerando análise específica de idioma, construção AST, correspondência / reescrita de padrão específico de idioma de destino usando o target- sintaxe de idioma,

E, como um sistema operacional, o DMS foi projetado para ter muito poucas opiniões ou restrições sobre quais (meta) programas você deseja escrever, o que significa que pode ser usado para uma ampla variedade de finalidades: extração de métricas, localização de código morto, implementação de tecelões de aspecto, tradução linguagens, gerando códigos a partir de DSLs, pesquisando novamente grandes aplicativos. (O DMS já foi usado para todas essas tarefas).

É necessário ter definições de linguagem robustas, se você não quiser gastar seu tempo codificando tudo no manual de referência do idioma (pense no que isso significa para Java e C ++). O DMS resolve esse problema, disponibilizando uma biblioteca de definições completas de idioma. O analógico aqui é como ter um banco de dados disponível para o seu sistema operacional; você não precisa implementar um deles para continuar escrevendo seu aplicativo centrado no banco de dados.

Ira Baxter
fonte
2

Consulte o conjunto de problemas 4 de Philip Greenspun, no curso 6.916 do MIT: Engenharia de Software de Serviços Web Inovadores ( http://philip.greenspun.com/teaching/psets/ps4/ps4.adp ).

Seu objetivo diz: "Ensine aos alunos as virtudes dos metadados. Mais especificamente, eles aprendem a representar formalmente os requisitos de um serviço da Web e, em seguida, criam um programa de computador para gerar os programas que implementam esse serviço".

Esse é um dos conjuntos de problemas que os potenciais recrutas da ArsDigita ( http://en.wikipedia.org/wiki/ArsDigita ) foram solicitados a resolver durante a primeira bolha.

O livro "SQL for Web Nerds", referências de Philip no pset, foi movido para ( http://philip.greenspun.com/sql/ ).

convencer
fonte
2

Por volta de 2001, comecei a trabalhar em um projeto que fazia uso extensivo de objetos de negócios e objetos de dados. Eu estava construindo o site front-end, mas fiquei desconectado, porque a camada de negócios e a camada de acesso a dados não estavam totalmente desenvolvidas. Depois de algumas semanas disso, comecei a dar uma olhada no que essas camadas estavam fazendo. Basicamente, eles estavam expondo dados retornados de procedimentos armazenados como coleções de objetos com propriedades correspondentes aos campos nos dados ou estavam pegando parâmetros de entrada e enviando-os para procedimentos armazenados para serem salvos nas tabelas do banco de dados. Havia muita serialização / desserialização ocorrendo entre as duas camadas, havia o Microsoft Transaction Server envolvido, uma biblioteca de tipos IDL / ODL ... mas tudo se encaixava em um padrão.

Duas semanas depois, tive um gerador de código elaborado que despejaria o IDL / ODL e também despejaria os objetos de negócios e dados. O cara que construiu os objetos de negócios e da camada de dados foi 2 anos para chegar ao ponto de depurar e testar esses objetos. Em duas semanas, com a geração de código, tivemos a mesma saída, mas, como tudo foi gerado, estava praticamente livre de erros.

Esse gerador de código (ferramenta CASE de nível inferior) me acompanhou por muitas iterações diferentes, por cerca de 8 a 10 anos, porque o princípio era simples: você está fazendo algo que precisa ser feito ao conversar com bancos de dados, é bastante codificação repetitiva e, assim que você acertar, não precisa mais se preocupar com isso.

Então, sim: use um gerador de código, principalmente quando a codificação for repetitiva e se encaixar em um padrão bem definido.

Conheço pessoas que usam macros RegX para fazer coisas semelhantes ou usam fórmulas do Excel para fazer coisas semelhantes (eu também faço isso).

David T. Macknet
fonte
2

Um exemplo de metaprogramação

Eu tenho uma biblioteca de autorização Ruby chamada Authority . Ele permite que os desenvolvedores façam perguntas em seus aplicativos com métodos como current_user.can_read?(@post)e @post.readable_by?(current_user). Essas perguntas são respondidas por classes autorizadas centralizadas.

Esta é a parte crucial: a autoridade não sabe quais métodos definir até ver a configuração do usuário . A configuração do usuário pode conter:

config.abilities =  {
  ...
  :read      => 'readable',
  :microwave => 'microwavable',  # user-defined
  ...
}

Nesse caso, é preciso haver um método parecido current_user.can_microwave?(@post).

A metaprogramação torna isso possível: depois de ler a configuração, eu sei quais métodos definir :

Authority.verbs.each do |verb|
  class_eval <<-RUBY, __FILE__, __LINE__ + 1 # allows for a nice bracktrace
    def can_#{verb}?(resource)
      resource.#{Authority.abilities[verb]}_by?(self)
    end
  RUBY
end
Nathan Long
fonte