Julia: модуль и импорт алиасинга

Созданный на 5 сент. 2012  ·  96Комментарии  ·  Источник: JuliaLang/julia

Для модульной системы мы можем импортировать и использовать модуль с точечной записью:

import ArgParse
... 
    ArgParse.add_argument(p, "--opt1") 
...

Это может быть полезно для предотвращения загрязнения пространства имен. Однако из-за многословия имен модулей было бы неплохо иметь псевдонимы модулей:

import ArgParse as ap 
... 
    ap.add_argument(p, "--opt1") 
...
design modules speculative

Самый полезный комментарий

эта проблема существует уже давно, хотя некоторые расширения все еще должны быть обсуждены,

import LongPackage as longpkg

само по себе кажется вполне разумным.

Все 96 Комментарий

Несколько минут назад я понял, что вы можете сделать следующее:

import ArgParse
ap = ArgParse

ap.add_argument(...)

Я все еще думаю, что было бы неплохо иметь нотацию «импортировать как» в качестве синтаксического сахара.

Пока я здесь и думаю об этом, было бы неплохо иметь способ импортировать несколько определенных функций в одну строку импорта, как упоминалось в сообщении на форуме @JeffreySarnoff . Поиск по выпускам пока не выявил такой просьбы.

Что-то типа

# from the forum post
import Module.(export1,export2,export3)
# or
import Module.[export1,export2,export3]
# or maybe
import Module.{export1,export2,export3}

Это другая проблема, чем эта, но связанная. Нужно ли мне

  1. обновить эту проблему, или
  2. создать новую проблему

(Я предполагаю, что полезность такой функции не вызывает сомнений...)

Изменить : теперь это работает с import Module: export1, export2, export3

Я думаю, что он также должен поддерживать, например,

import ArgParse.add_argument as addarg

чтобы иметь возможность переименовывать элементы модуля при импорте.

Хотите знать, как переименованные имена функций будут участвовать в диспетчеризации?

Это должно было бы работать в основном как псевдоним, эквивалентный const foo = Base.bar .

Это должно было бы работать в основном как псевдоним, эквивалентный const foo =
База.бар.

Когда мы импортируем функцию, мы либо планируем ее использовать, либо переопределяем. Дано
выше, с

импортировать Base.bar как foo

мы определенно можем использовать foo как Base.bar, но определение foo также переопределяет
База.бар? Должен ли?

Это старая проблема, но я думаю, что пришло время вернуться к ней, поскольку мы приближаемся к версии 0.2.

При написании кода я обнаружил, что всегда хотел написать import NumericExtensions as ne и в итоге напомнил себе, что это не поддерживается.

Я думаю, это не так уж сложно реализовать, и, на мой взгляд, это намного приятнее, чем писать

import NumericExtensions
const ne = NumericExtensions

+1

+1

Это все еще актуально? Я лично не возражаю, просто добавляя дополнительные foo = Foo для псевдонимов модулей, но похоже, что был некоторый консенсус в отношении поддержки псевдонимов сахара. Кому-то, кто чувствует себя достаточно сильно, возможно, придется сделать пиар самому.
Однако обратите внимание, что x as y был одним из сильнейших претендентов на синтаксис convert(y,x) , упомянутый в #1470. Кажется, было бы не слишком сложно устранить двусмысленность этих двух, но просто отметив.

Ценность этой функции в том, что вы можете избежать импорта исходного имени.

+1
Было бы здорово иметь эту функцию.

+1

Вместо того, чтобы вводить новое ключевое слово as , как насчет использования оператора => , как в

import Tlahuixcalpantecuhtli => Venus: xxxxxxxxx => x9, yyyyyyyy => y8, zzz

Это позволит ортогонально использовать псевдонимы модуля и любого подмножества переменных в одном и том же синтаксисе.

Не знаю, мне это кажется очень некрасивым.

import Tlahuixcalpantecuhtli as Venus: xxxxxxxxx as x9, yyyyyyyy as y8, zzz

Это лучше? Я нахожу это гораздо менее читабельным.

Я лично нахожу as более понятным, чем => , что может означать множество вещей.

полное раскрытие: предвзятость питона.

Мне нравится синтаксис => больше, чем as .

Мне нравятся велосипедные навесы, которые застревают в равновесии Нэша.

