Julia: módulo e alias de importação

Criado em 5 set. 2012  ·  96Comentários  ·  Fonte: JuliaLang/julia

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") 
...
design modules speculative

Comentários muito úteis

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.

Todos 96 comentários

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

  1. atualizar este problema, ou
  2. criar um novo problema

(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

Veja: https://github.com/fredrikekre/ImportMacros.jl/pull/3

@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

  1. import Foo; const Bar = Foo fornece uma solução perfeita quando o usuário não se importa com Foo no namespace,
  2. ImportMacros.jl deve lidar com o resto,
  3. nenhum parece ser usado com muita frequência em código prático para garantir uma sintaxe especial.

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.

Esta página foi útil?
0 / 5 - 0 avaliações