Você simplificou demais a declaração de Guido ao formular sua pergunta. O problema não é escrever um compilador para um idioma digitado dinamicamente. O problema é escrever um que esteja (critério 1) sempre correto, (critério 2) mantenha a digitação dinâmica e (critério 3) seja visivelmente mais rápido para uma quantidade significativa de código.
É fácil implementar 90% (falha do critério 1) do Python e ser consistentemente rápido nisso. Da mesma forma, é fácil criar uma variante Python mais rápida com tipagem estática (falha no critério 2). A implementação de 100% também é fácil (na medida em que é fácil implementar uma linguagem complexa), mas até agora todas as maneiras fáceis de implementá-la acabam sendo relativamente lentas (falha nos critérios 3).
A implementação de um intérprete mais o JIT correto, implementa o idioma inteiro e é mais rápido, pois alguns códigos são viáveis, embora significativamente mais difíceis (cf. PyPy) e somente se você automatizar a criação do compilador JIT (a Psyco fez sem ele , mas era muito limitado em qual código poderia acelerar). Mas observe que isso está explicitamente fora do escopo, pois estamos falando de estática(aka antecipadamente) compiladores. Menciono apenas isso para explicar por que sua abordagem não funciona para compiladores estáticos (ou pelo menos não há contraexemplo): primeiro ele precisa interpretar e observar o programa e gerar código para uma iteração específica de um loop (ou outro código linear). path) e, em seguida, otimize o inferno com base em suposições válidas apenas para essa iteração específica (ou pelo menos para todas as iterações possíveis). A expectativa é que muitas execuções posteriores desse código também correspondam à expectativa e, portanto, se beneficiem das otimizações. Algumas verificações (relativamente baratas) são adicionadas para garantir a correção. Para fazer tudo isso, você precisa ter uma idéia do que se especializar e uma implementação lenta, mas geral, para a qual voltar. Os compiladores AOT não têm. Eles não podem se especializar em tudocom base no código que eles não podem ver (por exemplo, código carregado dinamicamente) e especializar-se descuidadamente significa gerar mais código, o que apresenta vários problemas (utilização do icache, tamanho binário, tempo de compilação, ramificações adicionais).
A implementação de um compilador AOT que implemente corretamente o idioma inteiro também é relativamente fácil: Gere código que chama no tempo de execução para fazer o que o intérprete faria quando alimentado com esse código. Nuitka (principalmente) faz isso. No entanto, isso não gera muitos benefícios de desempenho (falha no critério 3), pois você ainda precisa fazer o mesmo trabalho desnecessário que um intérprete, exceto para despachar o bytecode para o bloco de código C que faz o que você compilou. esse é apenas um custo bastante pequeno - significativo o suficiente para valer a pena otimizar em um intérprete existente, mas não significativo o suficiente para justificar uma implementação totalmente nova com seus próprios problemas.
O que seria necessário para cumprir todos os três critérios? Nós não temos ideia. Existem alguns esquemas de análise estática que podem extrair algumas informações sobre tipos concretos, fluxo de controle etc. dos programas Python. Os que produzem dados precisos além do escopo de um único bloco básico são extremamente lentos e precisam ver o programa inteiro, ou pelo menos a maior parte dele. Ainda assim, você não pode fazer muito com essas informações, além de talvez otimizar algumas operações nos tipos internos.
Por que isso? Para ser franco, um compilador remove a capacidade de executar o código Python carregado no tempo de execução (falha no critério 1) ou não faz nenhuma suposição que possa ser invalidada por qualquer código Python. Infelizmente, isso inclui praticamente tudo útil para otimizar programas: Globals incluindo funções podem ser rebote, classes podem ser mutado ou inteiramente substituídos, os módulos podem ser modificadas arbitrariamente também, a importação pode ser sequestrado em diversas maneiras, etc. A única corda passada para eval
, exec
, __import__
ou inúmeras outras funções, pode fazer nada disso. Com efeito, isso significa que quase nenhuma otimização grande pode ser aplicada, gerando pouco benefício de desempenho (critérios com falha 3). Voltar ao parágrafo acima.