Зачем перепривязывать внутренние символы модуля? Это некрасиво, потому что вы действительно не должны этого делать.

Мне нравится синтаксис импорта Haskell . Я чувствую, что пространства имен Python более детализированы, чем модули Julia. Модуль Джулии больше похож на модули Haskell, так что, возможно, нам стоит поискать там вдохновение.

Я согласен с @ssfrr , ключевое слово as говорит само за себя и знакомо пользователям Python/Haskell. Чтобы решить проблему @StefanKarpinski с удобочитаемостью, также может помочь подход, подобный python:

from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz

Я чувствую, что разговариваю с REPL. Но я знаю, что такое предложение неосуществимо.

from Tlahuixcalpantecuhtli as Venus import xxxxxxxxx as x9, yyyyyyyy as y8, zzz

Это один из моих наименее любимых вариантов синтаксиса в Python. Чтобы немного изменить смысл, вы должны радикально перестроить всю строку, изменив исходное ключевое слово и порядок всего. Он выглядит совершенно иначе, чем другой импорт, скрывая тот факт, что они делают почти то же самое. Это ужасный синтаксический дизайн, имхо, придающий слишком большое значение внешнему английскому языку.

:+1: для as , :-1: для from

import a => b: c => d, e => f кажется мне слишком похожим на Perl.

Я никогда не слышал о равновесии Нэша и, прочитав о нем, не думаю, что это так. Но забавно то, что в то время как этот велосипедный сарай получил 10 ответов за один час, предложение № 6984, которое кажется мне очень важным (но более сложным), прошло 4 часа без комментариев!

Что касается as против => : любой из них будет хорошим улучшением по сравнению с нынешней ситуацией.

Хорошее замечание @BobPortmann по #6984.

Я думаю, что => отлично подходит для этого, FWIW.

Но забавно то, что в то время как этот велосипедный сарай получил 10 ответов за один час, предложение № 6984, которое кажется мне очень важным (но более сложным), прошло 4 часа без комментариев!

В оригинальной аналогии Паркинсона этот вопрос — сарай для велосипедов, а № 6984 — ядерный реактор.

:)

Я ненавижу мешать этот горшок, но я думаю, что предпочитаю as . => немного расплывчато, и странно, что он использует общий синтаксис со словарями. Может быть, просто = имеет смысл: import Foo: x = y по аналогии с const x = Foo.y , которую вы бы сделали сейчас.

По крайней мере, эта таблица в документации Haskell действительно полезна. Я часто (особенно когда впервые изучал Джулию) путался в том, когда использовать import / using / require или когда использовать import Mod.thing против import Mod: thing . На самом деле я просто искал объяснение синтаксиса двоеточия при импорте и не смог его найти.

@jakebolewski кажется, что синтаксис Haskell довольно близок: using Джулии эквивалентен (неквалифицированному) Haskell import , а import Джулии эквивалентен import qualified Хаскелла.


В качестве отправной точки может кто-то, кто знает лучше, подтвердить, что это хорошее резюме доступного в настоящее время синтаксиса импорта/использования? Скажем, у нас есть модуль Mod , который экспортирует функции x и y , а также имеет неэкспортируемые функции p и q .

import Mod приносит Mod.x , Mod.y , Mod.p и Mod.q . Все импортированные функции доступны для расширения метода.

using Mod приносит x , y , Mod.x , Mod.y , Mod.p и Mod.q . x и y недоступны для расширения метода, но Mod.* доступны.

import Mod.x, Mod.p приносит x , p , Mod.x , Mod.y , Mod.p и Mod.q . Все они доступны для расширения метода.

import Mod: x, p равно import Mod.x, Mod.p

using Mod.x, Mod.p приносит x , p , Mod.x , Mod.y , Mod.p и Mod.q . x и p недоступны для расширения метода, но Mod.* доступны.

using Mod: x, p равно using Mod.x, Mod.p

Насколько я могу судить, using Mod — это единственное использование, которое заботится о том, что экспортируется с помощью Mod .

Было бы очень полезно иметь это резюме в документации модуля! Может ли кто-нибудь подтвердить это, чтобы я мог подготовить PR?

Кажется, наименее спорная версия:

import Tlahuixcalpantecuhtli as Venus: xxxxxxxxx as x9, yyyyyyyy as y8, zzz

