Existe alguma maneira de exigir explicitamente em Julia (por exemplo, dentro de um módulo ou pacote) que tipos devem ser declarados ? Por exemplo, PackageCompiler
ou Lint.jl
tem algum suporte para tais verificações? Em termos mais gerais, a própria distribuição padrão Julia fornece algum analisador de código estático ou equivalente que possa ajudar a verificar esse requisito?
Como um exemplo motivador, digamos que queremos garantir que nossa base crescente de códigos de produção aceite apenas códigos sempre declarados por tipo , sob a hipótese de que grandes bases de código com declarações de tipo tendem a ser mais sustentáveis.
Se queremos impor essa condição, Julia em sua distribuição padrão fornece algum mecanismo para exigir declaração de tipo ou ajudar a avançar esse objetivo? (por exemplo, algo que possa ser verificado através de linters, ganchos de confirmação ou equivalente?)
fonte
hasmethod(f, (Any,) )
retornaráfalse
se nenhum genérico tiver sido definido. Você ainda precisará corresponder ao número de argumentos (hasmethod(f, (Any,Any) )
por exemplo, para uma função de dois argumentos).Respostas:
A resposta curta é: não, atualmente não há ferramentas para verificar o seu código Julia. No entanto, é possível em princípio, e algum trabalho foi feito nessa direção no passado, mas não há uma boa maneira de fazê-lo agora.
A resposta mais longa é que as "anotações de tipo" são um arenque vermelho aqui, o que você realmente deseja é a verificação de tipo; portanto, a parte mais ampla da sua pergunta é realmente a pergunta certa. Posso falar um pouco sobre por que as anotações de tipo são um arenque vermelho, algumas outras coisas que não são a solução certa e como seria o tipo certo de solução.
Exigir anotações de tipo provavelmente não realiza o que você deseja: basta colocar
::Any
qualquer campo, argumento ou expressão e teria uma anotação de tipo, mas não uma que informe a você ou ao compilador algo útil sobre o tipo real dessa coisa. Ele adiciona muito ruído visual sem adicionar nenhuma informação.Que tal exigir anotações de tipo concreto? Isso exclui apenas colocar
::Any
tudo (que é o que Julia implicitamente faz de qualquer maneira). No entanto, existem muitos usos perfeitamente válidos de tipos abstratos que isso tornaria ilegal. Por exemplo, a definição daidentity
função éQue anotação de tipo concreto você aplicaria
x
sob esse requisito? A definição se aplica a qualquer umx
, independentemente do tipo - esse é o objetivo da função. A única anotação de tipo que está correta éx::Any
. Isso não é uma anomalia: existem muitas definições de funções que exigem tipos abstratos para serem corretas, portanto, forçar aqueles a usar tipos concretos seria bastante limitante em termos de que tipo de código Julia se pode escrever.Há uma noção de "estabilidade de tipo" que é frequentemente mencionada em Julia. O termo parece ter se originado na comunidade Julia, mas foi escolhido por outras comunidades de linguagem dinâmica, como R. É um pouco complicado de definir, mas significa aproximadamente que, se você conhece os tipos concretos dos argumentos de um método, você conhece o tipo de seu valor de retorno também. Mesmo que um método seja do tipo estável, isso não é suficiente para garantir que ele digite check, porque a estabilidade do tipo não fala sobre nenhuma regra para decidir se algo do tipo verifica ou não. Mas isso está indo na direção certa: você gostaria de poder verificar se cada definição de método é do tipo estável.
Muitos não desejam exigir estabilidade de tipo, mesmo que possam. Desde Julia 1.0, tornou-se comum o uso de pequenos sindicatos. Isso começou com o redesenho do protocolo de iteração, que agora usa
nothing
para indicar que a iteração é feita versus o retorno de uma(value, state)
tupla quando houver mais valores para iterar. Asfind*
funções na biblioteca padrão também usam um valor de retorno denothing
para indicar que nenhum valor foi encontrado. Essas são instabilidades do tipo tecnicamente, mas são intencionais e o compilador é muito bom em raciocinar sobre a otimização em torno da instabilidade. Portanto, pelo menos pequenas uniões provavelmente devem ser permitidas no código. Além disso, não há um lugar claro para traçar a linha. Embora talvez se possa dizer que um tipo de retorno deUnion{Nothing, T}
é aceitável, mas não é algo mais imprevisível do que isso.O que você provavelmente realmente deseja, no entanto, em vez de exigir anotações de tipo ou estabilidade de tipo, é ter uma ferramenta que verifique se seu código não pode gerar erros de método, ou talvez de maneira mais ampla, que não gere nenhum tipo de erro inesperado. O compilador geralmente pode determinar com precisão qual método será chamado em cada site de chamada ou pelo menos reduzi-lo a alguns métodos. É assim que gera código rápido - o envio dinâmico completo é muito lento (muito mais lento que o vtables em C ++, por exemplo). Se você escreveu código incorreto, por outro lado, o compilador pode emitir um erro incondicional: o compilador sabe que você cometeu um erro, mas não informa até o tempo de execução, pois essas são as semânticas do idioma. Pode-se exigir que o compilador seja capaz de determinar quais métodos podem ser chamados em cada site de chamada: isso garantiria que o código fosse rápido e que não houvesse erros de método. É isso que uma boa ferramenta de verificação de tipo para Julia deve fazer. Há uma excelente base para esse tipo de coisa, já que o compilador já faz grande parte desse trabalho como parte do processo de geração de código.
fonte
Esta é uma pergunta interessante. A questão principal é o que definimos como tipo declarado . Se você quer dizer que existe uma
::SomeType
declaração em cada definição de método, é um pouco complicado, pois você tem diferentes possibilidades de geração dinâmica de código em Julia. Talvez exista uma solução completa nesse sentido, mas eu não a conheço (eu adoraria aprender).O que me vem à mente, porém, que parece relativamente mais simples é verificar se algum método definido dentro de um módulo aceita
Any
como argumento. Isso é semelhante, mas não equivalente à declaração anterior, como:procure o mesmo para a
methods
função que a assinatura de ambas as funções aceitax
comoAny
.Agora, para verificar se algum método em um módulo / pacote aceita
Any
como argumento para qualquer um dos métodos definidos nele, algo como o código a seguir poderia ser usado (eu não o testei extensivamente porque acabei de escrevê-lo, mas parece cobrir possíveis casos):Agora, quando você o executa no
Base.Iterators
módulo, você obtém:e quando você verifica o pacote DataStructures.jl, obtém:
O que proponho não é uma solução completa para sua pergunta, mas achei útil para mim, então pensei em compartilhá-la.
EDITAR
O código acima aceita
f
serFunction
apenas. Em geral, você pode ter tipos que podem ser chamados. Em seguida, acheck_declared(m::Module, f::Function)
assinatura pode ser alterada paracheck_declared(m::Module, f)
(na verdade, a própria função permitiriaAny
como o segundo argumento :)) e passar todos os nomes avaliados para essa função. Então você teria que verificar semethods(f)
há positivolength
dentro da função (comomethods
para retornos não exigíveis um valor que tenha comprimento0
).fonte