Para o sistema de módulos, podemos importar e usar um módulo com notação de ponto:
import ArgParse
...
ArgParse.add_argument(p, "--opt1")
...
Isso pode ser útil para evitar a poluição do namespace. Por causa da verbosidade dos nomes dos módulos, no entanto, seria bom ter aliases de módulo:
import ArgParse as ap
...
ap.add_argument(p, "--opt1")
...
Percebi minutos atrás que você pode fazer o seguinte:
import ArgParse
ap = ArgParse
ap.add_argument(...)
Eu ainda acho que seria bom ter a notação "importar como" como açúcar sintático.
Enquanto estou aqui pensando nisso, também seria bom ter uma maneira de importar várias funções específicas na mesma linha de importação, conforme mencionado na postagem do fórum de @JeffreySarnoff . Uma busca pelos problemas ainda não revelou tal solicitação.
Algo como
# from the forum post
import Module.(export1,export2,export3)
# or
import Module.[export1,export2,export3]
# or maybe
import Module.{export1,export2,export3}
É uma questão diferente desta, mas relacionada. Eu devo
(Estou assumindo que a utilidade de tal recurso não é controversa ...)
Edit : Isso funciona agora com import Module: export1, export2, export3
Eu acho que também deve apoiar, por exemplo,
import ArgParse.add_argument as addarg
para poder renomear os membros do módulo na importação.
Quer saber como os nomes de funções renomeados participariam do despacho?
Teria que funcionar basicamente como um alias, equivalente a const foo = Base.bar
.
Teria que funcionar basicamente como um alias, equivalente a const foo =
Base.bar.Quando importamos uma função, planejamos usá-la ou substituí-la. Dado
acima, com
importar Base.bar como foo
podemos definitivamente usar foo como Base.bar, mas definir foo também substituiria
Base.bar? Deveria?
Este é um problema antigo, mas acho que é hora de revisitar isso, pois estamos nos aproximando de 0,2
Ao escrever códigos, descobri que sempre quis escrever import NumericExtensions as ne
e acabei lembrando a mim mesmo que isso não era suportado.
Eu acho que isso não é muito difícil de implementar e, na minha opinião, isso é muito melhor do que escrever
import NumericExtensions
const ne = NumericExtensions
+1
+1
Isso ainda é relevante? Eu pessoalmente não me importo em fazer o extra foo = Foo
para o aliasing do módulo, mas parece que houve algum consenso para apoiar o açúcar de alias. Alguém que se sente forte o suficiente pode ter que fazer um PR por conta própria.
Observe, no entanto, que x as y
foi um dos mais fortes candidatos à sintaxe convert(y,x)
mencionada em #1470. Parece que não seria muito difícil desambiguar os dois, mas apenas observando.
O valor desse recurso é que você pode evitar importar o nome original.
+1
Seria ótimo ter esse recurso.
+1
Em vez de introduzir uma nova palavra-chave as
, que tal usar o operador =>
, como em
import Tlahuixcalpantecuhtli => Venus: xxxxxxxxx => x9, yyyyyyyy => y8, zzz
Isso permitiria o alias ortogonal do módulo e qualquer subconjunto de variáveis na mesma sintaxe.
Não sei, isso me parece muito feio.
import Tlahuixcalpantecuhtli as Venus: xxxxxxxxx as x9, yyyyyyyy as y8, zzz
Isso é melhor? Acho muito menos legível.
Eu pessoalmente acho as
mais auto-explicativo do que =>
, o que parece que pode significar várias coisas.
divulgação completa: viés python.
Eu gosto da sintaxe =>
melhor do que as
.
Eu gosto de bicicletários que ficam presos no equilíbrio de Nash.
Por que você religaria os símbolos internos de um módulo? É feio porque você realmente não deveria estar fazendo isso.
Eu gosto da sintaxe de importação do Haskell . Eu sinto que os namespaces do Python são mais granulares do que os módulos de Julia. Os módulos de Julia se parecem mais com os módulos de Haskell, então talvez devêssemos procurar inspiração lá.
Eu concordo com @ssfrr , a palavra-chave as
é muito mais autoexplicativa e também é familiar para usuários de Python/Haskell. Para resolver o problema @StefanKarpinski com legibilidade, a abordagem semelhante a python também ajudaria:
from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz
Parece que estou falando com o REPL. Mas sei que esse tipo de sugestão não é viável.
from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz
Esta é uma das minhas escolhas sintáticas menos favoritas em Python. Para alterar um pouco o significado, você precisa reorganizar toda a linha radicalmente, alterando a palavra-chave inicial e a ordenação de tudo. Parece completamente diferente de outras importações, obscurecendo esse fato aqueles que fazem quase a mesma coisa. Este é um design sintático terrível, imo, dando muito peso a ser superficialmente parecido com o inglês.
:+1: por as
, :-1: por from
import a => b: c => d, e => f
parece um pouco perl-esque para mim.
Eu nunca ouvi falar de equilíbrio de Nash e depois de ler sobre isso eu não acho que isso constitua um. Mas o engraçado é que, enquanto esta bikeshed tem 10 respostas em uma hora, uma proposta no #6984 que me parece muito importante (mas mais difícil) passou 4 horas sem comentários!
Quanto a as
vs =>
: qualquer um será uma boa melhoria em relação à situação atual.
Bom ponto @BobPortmann em #6984.
Eu acho que =>
é ótimo para isso, FWIW.
Mas o engraçado é que, enquanto esta bikeshed tem 10 respostas em uma hora, uma proposta no #6984 que me parece muito importante (mas mais difícil) passou 4 horas sem comentários!
Na analogia original de Parkinson, essa questão é o galpão de bicicletas e o número 6984 é o reator nuclear.
:)
Detesto mexer neste pote mas acho que prefiro as
. =>
é um pouco vago, e é estranho que isso compartilhe sintaxe com dicionários. Talvez apenas =
faça sentido: import Foo: x = y
, em analogia com o const x = Foo.y
que você faria agora.
Se nada mais, essa tabela na documentação do Haskell é realmente útil. Muitas vezes (especialmente quando estou aprendendo Julia) fiquei confuso sobre quando usar import
/ using
/ require
ou quando usar $# import Mod.thing
vs. import Mod: thing
. Na verdade, eu estava apenas procurando a explicação da sintaxe dos dois pontos nas importações e não consegui encontrá-la.
@jakebolewski parece que a sintaxe de Haskell é bem próxima, com using
de Julia sendo equivalente a import de Haskell (não qualificado) import
import
Julia sendo equivalente a import qualified
de Haskell
Como ponto de partida, alguém que conhece melhor pode confirmar que este é um bom resumo da sintaxe de importação/uso atualmente disponível? Digamos que temos um módulo Mod
que exporta as funções x
e y
, e também tem funções não exportadas p
e q
.
import Mod
traz Mod.x
, Mod.y
, Mod.p
e Mod.q
. As funções importadas estão todas disponíveis para extensão de método
using Mod
traz x
, y
, Mod.x
, Mod.y
, Mod.p
e Mod.q
. x
e y
não estão disponíveis para extensão de método, mas Mod.*
estão.
import Mod.x, Mod.p
traz x
, p
, Mod.x
, Mod.y
, Mod.p
e Mod.q
. Eles estão todos disponíveis para extensão de método.
import Mod: x, p
é o mesmo que import Mod.x, Mod.p
using Mod.x, Mod.p
traz x
, p
, Mod.x
, Mod.y
, Mod.p
e Mod.q
. x
e p
não estão disponíveis para extensão de método, mas Mod.*
estão.
using Mod: x, p
é o mesmo que using Mod.x, Mod.p
Até onde eu sei using Mod
é o único uso que se importa com o que é exportado por Mod
.
Seria muito útil ter este resumo na documentação do Módulo! Alguém pode confirmar isso, para que eu possa preparar um PR?
Parece que a versão menos controversa é:
import Tlahuixcalpantecuhtli as Venus: xxxxxxxxx as x9, yyyyyyyy as y8, zzz
Eu estou bem com isso. A versão de atribuição é interessante, porém, testando-a:
import Venus = Tlahuixcalpantecuhtli: x9 = xxxxxxxxx, y8 = yyyyyyyy, zzz
Achei que ia odiar isso, mas na verdade é bem legal. Eu gosto que os nomes apresentados neste módulo venham primeiro e sejam os mais proeminentes – de muitas maneiras, isso é o mais importante.
@jakebolewski : Por que você religaria os símbolos internos de um módulo? É feio porque você realmente não deveria estar fazendo isso.
Você pode querer importar ligações com nomes conflitantes de dois módulos diferentes e isso permite isso. Você também pode qualificá-los totalmente, mas se estamos apoiando o alias, também podemos oferecer suporte a isso.
Algumas novas linhas bem colocadas tornam mais legíveis, IMO:
import Tlahuixcalpantecuhtli as Venus:
xxxxxxxxx as x9,
yyyyyyyy as y8,
zzz
ou
import Venus = Tlahuixcalpantecuhtli:
x9 = xxxxxxxxx,
y8 = yyyyyyyy,
zzz
Estou descobrindo que prefiro a versão de atribuição bastante fortemente. Afinal, importar é uma forma de atribuição.
Seria muito útil ter este resumo na documentação do Módulo! Alguém pode confirmar isso, para que eu possa preparar um PR?
@brk00 , vá em frente!
@kmsquire , vou trabalhar nisso hoje!
Mas antes de fazer isso, quero ter certeza de que entendi bem. No exemplo @ssfrr , suponha que eu esteja no REPL e digite using Mod
. As funções importadas x
e y
não estão disponíveis para extensão de método. Significa que, se eu declarar outras funções x e y, não terei mais acesso aos métodos Mod
que antes importei, apenas aos novos?
Isto é o que acontece se você tentar estender uma função sem importá-la:
julia> module Mod
export x, y
x() = "x"
y() = "y"
p() = "p"
q() = "q"
end
julia> using Mod
julia> x()
"x"
julia> p()
ERROR: p not defined
julia> x(n) = n
ERROR: error in method definition: function Mod.x must be explicitly imported to be extended
Você pode adicionar um método assim:
julia> import Mod: x # or import Mod.x
julia> x(n) = n
x (generic function with 2 methods)
julia> methods(x)
# 2 methods for generic function "x":
x() at none:5
x(n) at none:1
Bem, fiquei confuso com o comportamento de resolução de nomes:
julia> module Mod
export x, y
x() = "x"
y() = "y"
p() = "p"
q() = "q"
end
julia> using Mod
julia> x(n) = n
x (generic function with 1 method)
Se eu atribuir uma nova função a x
antes de _usar_ aquela que acabei de importar com using Mod
, o erro não é gerado. Mas se eu, por exemplo, chamar x()
antes da nova atribuição (como você fez no seu exemplo), o erro é gerado de fato.
Ai, interessante! Eu não sabia disso. Acabei de chamar x
para demonstrar o namespace após o using
.
Talvez um dos desenvolvedores principais possa lançar alguma luz. O que está acontecendo quando x
é chamado que aciona o erro na tentativa de sobrecarga?
Acho o aliasing sugerido por @carlobaldassi acima muito útil na prática.
Eu estou querendo saber se import
poderia apenas retornar o objeto do módulo, para que ele pudesse ser alias diretamente com uma atribuição, com ou sem const
? Por exemplo
const Shortname = import ModuleWithLongName
ao invés de
import ModuleWithLongName
const Shortname = ModuleWithLongName
Isso não exigiria uma nova sintaxe, apenas uma combinação de elementos de sintaxe existentes. Percebo que atualmente import
é uma "declaração", chamada puramente por efeitos colaterais, e não tem valor. Se o analisador não permitir isso, eu preferiria import("Module")
ou algo semelhante.
Acho que a opção de alias de módulos pode ser boa, mas também acho que há uma desvantagem que ninguém mencionou: pode obscurecer significativamente o código. Da perspectiva de um leitor de código, é bom ter apenas um nome descritivo para um módulo.
Bastante trivial para procurar embora? Os formulários abreviados provavelmente também serão padronizados para pacotes populares como np
para numpy
.
Eu ainda prefiro numpy
a np
padronizado e até Arrays
a numpy
. numpy
soa como um cruzamento entre um movimento de dança dos anos 50 e uma praga mortal.
pode facilmente escrever:
Base.require("ArgParse")
const ap = Main.ArgParse
isso não precisa de uma sintaxe para 1.0
Eu sinto que devemos ter uma sintaxe para isso. Sim, podemos viver sem ele, pois você pode fazer require
e uma atribuição const
, mas usar require
é muito feio e a falta de uma sintaxe conveniente desencoraja as pessoas a renomear as coisas como eles querem/precisam.
O truque const
ser fácil fica aquém quando se precisa alias de uma macro, é assim que eu faço, e demorei um pouco para descobrir:
julia> macro foo(a, b, c)
a, b, c
end
<strong i="8">@foo</strong> (macro with 1 method)
julia> macro f(args...)
Expr(:macrocall, Symbol("@foo"), args...) |> esc
end
<strong i="9">@f</strong> (macro with 1 method)
julia> <strong i="10">@f</strong> 1 2 3
(1,2,3)
No ImportMacros.jl estou implementando:
<strong i="15">@from</strong> Foo.Bar use foo as f
<strong i="18">@from</strong> Foo.Bar use <strong i="19">@foo</strong> as @f
@Ismael-VC
Experimentar
<strong i="7">@eval</strong> const $(Symbol("@foo")) = $(Symbol("@bar"))
que é mais feio, mas mais curto e talvez mais claro do que definir uma macro localmente
@TotalVerb obrigado novamente por sua ajuda!
Parece que a única funcionalidade que falta no Julia + ImportMacros é criar um alias para o módulo e uma mistura de alias de um número variável de objetos desse módulo e importar outros sem alias:
<strong i="9">@from</strong> Foo.Bar as B use foo as f, <strong i="10">@bar</strong> as <strong i="11">@b</strong>, baz
Ou sem o from
:
import Foo.Bar as B: foo as f, <strong i="17">@bar</strong> as <strong i="18">@b</strong>, baz
import B = Foo.Bar: f = foo, <strong i="21">@b</strong> = <strong i="22">@bar</strong>, baz
O último parece não ser possível com macros:
julia> parse("<strong i="26">@from</strong> Foo.Bar as B use foo as f, <strong i="27">@bar</strong> as <strong i="28">@b</strong>, baz")
:(<strong i="29">@from</strong> Foo.Bar as B use foo as (f,@bar(as,(@b(),baz))))
julia> parse("<strong i="30">@import</strong> Foo.Bar as B: foo as f, <strong i="31">@bar</strong> as <strong i="32">@b</strong>, baz")
:(<strong i="33">@import</strong> Foo.Bar as B:foo as (f,@bar(as,(@b(),baz))))
julia> parse("<strong i="34">@import</strong> B = Foo.Bar: f = foo, <strong i="35">@b</strong> = <strong i="36">@bar</strong>, baz")
ERROR: ParseError("unexpected \"=\"")
in #parse#310(::Bool, ::Bool, ::Function, ::String, ::Int64) at .\parse.jl:184
in (::Base.#kw##parse)(::Array{Any,1}, ::Base.#parse, ::String, ::Int64) at .\<missing>:0
in #parse#311(::Bool, ::Function, ::String) at .\parse.jl:194
in parse(::String) at .\parse.jl:194
Na verdade, isso não precisa ser decidido para 1.0, pois todas as sintaxes propostas são erros ou tolices totais.
O que deve acontecer com um alias de módulo quando o módulo original é redefinido, por exemplo, por um reload("...")
no REPL? Com a abordagem const M = MyModule
, M
ainda se refere ao módulo antigo após o recarregamento, portanto, quaisquer nomes recém-definidos em MyModule
só serão acessíveis por MyModule.name
mas não M.name
.
Nesse caso, uma sintaxe de alias deve ser mais do que açúcar sintático para const M = Module
, mas certifique-se de que M
sempre se refira à mesma coisa que MyModule
.
Isso também simplificaria o fluxo de trabalho do REPL, pois posso continuar a import
um módulo durante o desenvolvimento, mas posso fazer referência a ele por meio de um alias mais curto sem precisar restabelecer um alias (não const
) toda vez que eu reload
.
A partir de agora, reload
foi removido, portanto, essa preocupação não é mais aplicável.
Isso também se aplica a include
, ou estou perdendo alguma coisa? Em geral, quando um módulo é redefinido, o nome do módulo import
ed se refere à nova definição enquanto um alias definido por atribuição se refere à definição antiga, certo?
Não, isso não deve ser uma preocupação. O nome importado NUNCA será alterado quando um módulo for redefinido, incluindo import
normal. O que mudou é o módulo que será importado se você importar novamente. O importado sempre foi apenas um encadernação.
Ok, talvez eu não tenha me expressado claramente, então aqui está um exemplo: Se você criar um arquivo test.jl
com o conteúdo
module Tst
f() = 1
end
você pode carregá-lo, importar Tst
, definir um alias para Tst
e está tudo bem:
julia> include("test.jl")
Tst
julia> import Tst
julia> const Tst2 = Tst
Tst
julia> Tst.f()
1
julia> Tst2.f()
1
Se você agora alterar test.jl
para
module Tst
f() = 2
end
e re- include
no REPL, o módulo e seu alias se comportam de forma diferente:
julia> include("test.jl")
WARNING: replacing module Tst
Tst
julia> Tst.f()
2
julia> Tst2.f()
1
Entendo por que esse é o caso, mas acho que isso não deveria acontecer quando você cria um alias com algo como import Tst as Tst2
.
Eu testei isso em 0.6.2, e no momento não posso testá-lo em 0.7, então não faço agora se ainda for o caso.
Você não está importando de forma alguma. O módulo que você inclui já está definido em seu escopo. O import
não é operacional.
É verdade, mas este não é o ponto. A questão é o que acontece quando eu faço algo como o hipotético import Tst as Tst2
. Então esta declaração não é um não operacional, mas cria um alias.
Se este alias funcionar como const Tst2 = Tst
, redefinir o módulo Tst
fará com que o alias aponte para a versão antiga do módulo. Isso não é desejável em nenhum caso. Ou o alias deve apontar para a nova versão de Tst
também, ou o usuário deve ser forçado a restabelecer o alias. Mas no último caso, usar o alias antigo sem restabelecê-lo deve gerar um erro em vez de apontar para o módulo antigo.
Esse é inteiramente o ponto. Este problema é sobre adicionar uma função a import
não à definição do módulo. Seu import
não é operacional e, após removê-lo, seu código não possui mais import
ou using
, portanto, não está relacionado a esse problema. Por favor, altere-o para que o módulo não seja definido no mesmo escopo que import
.
import Tst as Tst2
funcionará da mesma forma que import Tst
em termos do que é vinculativo a esse nome, que é sempre uma vinculação e não qualquer coisa mágica que você deseja. Não se trata de adicionar um alias ao definir o módulo.
Colocando de outra forma, seu código é basicamente,
a = Ref(1) # include
a = a # import
b = a # alias
a = Ref(2) # include again
E não faz sentido reclamar do comportamento do b = a
já que o a
não está vinculado ao a = a
. Tudo o que você vê é o efeito colateral de definir o módulo no mesmo escopo que a importação (ou seja a = a
e a = Ref(...)
no mesmo escopo) em um exemplo ruim.
Ok, eu só queria salientar que em algumas situações um alias definido por uma atribuição não funcionaria como esperado. Talvez eu tenha escolhido um mau exemplo. Eu pensei que isso poderia ser importante para uma sintaxe que cria um alias de módulo (relacionado ou não a import
), mas agora isso é hipotético de qualquer maneira. Desculpe o barulho.
esta questão existe há muito tempo, embora algumas extensões ainda devam ser discutidas, o
import LongPackage as longpkg
em si parece bastante razoável.
Ressalto. Eu estou querendo saber qual é a melhor solução - por enquanto - parece? Aqui está o que estou fazendo:
import LinearAlgebra
const linalg = LinearAlgebra
Por favor, corrija-me se houver uma solução melhor!
Esse é o padrão atual.
Que tal uma sintaxe ~? Eu não sei as ramificações, mas tem uma boa estética. Além disso, ~ é usado como símbolo de aproximado.
import LongPackage ~ lp
@JeffreySarnoff citando Stefan
@Stefan Karpinski
Esta é uma das minhas escolhas sintáticas menos favoritas em Python. Para alterar um pouco o significado, você precisa reorganizar toda a linha radicalmente, alterando a palavra-chave inicial e a ordenação de tudo. Parece completamente diferente de outras importações, obscurecendo esse fato aqueles que fazem quase a mesma coisa. Este é um design sintático terrível, imo, dando muito peso a ser superficialmente parecido com o inglês.
Nesse caso, ~ não teria nada a ver com o aspecto semelhante ao inglês da sintaxe do Python. Acho que quando vejo ~, apenas reconheço o conceito matemático de aproximado. 10.001 ~ 10.
Então, nesse sentido, eu acho ~ muito legal na verdade.
import LongPackage ~ lp
import HugePackage ~ hp
import MassivePackage ~ mp
import GiantPackage ~ gp
:-)
ok -- esqueça python -- estou excluindo esse comentário em favor de
lp imports LongPlayingRecords
hp imports HewlettPackard
mp imports MilitaryPolice
gp imports PariForNumberTheory
Eu não gostei originalmente, mas tantas pessoas parecem naturalmente querer escrever isso com as
que eu tenho que dizer que parece perverso fazer qualquer outra coisa. Não precisa ser reservado como uma palavra-chave ou qualquer coisa, pois este é um contexto sintático claro.
Uma linguagem de programação que _tenta_ fazer com que as pessoas saibam o que significa!?!
@Jeffrey Sarnoff
Uma linguagem de programação que tenta ser melhor que Python!!!
@neilpanchal Julia é definitivamente uma linguagem que tenta ser melhor que o Python de várias maneiras. Por outro lado, obviamente não sai do caminho para ser _diferente_ do Python, onde o Python acertou algo. Julia empresta convenções sintáticas do Python para a esquerda e para a direita.
Por favor, adote o python as
. Eles acertaram.
julia> importe LightGraphs como lg
ERRO: sintaxe: token extra "como" após o final da expressão
Apenas outro comentário de $0,02 em favor de as
que foge do Python. fwiw, agora que estou usando Julia com alguma regularidade novamente, sinto falta disso. Não como em Python, mas como em Clojure (veja exemplo ), onde namespaces com escopo de pacote (que são bons para desambiguação) podem ser simplificados para fazer uso explícito de bibliotecas muito mais manejáveis. Desde que eu tenho uma preferência para evitar using
(ou seja, importações curinga) tendo algum açúcar sintático para import Gadlfy as gf
sem a adição da linha const gf =
(o que eu faço atualmente) seja útil.
Ainda outro comentário apoiando a sintaxe as
:heart: Eu desejo muito para os futuros lançamentos de Julia.
Eu também desejo que isso aconteça. Especialmente porque não é uma mudança de ruptura (certo?).
Acho que isso será implementado em algum momento (eu tentaria por mim mesmo se pudesse), mas, enquanto isso, o pacote ImportMacros
tem praticamente todas as funcionalidades solicitadas aqui (embora esteja faltando documentação) :
<strong i="8">@import</strong> ModuleA as ma
<strong i="9">@using</strong> ModuleB as mb
<strong i="10">@from</strong> ModuleC use long_function_name as func
<strong i="11">@from</strong> Module use <strong i="12">@macroname</strong> as <strong i="13">@alias</strong>
Eu não sabia sobre ImportMacros
.jl, obrigado por apontar. Se essa funcionalidade existe em um pacote, acho que não é tão importante tê-la no idioma base.
Eu ainda acho que isso seria bom ter no idioma.
As importações são IMO exatamente o tipo de coisa para a qual você quase _sempre_ deseja adiar as convenções da linguagem base e não envolver macros ou adicionar uma dependência de pacote. import
... const
é a melhor opção, mesmo (especialmente) se a linguagem base nunca for estendida para tornar esse processo de duas etapas mais conciso.
Em particular, é um pouco feio ver
using ImportMacros
<strong i="6">@using</strong> OtherPackage as ...
<strong i="7">@using</strong> ThirdPackage as ...
porque o não macro using
se destaca.
Que tal import
retornar o módulo/função/o que quer que ele realmente traga para o escopo?
Então você pode escrever
lg = import LightGraphs
baz = import foo.bar
baz, kit, kat = import foo : bar1, bar2, bar3
A única coisa que precisa ser alterada é a função de importação
Eu realmente gosto da solução de @DhruvaSambrani porque ela se encaixa com a abordagem geral de "tudo é uma expressão" de Julia.
A única coisa é que, para ser consistente, a expressão de importação ainda teria que realizar o efeito colateral de trazer o nome completo para o escopo (talvez não um desastre). Também pode ser uma caixa especial para não importar o nome completo, mas a caixa especial é bruta.
No OCaml (a linguagem com os melhores módulos!), você pode fazer algo como:
module LG = LightGraphs
A diferença importante aqui é que o nome qualificado de cada módulo compilado com o programa OCaml está automaticamente no escopo.
É claro que usar import LightGraphs as LG
tem o benefício de que "todo mundo" já o conhece do Python, mas meu gosto pessoal é que é melhor reutilizar a sintaxe de atribuição para algo que literalmente é atribuição.
Ou uma mudança mais extensa seria introduzir um "namespace" como um Type (idk se isso já foi feito). Então import
pode expandir o módulo/qualquer coisa em um namespace e retornar o namespace. Mas isso quebrará todo o código Julia porque simplesmente fazer import mod_name
não tornará o namespace mod_name
disponível no escopo global. Se alguém puder, de alguma forma, tornar o namespace mod_name
disponível para global quando a importação for usada sem uma atribuição anterior, isso não será um problema.
Ou apenas adicione uma nova função import_as_ns(mod_name) :: Namespace
que será uma função pura que retorna todas as funções/módulos etc em um único namespace.
ele se encaixa com a abordagem geral de "tudo é uma expressão" de Julia
No entanto, import
e using
são especiais porque são usados apenas por seus efeitos colaterais e apenas no nível superior.
Aliás, dado que esta questão está em aberto há muito tempo, gostaria de saber se a triagem estaria disposta a revisitá-la e tomar uma decisão. Defendo o encerramento, uma vez que
import Foo; const Bar = Foo
fornece uma solução perfeita quando o usuário não se importa com Foo
no namespace,Vou dizer que depois de todo esse tempo, ainda sinto falta disso (e também uso ImportMacros no meu próprio código).
Eu acho que o estilo de café que você está escrevendo afeta a necessidade (ou desejo) disso. Quando estou escrevendo ML ou código científico, acho que não sinto muita falta. Quando escrevi outro código que usa vários pacotes com nomes longos é quando acho que uso mais o ImportMacros.
ImportMacros é suficiente. Eu apenas concordo com comentários que não parece tão limpo.
Também estou ansioso pelo dia em que poderei usar um autoformatador no meu editor para classificar minhas importações... mas é menos provável que saiba o que fazer com @using
...
Também concordo com @kmsquire que isso não é um problema tão grande, e ImportMacros deve ser suficiente. Mas qualquer método deve ser documentado para que não apareça novamente.
Embora eu ainda goste do açúcar m=import Module
😅
Eu não pretendia que minha mensagem implicasse que isso não é grande coisa.
Eu ainda preferiria uma sintaxe para isso.
ImportMacros é suficiente. Eu apenas concordo com comentários que não parece tão limpo.
Eu concordo. Também para:
using ImportMacros
<strong i="8">@using</strong> OtherPackage as ...
Isso torna um pouco chato ao apresentar a linguagem para iniciantes ou iniciantes em programação para explicar porque um determinado script começa com essa inconsistência, pois complica o fluxo e muda o foco do ensino.
nenhum parece ser usado com muita frequência em código prático para garantir uma sintaxe especial
A razão pela qual as pessoas não usam muito o ImportMacros no código real pode ser devido a uma troca. Por exemplo, eu usaria import ... as ...
quase todas as vezes, mas provavelmente não ao custo de ter que importar primeiro outro pacote para fazer isso. Mais do que uma dependência adicional, também tem um impacto em termos de "sensação" (e algumas pessoas gostam que seu código pareça elegante (especialmente em python ou Julia) 🤷♂️ , e isso using/@using
dá uma " hacky" à introdução de um script).
Então, sim, "ImportMacros é suficiente". Para quem realmente quer usar. Mas é isso, não é uma questão vital, e as pessoas podem trabalhar sem ele. No entanto, acredito fortemente que normalmente são essas pequenas coisas que fazem com que a sintaxe de uma linguagem permaneça, daí esse thread-monstro para adicionar seu suporte à base.
Eu acho que import ... as ...
é um açúcar sintático que definitivamente valeria a pena, é muito fácil de entender e explicar, e especialmente útil em linguagens como Julia em que o pacote tende a ter nomes bastante longos (obviamente, isso importa apenas sob a suposição de que as pessoas não querem fazer using ...
).
E o fato de que Python o implementou primeiro não deveria ser realmente relevante; Julia deve se esforçar para ter a melhor sintaxe antes de tentar se distanciar de outras linguagens e/ou enfatizar sua própria identidade.
Ter sintaxe para isso e convenções em torno de módulos populares (estou pensando em como é sempre import numpy as np
e import matplotlib.pyplot as plt
) também daria uma alternativa concisa para fazer using SuchAndSuch
e confiar em export
ed nomes (que é algo que eu geralmente evitava no código de "biblioteca").
@Dominique Makowski :
apresentar a linguagem para iniciantes ou iniciantes em programação para explicar por que um determinado script começa com essa inconsistência
Não entendo por que você acha que é uma inconsistência, é simplesmente gerenciamento de namespace, que é uma parte normal da programação em geral.
Além disso, renomear módulos pode não ser algo que os recém-chegados a Julia encontrariam primeiro.
Não estou dizendo que não há um caso de uso, mas pode ser raro justificar sua própria palavra-chave. No momento, pesquisar por using ImportMacros
fornece <10 resultados únicos no Github.
Eu estaria usando import ... as ... quase todas as vezes, mas provavelmente não ao custo de ter que importar primeiro outro pacote para fazer isso
Isso corta nos dois sentidos: se o custo (muito pequeno) de usar um pacote leve como ImportMacros
ou um símbolo extra ( import LongFoo; const Foo = LongFoo
) não valer o benefício, então o benefício pode não ser tão grande .
Julia deve se esforçar para ter a melhor sintaxe antes de tentar se distanciar de outras linguagens e/ou enfatizar sua própria identidade.
Não sei por que você acha que isso é uma motivação nesta discussão.
Existem alguns casos em que você pode querer usar as
onde a atribuição const
não funciona:
julia> using Distributed
julia> import Future; const future = Future
Future
julia> Future === Distributed.Future # oops, this is what we wanted
false
julia> Future === future # not what we wanted `Future` to mean
true
Sim, você pode contornar isso, mas é estranho. Dado que muitas vezes é útil, às vezes necessário e que import FooBar as FB
é a sintaxe "menos surpreendente" geralmente aceita, isso parece ser o que devemos seguir. Tudo isso precisa que alguém faça um PR implementando isso.
Nova usuária Julia aqui.
Eu venho de um fundo principalmente R, usando Python também. Eu tenho experiências ruins com o espaço de nome comum do pacote que leva a mecanismos de carregamento como using
. O risco de colisão é um, mas apenas do ponto de vista da legibilidade, é um fardo mental de pensar constantemente no módulo de cada método.
Eu não sei o quão complexo é adicionar essa sintaxe? É factível para um novato?
Provavelmente não é muito fácil. Isso envolverá hackear o código do analisador que está escrito no Scheme e conectar a alteração através do AST, potencialmente tocando em algum código C também.
Seria um bom recurso para evitar conflitos de nomes de funções em vários módulos.
De uma discussão sobre o slack, adiciono alguns pensamentos
Eu me pergunto se isso tornará as pessoas menos cuidadosas ao escolher nomes de funções e adicionar métodos adequadamente às funções corretas. O fato de que, por exemplo,
numpy.sin
sympy.sin
são funções diferentes é uma maneira infalível de ter que implementar duas versões de sua função se você quiser que ela funcione para entradas numéricas e simbólicas. Se esse namespace se espalhar também em julia devido à conveniência, podemos perder muita reutilização de código? Ou seja, temo que isso possa nos dar menos de " Isso incentiva as pessoas a trabalharem juntas ", como foi colocado por Lyndon em https://www.oxinabox.net/2020/02/09/whycompositionaljulia.html
@baggepinnen Eu não acho que isso mude nada a esse respeito.
Em Julia, métodos implementados em módulos diferentes não são mesclados importando os dois métodos para o mesmo namespace.
julia> module Foo
export sin
sin(s::String) = uppercase(s)
end
Main.Foo
julia> sin(1)
0.8414709848078965
julia> using .Foo
WARNING: using Foo.sin in module Main conflicts with an existing identifier.
julia> sin("wat")
ERROR: MethodError: no method matching sin(::String)
Closest candidates are:
sin(::BigFloat) at mpfr.jl:727
sin(::Missing) at math.jl:1197
sin(::Complex{Float16}) at math.jl:1145
...
Stacktrace:
[1] top-level scope at REPL[4]:1
[2] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.1/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288
julia> Foo.sin("wat")
"WAT"
É preciso adicionar explicitamente um dispatch à função original para que isso funcione:
julia> Base.sin(s::String) = Foo.sin(s)
julia> sin("wat")
"WAT"
Até onde eu sei, não há como o alias de módulo alterar a forma como as funções são despachadas.
E honestamente, algumas funções com o mesmo nome _deveriam_ viver em namespaces separados. Uma função find_delta
em uma biblioteca numérica fará algo não relacionado a find_delta
em uma biblioteca de diferenças de arquivo. Uma vez que seu código é tão polimórfico que encontrará uma maneira de executar em uma entrada quebrada, você está entrando no território do JavaScript, e ninguém quer isso.
@ninjaaron , posso ter falhado em transmitir alguma nuance na minha preocupação acima. Não estou preocupado com essa mudança mudando nada além da estratégia que as pessoas escolhem quando implementam bibliotecas.
A pessoa que implementa Foo
encontra um aviso como
julia> using .Foo
WARNING: using Foo.sin in module Main conflicts with an existing identifier.
pode escolher simplesmente import .Foo.sin as sin2
, ou pensar se ele realmente queria ou não fornecer um novo despacho para Base.sin
. Meu ponto era que, se ficar muito fácil simplesmente considerá-los como funções diferentes, talvez isso se torne muito difundido, mesmo em situações em que eles realmente deveriam ser métodos diferentes da mesma função. A situação atual, em que é um pouco mais complicado lidar com funções diferentes com o mesmo nome, tem o efeito colateral agradável de as pessoas conversarem umas com as outras e fazerem o possível para descobrir se realmente é a mesma função ou não. Não estou argumentando contra a possibilidade de ter funções diferentes com o mesmo nome, o que obviamente pode ser muito útil. Não tenho certeza se minha preocupação é importante ou não, mas achei que valia a pena retirá-la para garantir que ela fosse considerada.
@baggepinnen Isso faz sentido, e não há mal nenhum em trazer isso à tona. Eu não acho que isso fará uma grande diferença para as pessoas que criam bibliotecas, já que o alias de módulo afetará apenas os usuários da biblioteca. Acho que é possível que ter um alias de módulo mais fácil resulte em menos usuários _reclamando_ sobre conflitos de nomenclatura, mas eu ficaria surpreso se isso tivesse um tremendo impacto nas APIs.
Quando escrevo uma biblioteca, geralmente estou bem consciente se quero adicionar um dispatch a uma função no Base ou se quero mantê-lo separado. Eu diria que a maioria dos outros autores de bibliotecas também.
@ninjaaron , acho que se a convenção atual estiver usando uma implementação específica para despacho, como
numpy.sin
sympy.sin
usar import numpy.sin as np_sin
como uma opção extra para usuário/desenvolvedor não deve afetar a base de código atual.
A única preocupação será afetar apenas a função/interface de exposição.
Comentários muito úteis
esta questão existe há muito tempo, embora algumas extensões ainda devam ser discutidas, o
em si parece bastante razoável.