Я не против. Однако версия с заданием интересна, проверяя ее:

import Venus = Tlahuixcalpantecuhtli: x9 = xxxxxxxxx, y8 = yyyyyyyy, zzz

Я думал, что буду ненавидеть это, но на самом деле это довольно мило. Мне нравится, что имена, представленные в этом модуле, стоят первыми и наиболее заметными — во многих отношениях это самое важное.

@jakebolewski : Зачем перепривязывать внутренние символы модуля? Это некрасиво, потому что вы действительно не должны этого делать.

Вы можете захотеть импортировать привязки с конфликтующими именами из двух разных модулей, и это позволяет это сделать. Вы также можете полностью квалифицировать их, но если мы поддерживаем псевдонимы, мы могли бы также поддерживать и это.

Некоторые правильно расположенные новые строки делают более читаемыми, IMO:

import Tlahuixcalpantecuhtli as Venus:
    xxxxxxxxx as x9, 
    yyyyyyyy as y8,
    zzz

или

import Venus = Tlahuixcalpantecuhtli: 
    x9 = xxxxxxxxx, 
    y8 = yyyyyyyy, 
    zzz

Я обнаружил, что предпочитаю версию с заданием довольно сильно. В конце концов, импорт — это форма присвоения.

Было бы очень полезно иметь это резюме в документации модуля! Может ли кто-нибудь подтвердить это, чтобы я мог подготовить PR?

@brk00 , дерзай!

@kmsquire , я поработаю над этим сегодня!

Но прежде чем сделать это, я хочу убедиться, что я хорошо это понимаю. В примере с @ssfrr предположим, что я нахожусь в REPL и набираю using Mod . Импортированные функции x и y недоступны для расширения метода. Это означает, что если я объявлю другие функции x и y, у меня больше не будет доступа к методам Mod , которые я когда-то импортировал, только к новым?

Вот что произойдет, если вы попытаетесь расширить функцию, не импортируя ее:

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

Вы можете добавить такой метод:

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

Ну, меня смутило поведение разрешения имени:

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)

Если я назначу новую функцию x до того, как _использую_ ту, которую я только что импортировал с помощью using Mod , ошибка не возникнет. Но если я, например, вызываю x() перед новым назначением (как вы сделали в своем примере), ошибка действительно возникает.

А, интересно! Я не знал об этом. Я случайно вызвал x , чтобы продемонстрировать пространство имен после using .

Может быть, кто-то из основных разработчиков сможет пролить свет. Что происходит, когда вызывается x , что вызывает ошибку при попытке перегрузки?

Я считаю псевдоним, предложенный @carlobaldassi выше, очень полезным на практике.

Мне интересно, может ли import просто вернуть объект модуля, чтобы его можно было напрямую связать с присваиванием, с const или без него? Например

const Shortname = import ModuleWithLongName

вместо

import ModuleWithLongName
const Shortname = ModuleWithLongName

Для этого не потребуется никакого нового синтаксиса, только комбинация существующих элементов синтаксиса. Я понимаю, что в настоящее время import является «утверждением», вызванным исключительно побочными эффектами и не имеющим никакой ценности. Если парсер этого не позволяет, я бы предпочел import("Module") или что-то подобное.

Я думаю, что вариант псевдонимов модулей может быть хорошим, но я также думаю, что у него есть недостаток, о котором никто не упомянул: это может значительно скрыть код. С точки зрения читающего код хорошо иметь только одно описательное имя для модуля.

Довольно тривиально, чтобы посмотреть, хотя? Сокращенные формы также, вероятно, будут стандартизированы для популярных пакетов, таких как np за numpy .

Я бы все же предпочел numpy стандартному np и даже Arrays numpy . numpy звучит как нечто среднее между танцевальным движением 1950-х и смертельной чумой.

можно просто написать:

Base.require("ArgParse")
const ap = Main.ArgParse

для 1.0 синтаксис не нужен

Я чувствую, что у нас должен быть синтаксис для этого. Да, мы можем жить без него, так как вы можете сделать require и присваивание const , но использование require довольно уродливо, а отсутствие удобного синтаксиса отпугивает людей от переименования вещей. как хотят/надо.

