Julia: casos de uso para `import` e` using`
Então li a documentação para usingeimport em Julia. No entanto, o que isso não me diz é como devo usar essas duas afirmações na prática (e, dada a falta de ortogonalidade, isso não é muito fácil).
Caso em questão: vamos colocar o seguinte código trivial em "myfile.jl":
module MyModule
f() = 1
export f
end
import .MyModule # or: using .MyModule
se eu usar
importna última linha,fnão será exportado para oMainnamespace. No entanto, quando eu mudo"myfile.jl"(por exemplo, modificando o valor de retorno def) e, em seguida, reinicioinclude, a função é substituída (este é o comportamento que desejo). (Observe que eu poderia explicitamenteimport .MyModule: f, mas isso introduz redundância desnecessária; além disso, o caso da vida real envolverá uma longa lista de funções com nomes longos. OK, também posso escrever uma macro que usanames(Main.MyModule), mas de alguma forma sinto que isso deveria seja mais simples.)se eu substituir
importporusing, isso é invertido:fagora é exportado, mas para alterar qualquer coisa no módulo agora é necessário reiniciar o interpretador Julia.usando
importeusingexporta apenas a primeira versão def()para o namespace principal: quando eu atualizo o código, apenas o primeiro valor de retorno é usado.
Portanto, minha pergunta não é sobre o comportamento de ambas as declarações importe using, que estão documentadas (se não explicadas ) na página vinculada, mas sobre a intenção por trás delas. Por que duas declarações quando uma seria suficiente? Por que um desses ignora todas as exportdiretivas? Em que caso devo, na prática, usar cada afirmação?
(a versão é 1.1.0. Além disso, funciona em um sistema sem fácil Pkgacesso, então não tentei Reviseainda.)
Respostas
A convenção em Julia deve using PackageNameser usada, a menos que você tenha algum motivo específico para fazer o contrário.
Esta decisão de design de linguagem é motivada simplesmente pelo conforto dos usuários. Julia está usando várias funções de envio e, normalmente, pacotes de exportação de funções que operam em argumentos digitados (no entanto, geralmente são abstracttipos, portanto a funcionalidade das funções não é limitada).
Com esse design, todas as funções e métodos que os implementam podem estar no mesmo namespace, uma vez que diferem nos tipos de parâmetro que são específicos para seus pacotes. Portanto, um cenário em que usingtraz um pacote para o namespace que substitui as implementações de método existentes é muito raro (e então Julia relata um aviso). Esse processo é controlado adicionalmente pelos autores do pacote por meio do uso da exportpalavra - chave.
Acho que parece estranho para você porque está acostumado com linguagens como Python. Por exemplo, numpytem exatamente os mesmos nomes de função que as funções padrão e não há uma boa maneira de nomeá-los de forma diferente (por exemplo, tente pensar qual seria uma boa alternativa para uma função chamada sin:-)). Portanto, em Python, você sempre precisa importar funções para seus próprios namespaces, talvez com um alias como import numpy as np. Depois de ter vários anos de experiência com os dois idiomas, eu diria que a maneira como Julia faz isso é natural e a maneira Pytoniana é estranha :-)
Em relação às importpossibilidades de trazer funções únicas de um pacote para o seu namespace, vejo isso como uma solução alternativa para situações em que ocorre um conflito de nomes. Além disso, você precisa fazer isso explicitamente se quiser adicionar novas implementações de método a algum outro pacote (como definir um novo significado para o +operador). Fora isso, basta digitar usinge nunca mais olhar para trás.
Uma coisa a acrescentar, além da resposta de Przemyslaw Szufel. Quando você está estendendo um método, é possível fazer isso sem usar importpara a função que você está estendendo. Por exemplo, se eu quiser estender a fpartir do módulo Foo, posso fazer using Fooe definir um novo método para Foo.f. Aqui está um exemplo completo:
module Foo
struct A
x::Int
end
f(a::A) = a.x + 1
export A, f
end
using .Foo
struct B
x::Int
end
Foo.f(b::B) = b.x + 2
A vantagem dessa abordagem é que, ao ler o código, é mais fácil ver a origem da função que você está estendendo.