Quais verificações de tipo o Raku realiza em tempo de compilação? Isso pode mudar no futuro?

Aug 19 2020

Atualmente (em agosto de 2020), Rakudo não verifica os valores de retorno das funções em tempo de compilação; ou seja, ele não fornece garantias estáticas de que as funções satisfazem suas restrições de retorno. Concretamente, as duas funções a seguir compilam como Raku:

sub get-int(--> Int) { 'bug' }
sub get-int($a --> Int} { when $a == 5 { 'Rare bug' }
   default      { 42 }
}

Tenho duas perguntas relacionadas:

  1. Existe alguma maneira de saber o que (se houver) verificação de tipo ocorre atualmente em tempo de compilação? (Por meio de uma lista que alguém escreveu, em algum lugar nos documentos ou em um local central na fonte Rakudo) Ou é mais ad hoc do que isso?

  2. Esta falta de tempo de compilação para verificação de tipo é uma decisão de design intencional? Ou adicionar mais verificação de tipo estática é algo que seria bom ter um dia, mas ainda não foi implementado?

(Estou familiarizado com a ótima resposta de Johnathan para As penalidades de desempenho para tipos / restrições em Raku?, Que afirma que "Raku exige que as restrições de tipo escritas no programa sejam aplicadas no tempo de execução, no máximo ." Essa resposta descreve várias maneiras de evitar a execução - custos de tempo de verificação de tipo, mas não descreve quais, se houver, verificação de tipo são feitas em tempo de compilação (o que certamente evitaria custos de tempo de execução!).

Respostas

12 JonathanWorthington Aug 19 2020 at 12:04

Atualmente, muito pouca verificação de tipos é feita em tempo de compilação; o que ocorre principalmente como um efeito colateral do otimizador estático. As verificações hoje são em grande parte sobre chamadas de sub-rotina, onde:

  • Podemos determinar a aridade da chamada e saber que o número de argumentos passados ​​nunca corresponderá
  • Temos argumentos literais e podemos ver que eles nunca corresponderiam à assinatura

Isso é uma sobra de quando o otimizador estático fazia mais trabalho inlining. Hoje em dia, ele apenas inline operadores nativos em tempo de compilação e deixa o resto para o otimizador dinâmico da VM, que é muito mais capaz de inlining e também pode uninline (permitindo otimização especulativa, mas também significando que os rastreamentos de pilha originais podem ser recuperados, enquanto o o otimizador estático perdeu esta informação).

Fazer mais em tempo de compilação é considerado desejável; no entanto, existem algumas questões práticas a serem consideradas.

  1. A introdução de verificações adicionais também pode causar quebra de código que funcionava antes. Considere um módulo com um caminho de código que falharia em uma verificação de tempo de compilação mais rígida, mas que está sendo usado em sistemas que nunca são executados nesse caso. Se ele começou a falhar ao compilar em versões mais novas do compilador, seria impossível implantar esse sistema após uma atualização do compilador. Em geral, isso significa que as verificações realizadas devem mudar nas mudanças de versão do idioma. (Isso ainda significa que as pessoas devem declarar a versão da linguagem na qual estão escrevendo ao escrever o código.)
  2. Que mais verificações sendo feitas em tempo de compilação "certamente evitarão custos de tempo de execução" pode ser verdade, mas não é trivial pensar nisso. Um tempo de execução gerenciado não pode confiar cegamente nas promessas feitas no bytecode que é fornecido, uma vez que isso pode levar a violações de segurança de memória (que levam a SIGSEGV ou pior). Isso é claramente verdadeiro em uma linguagem como Raku, em que a semântica da verificação de tipo é programável, mas é verdade na JVM, CLR e assim por diante. As maiores vitórias relacionadas ao tipo no Raku vêm do uso de tipos nativos, o que pode evitar muitas alocações e, portanto, o trabalho de coleta de lixo.
  3. A implementação de verificações adicionais aumentará a complexidade do compilador e também a quantidade de tempo necessária para a compilação. O primeiro deles já é um problema; o frontend do compilador não viu nenhuma mudança arquitetônica significativa em cerca de uma década. O trabalho atual do RakuAST que estabelece uma base para macros também envolve uma quase reescrita da interface do compilador. A arquitetura aprimorada deve facilitar a implementação de mais verificações de tipo de tempo de compilação, mas o pensamento também está indo para como os aspectos da compilação podem ser paralelizados, o que pode permitir que o compilador faça mais sem aumentar o tempo de compilação do wallclock.

Uma vez que a revisão do frontend do compilador atual esteja completa, mais verificações de tempo de compilação sendo introduzidas (mas apenas habilitadas na próxima versão do idioma) parece bastante provável - pelo menos, desde que alguém trabalhe nisso.

No entanto, há uma oportunidade ainda mais empolgante surgindo nesta área: como haverá uma API para programas Raku, e com planos surgindo para passes de compilador personalizados, em breve também será possível implementar verificadores de tipo como módulos ! Alguns deles podem levar a verificações que fazem parte das futuras versões do idioma Raku. Outros podem ser bastante específicos do domínio e destinados a permitir o uso mais correto de um determinado módulo. Outros podem impor rigores que não estão no espírito do idioma base, mas que alguns usuários do idioma podem optar por aderir.