Простой трюк с const терпит неудачу, когда нужно использовать псевдоним для макроса, именно так я это делаю, и мне потребовалось некоторое время, чтобы понять:

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)                                                    

В ImportMacros.jl я реализую:

  • <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

См.: https://github.com/fredrikekre/ImportMacros.jl/pull/3 .

@Исмаэль-ВК

Пытаться

<strong i="7">@eval</strong> const $(Symbol("@foo")) = $(Symbol("@bar"))

что уродливее, но короче и, возможно, понятнее, чем локальное определение макроса

@TotalVerb еще раз спасибо за вашу помощь!

Кажется, единственная часть функциональности, отсутствующая в Julia + ImportMacros, — это псевдоним как модуля, так и сочетание псевдонимов переменного количества объектов из такого модуля и импорта других без псевдонимов:

  • <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

Или без 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

Последнее кажется невозможным с макросами:

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

На самом деле это не нужно решать для 1.0, поскольку все предлагаемые синтаксисы являются либо ошибками, либо полной ерундой.

Что должно произойти с псевдонимом модуля, когда исходный модуль переопределен, например, с помощью reload("...") в REPL? При подходе const M = MyModule M по-прежнему ссылается на старый модуль после перезагрузки, поэтому любые вновь определенные имена в MyModule будут доступны только MyModule.name , но не M.name .

В этом случае синтаксис псевдонима должен быть больше, чем синтаксический сахар для const M = Module , но вместо этого убедитесь, что M всегда ссылается на то же самое, что и MyModule .

Это также упростило бы рабочий процесс REPL, поскольку я могу продолжать использовать import модуль во время разработки, но могу ссылаться на него через более короткий псевдоним без необходимости восстанавливать псевдоним (не const ) каждый раз, когда я reload .

На данный момент reload удален, поэтому эта проблема больше не актуальна.

Это также относится к include , или я что-то упустил? В общем случае, когда модуль переопределяется, имя модуля, измененное на import , относится к новому определению, а псевдоним, определенный присваиванием, относится к старому определению, верно?

Нет, это не должно беспокоить. Импортированное имя НИКОГДА не будет изменено при переопределении модуля, включая обычное import . Что изменилось, так это модуль, который будет импортирован, если вы снова импортируете его. Импортное всегда было только привязкой.

Ок, возможно я не совсем понятно выразился, поэтому вот пример: Если создать файл test.jl с содержимым

module Tst
f() = 1
end

вы можете загрузить его, импортировать Tst , определить псевдоним для Tst , и все в порядке:

julia> include("test.jl")
Tst

julia> import Tst

julia> const Tst2 = Tst
Tst

julia> Tst.f()
1

julia> Tst2.f()
1

Если вы сейчас измените test.jl на

module Tst
f() = 2
end

и повторно include в REPL модуль и его псевдоним ведут себя по-разному:

julia> include("test.jl")
WARNING: replacing module Tst
Tst

julia> Tst.f()
2

julia> Tst2.f()
1

Я понимаю, почему это так, но я думаю, что этого не должно происходить, когда вы создаете псевдоним с чем-то вроде import Tst as Tst2 .

Я тестировал это на 0.6.2, и на данный момент я не могу проверить это на 0.7, поэтому я не буду делать это сейчас, если это все еще так.

Вы его вообще не импортируете. Модуль, который вы включаете, уже определен в вашей области. import не работает.

Верно, но не в этом дело. Дело в том, что происходит, когда я делаю что-то вроде гипотетического import Tst as Tst2 . Тогда этот оператор не является невыполнимым, но создает псевдоним.

Если этот псевдоним работает как const Tst2 = Tst , то переопределение модуля Tst приведет к тому, что псевдоним будет указывать на старую версию модуля. Это нежелательно ни в коем случае. Либо псевдоним также должен указывать на новую версию Tst , либо пользователь должен быть вынужден восстановить псевдоним. Но в последнем случае использование старого псевдонима без его переустановки должно вызывать ошибку, а не указывать на старый модуль.

В этом вся суть. Эта проблема связана с добавлением функции в import , а не в определение модуля. Ваш import не работает, и после его удаления в вашем коде больше нет import или using , поэтому он не имеет отношения к этой проблеме. Измените его, чтобы модуль не определялся в той же области, что и import .

