Da lista de discussão:
Stefan:
Acho que deveríamos acabar com a importação e apenas ter
usando Foo
usando Foo: barO primeiro carregaria e criaria uma ligação para Foo, tornaria suas exportações disponíveis como ligações "soft" (o que using faz agora). O segundo também carregaria e criaria uma ligação para Foo e tornaria a barra disponível como uma ligação "difícil" (o que a importação faz agora)."
Parece que isso ainda é um ponto de confusão para os recém-chegados.
Também precisaríamos da funcionalidade import Foo
de alguma forma, onde você apenas obtém o Foo
e nada mais
using Foo:
?
using Foo: Foo
?
Para vincular Foo
ao módulo Foo (e nada mais):
import Foo
Para vincular Foo
ao módulo Foo, x
a Foo.x, y
a Foo.y
import Foo: x, y
Para vincular Foo
ao módulo Foo, e todos os nomes exportados de Foo são vinculados sem qualificação:
import Foo: *
Isso poderia ser using
, mas eu sinto que isso está mais no espírito de import
.
Isso também remove a distinção entre trazer algo para o escopo e disponibilizá-lo para extensão de método. Pessoalmente, acho que isso é uma coisa boa e torna o sistema de módulos mais compreensível, mas queria ter certeza de que trouxemos isso à tona.
Há um forte argumento a ser feito de que uma construção que disponibiliza todas as ligações exportadas de um módulo deve torná-las ligações suaves ( using
) e não ligações rígidas ( importall
). Suponha que o módulo A use o módulo B e defina foo(::Any)
. Se uma versão posterior do módulo B também definir e exportar foo(::Any)
, você não quer que eles se sobreponham. Você também não quer que o módulo B defina foo(::Int)
e tenha o módulo A às vezes chamando esse método em vez do método definido porque é mais específico, ou ter que listar todos os identificadores que você deseja do módulo B para evitar a importação de um único identificador conflitante.
Mas se você listou explicitamente os identificadores que deseja importar, nunca há uma razão para fornecer uma ligação suave. Ou você não vai definir novos métodos de um identificador, caso em que hard e soft binding têm comportamento idêntico, ou você vai definir novos métodos dele, nesse caso um soft binding é equivalente a nenhum binding e se você quiser esse comportamento, você deve apenas remover o identificador da lista.
Então, resumindo, eu gosto da proposta de @StefanKarpinski . Conceitualmente, precisamos de ligações rígidas e flexíveis, mas podemos obter todos os comportamentos úteis com uma única palavra-chave.
Eu vejo o seu ponto. Nesse caso, gosto da sua proposta de que using Foo:
(com os dois pontos, mas sem itens) parece conceitualmente consistente. Os dois-pontos indicam que você vai limitar quais símbolos usar, mas você simplesmente não lista nenhum.
O :
vazio à direita parece um pouco engraçado, mas acho que as pessoas se acostumariam com isso
O problema com os dois pontos vazios à direita é que atualmente procuramos um nome na próxima linha – em outras palavras using Foo:
é considerado incompleto.
Relacionado: #4600
Eu sei que a prática atual de Julia é importar tudo para o
namespace do módulo atual, mas eu realmente acho que ainda deve haver
ser uma maneira simples e natural de importar apenas o nome de um módulo.
Eu também acho que a sobrecarga atual de usar Foo e usar Foo.Bar é
problemático; ele parece dentro do Foo mas não dentro do Foo.Bar (a menos que o Foo.Bar seja
um módulo?)
Acho que na proposta de Stefan, isso é abordado com
usando Foo: Foo
Na quinta-feira, 14 de agosto de 2014, toivoh [email protected] escreveu:
Eu sei que a prática atual de Julia é importar tudo para o
namespace do módulo atual, mas eu realmente acho que ainda deve haver
ser uma maneira simples e natural de importar apenas o nome de um módulo.Eu também acho que a sobrecarga atual de usar Foo e usar Foo.Bar é
problemático; ele parece dentro do Foo mas não dentro do Foo.Bar (a menos que o Foo.Bar seja
um módulo?)—
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/JuliaLang/julia/issues/8000#issuecomment -52202142.
@kmsquire , mas o que acontece se houver um módulo Foo dentro do módulo Foo? Isso é, infelizmente, ambíguo.
Isso é um erro de design (e causa um aviso), então não importa
Eu gosto da versão proposta por @ssfrr acima do melhor.
Existe uma convenção subjacente de que os pacotes têm um e apenas um ponto de entrada de módulo? -- Para import Foo
significa importar o módulo Foo
do pacote Foo
. Qualquer outro módulo deve ser um submódulo de Foo
. O que significa que há uma correspondência implícita entre o pacote Foo, Foo.jl
no pacote e o module Foo
dentro dele.
Eu pergunto b/c estou querendo saber se import
também poderia ser usado para importação local/relativa. Digamos que um projeto tenha src/foo.jl
e src/bar.jl
então em foo.jl
:
import bar
importaria de src/bar.jl
. Esta é uma melhoria em relação ao uso include
porque opera dentro do sistema de módulos.
Sim, espera-se que os pacotes, pontos de entrada de pacotes e módulos sejam um para um. Você pode quebrar essa convenção se precisar, mas não há muita necessidade disso, pois os módulos são apenas para nomear e não são unidades funcionais. A ideia que você está trazendo é #4600, exceto que a sintaxe para uma importação relativa é import .bar
enquanto import bar
é uma importação absoluta.
@StefanKarpinski O import .bar
realmente procura por "bar.jl" no diretório local? Fiquei com a impressão de que import .Bar
se referia apenas a um submódulo de um módulo pai já atual.
ATUALIZAÇÃO: Duh, isso é o que você propõe em #4600. Desculpe.
:+1:. Se vamos fazer isso, 0,4 seria um bom momento para fazê-lo.
:+1: Por favor, vá em frente e limpe um pouco disso (para 0,4!)
em vez de ter dois mecanismos de importação para distinguir entre estender ou não, por que não sinalizar a extensão no próprio site de extensão por meio de uma palavra-chave ou anotação na definição da função, por exemplo, substituir palavra-chave antes da função? Semelhante à anotação @Override
em Java.
Isso tem a vantagem de ver claramente que a função está substituindo outra (e, portanto, é um método)
Isso já é possível, @ssagaert. Você faz isso escrevendo explicitamente o nome do módulo na definição da função (por exemplo, Base.print(…) = …
), e parece ser um estilo para o qual muitas pessoas estão convergindo. O único ponto de discórdia é que a sintaxe não funciona para todos os nomes possíveis (como .+
, etc.).
(Como um aparte, tenha cuidado ao colocar macros com backticks `` para evitar incomodar outros usuários do GitHub).
:+1: para @ssagaert sugestão de usar @override
, a mensagem de erro original quando se tenta estender um método sem importá-lo primeiro é assim:
ERROR: error in method definition: function Foo.x must be explicitly imported to be extended
Então talvez @extend
poderia ser mais adequado? Eu não sou um falante nativo de inglês, mas meu entendimento é que _override_ significa algo como: cancelar, anular, invalidar, negar, anular, anular, descontinuar, etc.
Acho que isso é mais explícito:
<strong i="13">@extend</strong> Base.show(...) = ...
Do que:
import Base: show
# ... several lines here
Base.show(...) = ...
@Ismael-VC Base.show(...) = ...
já funciona sem importar nada. import
só é necessário se você quiser que show(...) = ...
estenda Base.show
.
A palavra de substituição @Ismael-VC foi apenas uma sugestão. Pode ser estender ou qualquer outra coisa significativa. Também não deve haver @
já que em julia isso significa que é uma macro ( @Override
se refere a Java onde é uma anotação).
@simonster obrigado eu não sabia disso!
@ssagaert então você quer dizer uma palavra-chave? Eu tentei algo assim, mas ainda sou péssimo em macro-foo:
module Extend
export <strong i="9">@extend</strong>
macro extend(x)
mod = x.args[1].args[1].args[1]
met = x.args[1].args[1].args[2]
imp = :(Expr(:import, $mod, $met))
:(Expr(:toplevel, $imp, $(esc(x))))
end
end
Eu sei que isso não é geral, ainda assim não consigo fazer uma expressão que retorne o que o parse faz:
julia> using Extend
julia> type Foo end
julia> <strong i="13">@extend</strong> Base.show(x::Foo) = Foo
:($(Expr(:toplevel, :($(Expr(:import, Base, :show))), show)))
julia> parse("import Base.show; Base.show(x::Foo) = Foo")
:($(Expr(:toplevel, :($(Expr(:import, :Base, :show))), :(Base.show(x::Foo) = begin # none, line 1:
Foo
end))))
Acho que uma macro @extend
geral e funcional não mudaria a semântica do mecanismo de importação.
@Ismael-VC eu fiz, mas também gosto do 'truque' do @mbauman .
Eu acho que pode ser bom poder usar .
sozinho para significar "isto". para que você possa escrever expressões como:
import Base: .
(significa importar Base.Base
)
ou
using ..
tenho certeza de que isso exigiria https://github.com/JuliaLang/julia/pull/11891#issuecomment -116098481 no entanto. talvez seja suficiente permitir espaços antes do .
, mas não depois dele, para resolver o caso de ambiguidade?
Eu acredito que require já está obsoleto. Pode ser bom adicionar uma notação de ponto mais flexível na importação de módulos por 1.0, mas duvido que vamos mudar alguma coisa aqui pelo congelamento de recursos 0.6
Depois de muita discussão sobre isso ontem, estou inclinado a algo como as propostas em https://github.com/JuliaLang/julia/issues/8000#issuecomment -52142845 e https://github.com/JuliaLang/julia/ issues/8000#issuecomment -52143609:
using A: x, y # hard imports x and y from A
using A: A # hard imports just the identifier `A`
using A: ... # soft imports all of A's exports
using A # equivalent to `using A: A, ...`
using A.B # A.B must be a module. equivalent to `using A.B: B, ...`
using A: ..., thing1, thing2 # import all exports plus some non-exported things
A alternativa seria manter import
em vez de using
:
import A # hard binding for the module `A`
import A: ... # soft bindings for all names exported by `A`
import A: x, y # hard bindings for `x` and `y` from `A`
import A: x, y, ... # equivalent to doing both of the previous two
A regra geral para determinar se uma vinculação é hard ou soft seria simples: qualquer nome explicitamente solicitado é uma hard binding, qualquer vinculação dada implicitamente é soft.
Edit: há algum desejo de adicionar a este esquema algum atalho para import A; import A: ...
que é aproximadamente o que using A
faz atualmente (a única diferença é que using A
atualmente importa A
); isso pode continuar a ser using A
ou import A...
foi proposto.
Sim, acho que também é uma boa proposta. Basicamente se resume a se alguém prefere using
ou import
.
Adendo, poderíamos manter using A
como um atalho para import A; import A: ...
- juntamente com a eliminação do comportamento atual de que cada módulo tem uma ligação exportada para si mesmo, e é por isso que using A
causa que haja uma ligação suave para A
disponível.
Eu ficaria muito desapontado se ainda tivéssemos várias palavras-chave depois de tudo isso.
Eu gosto da simetria de import
e export
. (Como alguém apontou em algum lugar.)
Sempre fazer "hard bindings" não parece o padrão mais seguro em termos de extensão de método. Havia a proposta relacionada, mas um tanto separada, de exigir qualificação de módulo para extensão de método que poderia remover a necessidade de "vinculações rígidas".
Ligações rígidas também são necessárias para esta finalidade:
import Package: x
x = 1 # gives an error
E, crucialmente, essa proposta nem sempre faria ligações rígidas --- apenas para coisas que você lista explicitamente. Exigir que se diga import
em vez de using
não adiciona muita segurança.
A segurança vem da maioria dos lugares que não estão fazendo extensão de método que pode sair bem com uma ligação suave. Acho que ainda devemos ter uma maneira de solicitar uma ligação suave específica sem ter que importar todas as exportações de um pacote (ou obter uma ligação suave específica que não é exportada).
Ainda temos o aviso para substituir uma ligação importada?
Acho que exigir a qualificação do módulo é uma boa ideia. Agora mesmo para saber se uma função está sendo introduzida ou um método estendido você tem que olhar todo o conteúdo do pacote para uma instrução import A: func
.
Ainda temos o aviso para substituir uma ligação importada?
Eu acredito que é realmente um erro agora.
Há outra proposta circulando que mantém as duas palavras-chave, mas ainda simplifica um pouco as coisas:
import A: ...
using A:
using A.B
, exija que A.B
seja um módulo e documente que é uma abreviação de import A.B; import A.B: ...
.Dessa forma, tudo pode ser feito com apenas import
, mas using X
está disponível por conveniência. Isso também seria especialmente fácil de fazer a transição.
BTW, using
parece inconsistente como postei aqui . Se mantivermos using
, ele deve ser renomeado para use
se possível.
Acho que devemos exigir a quantificação do módulo ao estender funções, pois seu significado é muito mais claro do que o padrão de importação e extensão.
Minha abordagem favorita:
using A: A
import
e o tipo de vinculação que ele criaCoisas que precisariam acontecer para implementar https://github.com/JuliaLang/julia/issues/8000#issuecomment -327512355:
using A
para hard import A
using A: x
using A.x
onde x
não é um submóduloimport A.x
onde x
não é um submódulo...
para import
using A: x
é usado com frequência e é muito útil. Você está dizendo que deseja x
em seu namespace, mas não deseja estendê-lo. Em import A: x
, você está dizendo que deseja estender x
. Há uma distinção significativa entre ter uma função disponível para uso e ser capaz de estendê-la.
Pensando bem, eu diria que o maior problema aqui é que using A.B
faz duas coisas: se B
é um módulo, ele importa suavemente todas as suas exportações e, caso contrário, apenas importações suaves B
. Acho que devemos apenas corrigir isso, e tornar using A.B
permitido apenas para módulos, e ter using A: a, b
para importação suave de ligações específicas, uma de cada vez.
Eu preferiria que houvesse uma maneira de escrever import A: x
em vez de ser equivalente a import A.x
.
Eu voto em import A: x
já que também podemos; import A: x, y, @z
mas import A.x, A.y, a.@z
ficaria feio.
Isso ser removido de 1.0 significa que ficaremos com using
e import
para 1.0? Isso é um pouco lamentável na minha opinião.
E quanto a:
using A
torna-se import A: ...
using A.X
( X
é um módulo) torna-se importação A.X: ...
using A: X
( X
não é um módulo) torna-se import A: X
import A: X
não é alterado, mas você não pode estender automaticamente X
(veja o primeiro ponto)using
Estou perdendo algum caso de uso? Talvez isso já tenha sido sugerido...
O que eu gosto de ser explícito sobre o módulo ao estender é que a extensão se torna muito mais local. Neste momento, quando um método é estendido, é comum colocar o import bem próximo ao topo do módulo (que pode estar em um arquivo completamente diferente!). Quando o método estendido é excluído, a instrução de importação geralmente é esquecida.
Acho que é essencialmente o mesmo que a minha proposta acima, que teve bastante apoio. @JeffBezanson realmente quer manter using A
pelo menos para facilitar o uso e using A: x
porque aparentemente (não estou convencido disso), é um caso de uso importante poder importar uma ligação em de tal forma que você não pode estendê-lo. Existem algumas propostas para ir na outra direção e substituir import
por using
mas nenhuma delas realmente teve muita tração ( import
parece mais fundamental).
Acho que a diferença está em:
x
e y
de A
Assumindo que o significado de "hard binding" é tal que você pode estendê-lo sem prefixo de módulo, na minha versão não há hard bindings. Se você quiser estender, seu módulo prefixa exatamente onde você o estende. Nenhuma instrução import
assustadora em outros arquivos alterando o significado se algo é uma extensão ou não.
e
using A: x
porque aparentemente (não estou convencido disso), é um caso de uso importante poder importar uma ligação de forma que você não possa estendê-la.
Forçar o prefixo do módulo não está lidando com isso? Ou estamos falando de não módulos como:
module M
x = 1
end
Ambos import M: x; x = 2
e using M: x; x = 2
dão a mesma mensagem de aviso, então não vejo qual é o problema...
Manter using A
para facilitar mais de import A: ...
parece um pouco excessivo na minha opinião.
Forçar o prefixo do módulo não está lidando com isso?
Sim; se você tivesse que qualificar funções para estendê-las, esse ponto seria irrelevante.
Continuar usando A para facilitar a importação A: ... parece um pouco excessivo na minha opinião.
Eu vejo de maneira oposta; fazer as pessoas mudarem de using A
(que é bom e curto e estamos todos acostumados) para import A: ...
apenas para satisfazer um requisito artificial de que haja apenas 1 palavra-chave é excessivo.
A partir da leitura do thread, parece que o principal valor em ter duas palavras-chave é diferenciar as ligações que podem ser estendidas ou não (ligação rígida). Com isso em mente, acho que existem duas soluções viáveis:
using
e extending
neste caso. import
é bom, mas extending
torna óbvia a razão para a existência de uma segunda palavra-chaveEm ambos os casos, sugiro que using
seja como está agora com a adição de um dos seguintes para vincular apenas o módulo Foo
:
using Foo: nothing
(funciona agora)using Foo: Foo
(funciona agora)using Foo:
(pode ser adicionado mais tarde)Então extending
deve se comportar de forma idêntica a using
com a única diferença é que você pode estender ligações trazidas com extending
e possivelmente não permitir extending Foo
para que ele tenha ser explícito.
| | disponibilizar (usando) | tornar extensível (importação)|
| ------------------- | -------------------------- | ---------------------- |
| único módulo | using module: module
ou using module: nothing
| import module
|
| tudo exportado | using module
(efeito colateral: age como import module
também) | ? |
| coisas particulares | using module: x,y
| import module: x,y
|
| | disponibilizar (usando) | tornar extensível (importação) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module
| import module
|
| tudo exportado | using module: *
| import module: *
|
| coisas particulares | using module: x,y
| import module: x,y
|
O bom disso é que importar mais corresponde a escrever mais. Ou seja, você começa com using module
e se quiser importar uma variável diretamente para o namespace você adiciona um : x
em vez de remover um nothing
ou module
. Isso também significa que a coisa mais curta que você digita inclui o mínimo.
Você também pode fazer using: *,x
para disponibilizar tudo o que foi exportado e x
que não foi exportado.
| | disponibilizar (usando) | tornar extensível (importação) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module:
| import module:
|
| tudo exportado | using module: *
| import module: *
|
| coisas particulares | using module: x,y
| import module: x,y
|
mantenha em torno using module
e import module
com o comportamento atual para compatibilidade com versões anteriores, mas deprecie-o.
@FelixBenning : import Module
atualmente não (por si só) torna nada extensível mais do que using Module
, apenas carrega o código e traz Module
(e nada mais) para o namespace .
Apenas para espelhar o que eu disse no slack e para que ele não desapareça no slack hole:
Eu não acho que tornar using X: *
o padrão para disponibilizar todas as coisas exportadas versus apenas using X
tornará as pessoas necessariamente mais cautelosas com o que importam. Eu sei, apontar para como os outros fazem isso é considerado ruim, mas o Python basicamente tem essa semântica com seus import X
e import X: *
, mas seu ecossistema está repleto dessas importações de estrelas 🤷♂️ (e eles estão notoriamente odiando isso) Eu não acho que o texto marginalmente mais longo que alguém tem que digitar impede as pessoas de fazerem o que elas consideram ser o mais conveniente: apenas importar/usar tudo e deixar o compilador descobrir. É por isso que desconfio da bala mágica de fazer as pessoas escreverem essa estrela explicitamente.
Além disso, import module: *
e using module: *
não estão disponíveis para o significado proposto. Já tem um significado, pois *
é um identificador Julia válido e pode ser importado/usado como +
ou a palavra mul
.
@tpapp ou eu entendi mal a documentação novamente, ou import Module
torna Module.x
extensível. Enquanto using Module: x
não torna Module.x
extensível. Portanto import Module
torna algo disponível para extensão e using Module
também, e é por isso que observei que usar tem esse efeito colateral.
(de https://docs.julialang.org/en/v1/manual/modules/)
Realmente não importa qual de nós está certo - em ambos os casos, a situação atual é obviamente uma bagunça se não conseguirmos descobrir o que tudo faz.
@mbauman bom ponto - eu esqueci disso. Eu realmente não me importo com o *
apenas a estrutura de ter using
espelhando as coisas que import
faz com a diferença de importar vs usar sendo se as coisas se tornam extensíveis ou não. Então, se houver um símbolo mais apropriado - all
, __all__
, everything
, exported
, ...? Eu sou a favor. Eu só acho que importar mais provavelmente deve ser refletido digitando mais.
Mas se você não quer isso, você também pode ir para
| | disponibilizar (usando) | tornar extensível (importação) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module: module
| import module: module
|
| tudo exportado | using module
| import module
|
| coisas particulares | using module: x,y
| import module: x,y
|
ou
| | disponibilizar (usando) | tornar extensível (importação) |
| ----------------- | ------------------------ | -------------------------- |
| único módulo | using module
| import module
|
| tudo exportado | using module: module
| import module: module
|
| coisas particulares | using module: x,y
| import module: x,y
|
Mas seja qual for o resultado final, ele deve ser consistente. E no momento simplesmente não é.
using A
torna A.f
extensível, _não_ f
por si só. Para estender _just_ f
sem declarar de qual módulo você quer que ele seja estendido, você tem que import A: f
explicitamente. Caso contrário, você ainda terá que qualificá-lo.
Verifique o seguinte para a semântica de using
julia> module A
export f
f() = "no args in A"
end
Main.A
julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
[1] top-level scope at REPL[2]:1
julia> using .A
julia> f()
"no args in A"
julia> f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
f() at REPL[1]:3
Stacktrace:
[1] top-level scope at REPL[5]:1
julia> f(x) = "one arg where?"
ERROR: error in method definition: function A.f must be explicitly imported to be extended
Stacktrace:
[1] top-level scope at none:0
[2] top-level scope at REPL[6]:1
julia> A.f(x) = "one arg where?"
julia> f(1)
"one arg where?"
E isso para a semântica de import
:
julia> module A
export f
f() = "no args in A"
end
Main.A
julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
[1] top-level scope at REPL[2]:1
julia> import .A
julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
[1] top-level scope at REPL[4]:1
julia> A.f()
"no args in A"
julia> f(1)
ERROR: UndefVarError: f not defined
Stacktrace:
[1] top-level scope at REPL[6]:1
julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
f() at REPL[1]:3
Stacktrace:
[1] top-level scope at REPL[7]:1
julia> f(x) = "one arg where?"
f (generic function with 1 method)
julia> f(1)
"one arg where?"
julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
f() at REPL[1]:3
Stacktrace:
[1] top-level scope at REPL[10]:1
julia> A.f(x) = "one arg where in A"
julia> A.f(1)
"one arg where in A"
@FelixBenning : sim, acho que você não entendeu. FWIW, eu acho que o "... torna Foo.x
extensível" é uma maneira confusa de abordar a distinção --- você sempre pode definir métodos para nomes de funções totalmente qualificados. O que acontece com using Foo: x
é que o próprio Foo
não será trazido para o namespace.
Aliás, relendo este tópico, estou me perguntando se #25306 nos trouxe a uma espécie de ótimo local, e a única decisão que resta é se precisamos de importações não extensíveis para o namespace (atualmente using Foo: f
). Mas como isso está quebrando, o capítulo do manual talvez se beneficie de uma reescrita nesse meio tempo, muitos usuários acham tudo confuso.
É assim que eu abordaria o status quo nos documentos:
using M
carrega o módulo e traz M
e seus símbolos exportados para o namespace. Este é o único caso em que as exportações importam.M
, você pode usar nomes qualificados como M.y
para (a) acessar símbolos não exportados e (b) adicionar métodos a funções, independentemente de serem exportados ou nãoM.f
ou ...import M: f
, então você pode simplesmente usar f
ao definir métodos.import M
e using M: x
são para trazer apenas M
ou x
(e não M
), respectivamente, para o namespace, respectivamente. Algumas pessoas gostam de usar esses formulários no código do pacote para garantir que seus namespaces sejam mantidos limpos (link para guias de estilo relevantes aqui).A ordem reflete mais ou menos os casos de uso encontrados com uso mais avançado. Todos os itens acima se aplicam a submódulos, com M.A
no lugar de M
.
O que dizer disso:
using M
traz M
para o escopousing M: x
traz M.x
para o escopo (como x
)using M: ...
traz todos os símbolos exportados de M
para o escopousing M: x, ...
traz todos os símbolos exportados de M
no escopo e também x
(que podem não ser exportados)import
. Você precisa usar o nome qualificado para estender uma função. (Ou using M; const foo = M.foo
como já se pode fazer agora.)M
também pode ser um submódulo, por exemplo, Foo.Bar
e x
também podem ser x as y
com o significado atual.Ou usamos import
em vez de using
, o que torna isso igual a https://github.com/JuliaLang/julia/issues/8000#issuecomment -355960915.
Um uso muito comum (especialmente em "scripts", mas também em pacotes para certos estilos é)
using Foo, Bar, Baz
e apenas confiando em símbolos exportados. Haveria algum benefício em manter isso o mais simples, possivelmente tão simples quanto é agora.
@tpapp
Acho que você entendeu errado. FWIW, eu acho que o "... torna o Foo.x extensível" é uma maneira confusa de abordar a distinção --- você sempre pode definir métodos para nomes de funções totalmente qualificados.
Ok, então posso estender Foo.x
depois using Foo: x
? Porque se sim, então a documentação não está completa (veja minha captura de tela). Se não, então eu entendi perfeitamente como essas declarações funcionam e obviamente import Foo
fez algo para tornar Foo.x
extensível. Portanto, ele literalmente torna Foo.x
disponível para extensão. Em todos os sentidos dessas palavras. Claro que não disponibiliza x
para extensão, mas é para isso que serve import Foo: x
Ok, então posso estender
Foo.x
depoisusing Foo: x
?
Não, a menos que você traga Foo
para o namespace de alguma forma (por exemplo using Foo
).
Eu entendi perfeitamente como essas declarações funcionam
Não tenho certeza sobre isso - no entanto, se você tiver dúvidas sobre módulos e namespaces, por favor, use o fórum do Discourse.
obviamente
import Foo
fez algo para tornarFoo.x
extensível
Apenas no sentido de que você deve poder se referir a uma função de alguma forma usando um nome qualificado antes de adicionar métodos a ela.
então a documentação não está completa
Eu acho que é, de uma forma meio peculiar. Nesse exemplo em particular, se tudo que você faz é using MyModule: x, p
; então nenhum método está disponível para extensão, então a tabela está correta.
Concordo que poderia ser melhor escrito, como disse acima. Muitas pessoas não acostumadas com namespaces acham confuso. E, TBH, todo o circo using
/ import
é um pouco confuso, daí esse problema.
@tpapp
Ok, então aqui está a coisa: não é absolutamente óbvio que você possa estender todas as funções com o nome completo se o módulo estiver no namespace. Eu sei que esse é um comportamento atual, mas não acho que alguém que já não saiba disso assumiria isso. Especialmente porque estar no namespace nem sempre significa que ele também é extensível. Se eu fizer using module:x
não posso estender x
. Embora eu possa estender x
, se eu usar import module:x
. Portanto, é uma suposição razoável, que a diferença entre using
e import
é se você pode ou não estender as funções importadas.
Usando essa suposição, faria sentido, se using module
permitisse apenas o uso de module.x
, mas não permitisse a extensão de module.x
. Enquanto import module
permitiria a extensão de module.x
. Portanto, se você tomar essa suposição e ler a documentação, descobrirá que duas coisas estão erradas.
using module
permite estender module.x
. Portanto, do ponto de vista dos alunos, using module
tem o efeito colateral , que também age como import module
- ou seja, torna module.x
extensível. E tornar as coisas extensíveis é uma coisa import
, não uma coisa de using
import
, using
não apenas disponibiliza module
, mas sim tudo nele.Então é isso que eu tentei representar com a minha mesa. Se duas palavras diferentes forem usadas (importar/usar), elas devem fazer duas coisas diferentes e isso deve ser claro. Se using
às vezes permite extensão e outras vezes não, isso não é um comportamento previsível.
Uma boa alternativa é remover completamente a importação como sugere @martinholters . Você simplesmente não pode ter duas palavras que são usadas aleatoriamente para certas coisas. Se import
significa tornar as coisas extensíveis ao importar certas funções, enquanto using module: foo
não permite isso, então o mesmo comportamento deve acontecer quando você inclui apenas module
no namespace.
Só porque você pode estender tudo pelo nome completo agora se o módulo estiver no namespace, não significa que isso seja uma coisa óbvia ou direta.
Ou estar no namespace é suficiente, então eu também deveria poder estender x
se eu o incluísse no namespace com using module:x
ou estar no namespace não é suficiente, e então eu também deveria não poderá estender module.x
ao usar o comando using
.
Ou terceira opção: não há import
e você simplesmente tem que estender funções com seu nome completo o tempo todo.
Não é absolutamente óbvio que você possa estender cada função com o nome completo se o módulo estiver no namespace.
Eu concordo, e é por isso que defendo uma reescrita dos documentos. No entanto, isso é tangencial à questão atual. Seria melhor manter (1) propostas para melhorar a documentação da API atual e (2) propostas para redesenhá-la separadas, mesmo que as duas estejam um pouco relacionadas. Eu pretendo fazer um PR para (1) em breve.
@tpapp Você não pode documentar algo que inerentemente não faz sentido. Nenhuma dessas documentações está correta:
using module:x
não permite estender x
)using
e import
" (falso porque para nomes completos é de fato suficiente estar no namespace - parando isso ser suficiente foi minha sugestão)import module:x
deixasse de existir para ser a verdade completa - proposta @martinholters )Qualquer um desses seria aceitável. Mas nada disso é realmente realidade. A realidade é que atualmente existem duas palavras diferentes usadas para "importar coisas", mas elas não têm um caso de uso distinto. É como um loop for
às vezes se comporta como um loop while
se você iterar sobre booleanos. Nenhuma quantidade de documentação fará com que isso não seja confuso
Você não pode documentar algo que inerentemente não faz sentido.
A API de módulos atual é bem definida, então pode ser documentada (já é, só acho que deveria ser melhor).
Nenhuma dessas documentações está correta:
Por favor, tenha a gentileza de esperar pelo meu PR (ou de outra pessoa) e comentar sobre o texto real que estará lá. Postar documentação hipotética e depois alegar que ela está incorreta é apenas adicionar ruído a essa discussão.
@tpapp talvez eu devesse ter dito,
você não pode documentar algo de uma maneira não confusa que não faça sentido inerentemente
Quero dizer, em certo sentido, o código é toda a documentação que você precisa, certo? O que há de errado com isso? O tempo que leva para digerir. E atualmente não vejo uma maneira breve de descrever como funciona, porque está cheio de exceções. Exceções que não deveriam estar lá em primeiro lugar.
Você realmente não vê o que estou tentando transmitir?
Acho que há um consenso geral de que a situação atual é desnecessariamente complexa e inadequadamente documentada. E, sem dúvida, simplificar o maquinário facilitará a documentação. Mas tal simplificação estará quebrando, então não pode ser feita antes de 2.0. Então, seu ponto de vista é que a melhoria da documentação anterior não faria sentido? Então eu discordo. Eu vejo dois problemas separados: Simplificação a ser feita com 2.0 (sobre o qual este problema é) que (espero) incluirá as atualizações de documentação necessárias e melhorará a documentação do funcionamento atual que, lendo este tópico, parece muito necessário, mas é outro problema .
Depois de fazer #38271, acho que as questões pendentes são
Foo.bar() = ...
),using Foo: bar; bar() = ...
)import Foo: bar; bar() = ...
)using Foo
traz todos os símbolos exportados em Foo
para o escopo, import Foo
apenas o módulo ( status quo )using Foo: ...
ou alguma outra sintaxe semelhante, então using Foo
apenas o módulo.(1|2) & 2 permitiriam a unificação em uma única palavra-chave, using
ou import
, ao custo de perder uma única linha
using LinearAlgebra, Random, StaticArrays
Não tenho certeza se vale a pena, mesmo sem considerar a mudança de ruptura.
Este não é um daqueles problemas que oferecem uma solução limpa que o projeto original simplesmente deixou passar; há trocas. Eu esperaria e veria se uma documentação melhor pode melhorar a experiência do usuário, mantendo a configuração atual (1.0).
Para o 2.0, acho que apenas uma limpeza da sintaxe para ser mais consistente e descritiva do que realmente está acontecendo seria bom. Algo como:
| Antes | Depois |
|-|-|
| using Foo
| useall from Foo
|
| import Foo
| use Foo
|
| using Foo: a
| use a from Foo
|
| import Foo: a
e import Foo.a
| extend a from Foo
|
Comentários muito úteis
Eu gosto da simetria de
import
eexport
. (Como alguém apontou em algum lugar.)