import Tst as Tst2 будет работать так же, как import Tst с точки зрения того, что привязывается к этому имени, которое всегда является привязкой, а не какой-то магией, которую вы хотите. Речь НЕ идет о добавлении псевдонима при определении модуля.

Другими словами, ваш код в основном,

a = Ref(1) # include
a = a # import
b = a # alias
a = Ref(2) # include again

И нет никакого смысла жаловаться на поведение b = a , так как a вообще не привязан к a = a . Все, что вы видите, это побочный эффект определения модуля в той же области видимости, что и импорт (т.е. a = a и a = Ref(...) в той же области видимости) в плохом примере.

Хорошо, я просто хотел указать, что в некоторых ситуациях псевдоним, определенный присваиванием, не будет работать должным образом. Возможно, я выбрал неудачный пример. Я подумал, что это может быть важно для синтаксиса, который создает псевдоним модуля (независимо от того, связан ли он с import ), но сейчас это все равно гипотетически. Извините за шум.

эта проблема существует уже давно, хотя некоторые расширения все еще должны быть обсуждены,

import LongPackage as longpkg

само по себе кажется вполне разумным.

Удар. Мне интересно, как выглядит лучшее решение на данный момент? Вот что я делаю:

import LinearAlgebra
const linalg = LinearAlgebra

Пожалуйста, поправьте меня, если есть лучшее решение!

Это текущий стандарт.

Как насчет синтаксиса ~? Я не знаю разветвлений, но у этого есть хорошая эстетика. Кроме того, ~ используется как символ для приближения.

import LongPackage ~ lp

@JeffreySarnoff цитирует Стефана

@StefanKarpinski

Это один из моих наименее любимых вариантов синтаксиса в Python. Чтобы немного изменить смысл, вы должны радикально перестроить всю строку, изменив исходное ключевое слово и порядок всего. Он выглядит совершенно иначе, чем другой импорт, скрывая тот факт, что они делают почти то же самое. Это ужасный синтаксический дизайн, имхо, придающий слишком большое значение внешнему английскому языку.

В этом случае ~ не будет иметь ничего общего с английским аспектом синтаксиса Python. Я думаю, что когда я вижу ~, я просто узнаю математическую концепцию приближения. 10.001 ~ 10.

Так что, в этом смысле, я нахожу ~ на самом деле довольно милым.

import LongPackage ~ lp
import HugePackage ~ hp
import MassivePackage ~ mp
import GiantPackage ~ gp

:-)

хорошо -- забудьте о питоне -- я удаляю этот комментарий в пользу

lp imports LongPlayingRecords
hp imports HewlettPackard
mp imports MilitaryPolice
gp imports PariForNumberTheory

Сначала мне это не нравилось, но так много людей, кажется, естественно хотят написать это с помощью as , я должен сказать, что делать что-то еще кажется извращенным. Его не нужно зарезервировать как ключевое слово или что-то еще, так как это четкий синтаксический контекст.

Язык программирования, который _пытается_ дать людям понять, что это значит!?!

@ДжеффриСарнофф

Язык программирования, который пытается быть лучше Python!!!

@neilpanchal Julia определенно является языком, который во многих отношениях старается быть лучше Python. С другой стороны, совершенно очевидно, что он не изо всех сил старается быть _отличным_ от Python, где Python что-то сделал правильно. Джулия заимствует синтаксические соглашения из Python налево и направо.

Пожалуйста, используйте python as . Они поняли это правильно.

julia> импортировать LightGraphs как lg
ОШИБКА: синтаксис: дополнительный токен "как" после конца выражения

Просто еще один комментарий в размере 0,02 доллара в пользу as , который уходит от Python. Между прочим, теперь, когда я снова использую Джулию с некоторой регулярностью, я скучаю по этому. Не как в Python, а как в Clojure (см. пример ), где пространства имен с областью действия пакета (что хорошо для устранения неоднозначности) можно упростить, чтобы сделать явное использование библиотек гораздо более удобным. Поскольку я предпочитаю избегать using (т.е. импорта подстановочных знаков), наличие некоторого синтаксического сахара в import Gadlfy as gf без добавления строки const gf = (что я сейчас делаю) быть под рукой.

Еще один комментарий, поддерживающий синтаксис as :heart: Жаль, что в будущих релизах Джулии этого не будет.

Я тоже хочу, чтобы это произошло. Тем более, что это не критическое изменение (верно?).

Я думаю, что это будет реализовано в какой-то момент (я бы попробовал сам, если бы у меня было время), но в то же время пакет ImportMacros имеет почти все функции, запрошенные здесь (хотя в нем отсутствует документация) :

<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>

Я не знал о ImportMacros .jl, спасибо, что указали на это. Если эта функциональность существует в пакете, я думаю, не так уж важно иметь ее в базовом языке.

Я все еще думаю, что это было бы хорошо иметь на языке.

Импорт - это IMO именно то, для чего вы почти _всегда_ хотите полагаться на соглашения базового языка, а не заключать в макросы или добавлять зависимость пакета. import ... const лучше использовать в качестве резерва, даже (особенно) если базовый язык никогда не расширялся, чтобы сделать этот двухэтапный процесс более кратким.

В частности, немного некрасиво видеть

using ImportMacros
<strong i="6">@using</strong> OtherPackage as ...
<strong i="7">@using</strong> ThirdPackage as ...

потому что немакрос using торчит.

Как насчет того, чтобы import возвращал модуль/функцию/все, что он на самом деле приносит в область видимости?
Тогда вы можете написать
lg = import LightGraphs
baz = import foo.bar
baz, kit, kat = import foo : bar1, bar2, bar3
Единственное, что нужно изменить, это функцию импорта

Мне очень нравится решение @DhruvaSambrani , потому что оно соответствует общему подходу Джулии «все есть выражение».

Единственное, что для того, чтобы быть последовательным, выражение импорта по-прежнему должно преформировать побочный эффект включения полного имени в область действия (возможно, это не катастрофа). Это также может быть в специальном регистре, чтобы не импортировать полное имя, но специальный регистр является грубым.

В OCaml (язык с лучшими модулями!) вы можете сделать что-то вроде:

module LG = LightGraphs

Важным отличием здесь является то, что полное имя каждого модуля, скомпилированного с помощью программы OCaml, автоматически попадает в область действия.

Конечно, использование import LightGraphs as LG имеет то преимущество, что «все» уже знают его по Python, но лично мне кажется, что лучше повторно использовать синтаксис присваивания для чего-то, что буквально является присваиванием.

Или более обширным изменением было бы введение «пространства имен» в качестве типа (idk, если это уже сделано). Затем import может расширить модуль/что угодно в пространство имен и вернуть пространство имен. Но это нарушит весь код Julia, потому что простое выполнение import mod_name не сделает пространство имен mod_name доступным в глобальной области видимости. Если можно каким-то образом сделать пространство имен mod_name доступным для глобального, когда импорт используется без предшествующего ему присваивания, то это не проблема.

Или просто добавьте новую функцию import_as_ns(mod_name) :: Namespace , которая будет чистой функцией, возвращающей все функции/модули и т. д. в одном пространстве имен.

это соответствует общему подходу Джулии "все является выражением"

Однако import и using являются особенными, потому что они используются только для их побочных эффектов и только на верхнем уровне.

Между прочим, учитывая, что этот вопрос был открыт в течение длительного времени, мне интересно, захочет ли Triage вернуться к нему и принять решение. Я бы рекомендовал закрыться, т.к.

  1. import Foo; const Bar = Foo обеспечивает идеальное решение, когда пользователь не возражает против Foo в пространстве имен,
  2. ImportMacros.jl должен справиться с остальным,
  3. ни один из них не используется очень часто в практическом коде, чтобы гарантировать особый синтаксис.

Скажу, что после всего этого времени я все еще скучаю по этому (и я также использую ImportMacros в своем собственном коде).

Я действительно думаю, что стиль кофе, который вы пишете, влияет на потребность (или желание) в этом. Когда я пишу машинное обучение или научный код, я не сильно по этому скучаю. Когда я писал другой код, который использует несколько пакетов с длинными именами, я обнаружил, что больше всего использую ImportMacros.

Импорта макросов достаточно. Я просто согласен с комментариями, что это не выглядит так чисто.

Я также с нетерпением жду того дня, когда я смогу использовать автоформатер в своем редакторе для сортировки импорта... но вряд ли он будет знать, что делать с @using ...

Я также согласен с @kmsquire , что это не такая уж большая проблема, и ImportMacros должно быть достаточно. Но любой метод должен быть задокументирован, чтобы он больше не возникал.

Хотя мне все равно нравится сахар m=import Module 😅

Я не хотел, чтобы мое сообщение подразумевало, что это не имеет большого значения.

Я бы все же предпочел синтаксис для этого.

Импорта макросов достаточно. Я просто согласен с комментариями, что это не выглядит так чисто.

Я согласен. Также для:

using ImportMacros
<strong i="8">@using</strong> OtherPackage as ...

Это немного раздражает при представлении языка новичкам или новичкам в программировании, чтобы объяснить, почему данный скрипт начинается с этой несогласованности, поскольку это усложняет поток и смещает фокус обучения.

ни один из них не используется очень часто в практическом коде, чтобы гарантировать особый синтаксис

Причина, по которой люди редко используют ImportMacros в реальном коде, может быть связана с компромиссом. Например, я бы использовал import ... as ... почти каждый раз, но, вероятно, не за счет необходимости сначала импортировать другой пакет, чтобы сделать это. Это больше, чем дополнительная зависимость, это также оказывает влияние с точки зрения «ощущения» (и некоторым людям нравится, чтобы их код выглядел гладким (особенно в Python или Julia) 🤷‍♂️ , и это using/@using дает довольно " хакерский" настрой на введение скрипта).

Так что да, «ImportMacros достаточно». Для людей, которые действительно хотят его использовать. Но в том-то и дело, что это не жизненно важный вопрос, и люди могут работать и без него. Тем не менее, я твердо верю, что именно эти мелочи, как правило, способствуют тому, чтобы синтаксис языка придерживался, поэтому этот монстр-тред добавляет свою поддержку в базу.

Я думаю, что import ... as ... — это синтаксический сахар, который определенно стоит того, его действительно легко понять и объяснить, и он особенно полезен в таких языках, как Julia, в котором пакеты имеют довольно длинные имена (очевидно, это имеет значение только при условии, что люди не хотят делать using ... ).

И тот факт, что Python реализовал его первым, не должен иметь значения; Джулия должна стремиться к лучшему синтаксису, прежде чем пытаться дистанцироваться от других языков и/или подчеркивать свою индивидуальность.

Наличие синтаксиса для этого и соглашений вокруг популярных модулей (я думаю о том, что это всегда import numpy as np и import matplotlib.pyplot as plt ) также дало бы краткую альтернативу выполнению using SuchAndSuch и использованию export измененные имена (чего я обычно избегал в «библиотечном» коде).

@ДоминикМаковски :

представление языка новичкам или новичкам в программировании, чтобы объяснить, почему данный скрипт начинается с этой несогласованности

Я не понимаю, почему вы думаете, что это несоответствие, это просто управление пространством имен, что является нормальной частью программирования в целом.

Кроме того, переименование модулей может быть не тем, с чем в первую очередь столкнутся новички в Julia.

Я не говорю, что нет варианта использования, но может быть слишком редко, чтобы оправдать собственное ключевое слово. На данный момент поиск using ImportMacros дает <10 уникальных результатов на Github.

Я бы использовал import... as... почти каждый раз, но, вероятно, не за счет необходимости сначала импортировать другой пакет, чтобы сделать это

Это работает в обе стороны: если (очень незначительная) стоимость использования облегченного пакета, такого как ImportMacros или дополнительного символа ( import LongFoo; const Foo = LongFoo ), не стоит выгоды, то выгода может быть не такой уж большой. .

Джулия должна стремиться к лучшему синтаксису, прежде чем пытаться дистанцироваться от других языков и/или подчеркивать свою индивидуальность.

Я не уверен, почему вы думаете, что это мотивация в этой дискуссии.

В некоторых случаях вы можете захотеть использовать as , когда присваивание const не совсем работает:

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

Да, это можно обойти, но неудобно. Учитывая, что это часто удобно, а иногда необходимо, и что import FooBar as FB является общепринятым «наименее удивительным» синтаксисом, кажется, что мы должны использовать его. Все это нужно для того, чтобы кто-то сделал пиар, реализующий это.

Новый пользователь Юлия здесь.
У меня в основном R фон, также я использую Python. У меня плохой опыт работы с общим пространством имен пакетов, к которому приводят такие механизмы загрузки, как using . Риск столкновения один, но только с точки зрения удобочитаемости, постоянно думать о модуле каждого метода — это умственное бремя.

Я не знаю, насколько сложно добавить этот синтаксис? Выполнимо ли это для новичка?

Наверное, не очень легко. Это будет включать в себя взлом кода анализатора, написанного на Scheme, и проводку изменений через AST, потенциально затрагивая также некоторый код C.

Это было бы хорошей возможностью избежать конфликта имен функций в нескольких модулях.

Из обсуждения в slack добавлю несколько мыслей

Интересно, сделает ли это людей менее осторожными при выборе имен функций и правильном добавлении методов к правильным функциям. Тот факт, что, например,

numpy.sin
sympy.sin

являются разными функциями - это верный способ реализовать две версии вашей функции, если вы хотите, чтобы она работала как для числовых, так и для символьных входных данных. Если такое пространство имен станет широко распространенным и в julia из-за удобства, мы можем упустить много повторного использования кода? То есть, я боюсь, что это может дать нам меньше « Это побуждает людей работать вместе », как это было сказано Линдоном в https://www.oxinabox.net/2020/02/09/whycompositionaljulia.html .

@baggepinnen Я не думаю, что это что-то меняет в этом отношении.

В Julia методы, реализованные в разных модулях, не объединяются путем импорта обоих методов в одно и то же пространство имен.

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"

Чтобы это работало, нужно явно добавить отправку в исходную функцию:

julia> Base.sin(s::String) = Foo.sin(s)

julia> sin("wat")
"WAT"

Насколько я могу судить, псевдоним модуля не может изменить способ отправки функций.

И, честно говоря, некоторые функции с одинаковыми именами _должны_ жить в разных пространствах имен. Функция find_delta в числовой библиотеке будет делать что-то, не связанное с find_delta в библиотеке различий файлов. Как только ваш код станет настолько полиморфным, что он найдет способ выполниться при некорректном вводе, вы попадете на территорию JavaScript, а этого никто не хочет.

@ninjaaron Возможно, я не смог передать некоторые нюансы в своем беспокойстве выше. Меня не беспокоит, что это изменение изменит что-либо, кроме стратегии, которую люди выбирают при реализации библиотек.
Человек, реализующий Foo , столкнулся с предупреждением вроде

julia> using .Foo
WARNING: using Foo.sin in module Main conflicts with an existing identifier.

может выбрать либо просто import .Foo.sin as sin2 , либо подумать, действительно ли он хотел предоставить новую отправку за Base.sin . Моя точка зрения заключалась в том, что если станет очень легко просто рассматривать их как разные функции, возможно, это станет слишком распространенным, даже в ситуациях, когда они действительно должны быть разными методами одной и той же функции. Нынешняя ситуация, когда работать с разными функциями с одинаковыми именами немного сложнее, имеет приятный побочный эффект: люди разговаривают друг с другом и делают все возможное, чтобы выяснить, действительно ли это одна и та же функция или нет. Я не возражаю против возможности иметь разные функции с одним и тем же именем, что, конечно, может быть очень полезно. Я даже не уверен, важно ли мое беспокойство или нет, но решил, что стоит поднять его, чтобы убедиться, что оно было рассмотрено.

@baggepinnen Это имеет смысл, и нет ничего плохого в том, чтобы поднять этот вопрос. Я не думаю, что это будет иметь большое значение для людей, создающих библиотеки, поскольку псевдонимы модулей будут влиять только на пользователей библиотек. Я предполагаю, что более простое использование псевдонимов модулей приведет к меньшему количеству пользователей, _жалующихся_ на конфликты имен, но я был бы удивлен, если бы это оказало огромное влияние на API.

Когда я пишу библиотеку, я обычно очень хорошо понимаю, хочу ли я добавить отправку к функции в Base или я хочу сохранить ее отдельно. Я бы предположил, что и большинство других авторов библиотек тоже.

@ninjaaron Я думаю, что если текущая конвенция использует конкретную реализацию для отправки, например

numpy.sin
sympy.sin

использование import numpy.sin as np_sin в качестве дополнительной опции для пользователя/разработчика не должно влиять на текущую кодовую базу.
Единственная проблема будет касаться только функции/интерфейса разоблачения.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги