Julia: Удалите require, import и, возможно, importall и объедините с использованием

Созданный на 14 авг. 2014  ·  72Комментарии  ·  Источник: JuliaLang/julia

Из списка рассылки:

Стефан:

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

используя Фу
используя Foo: бар

Первый загрузит и создаст привязку для Foo, сделает его экспорт доступным как «мягкие» привязки (что сейчас делает использование). Второй также загрузит и создаст привязку для Foo и сделает bar доступным как «жесткую» привязку (что сейчас делает импорт)».

Кажется, это все еще довольно путаница для новичков.

breaking design modules speculative

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

Мне нравится симметрия import и export . (Как кто-то где-то указал.)

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

Нам тоже как-то понадобится функциональность import Foo , где вы просто получаете Foo и больше ничего.

using Foo: ?

using Foo: Foo ?

Чтобы привязать Foo к модулю Foo (и больше ничего):
import Foo
Для привязки Foo к модулю Foo, x к Foo.x, y к Foo.y
import Foo: x, y
Чтобы привязать Foo к модулю Foo, и все экспортированные имена Foo привязываются без уточнения:
import Foo: *

Это могло бы быть using вместо этого, но я чувствую, что это больше в духе import .

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

Существует веский аргумент в пользу того, что конструкция, которая делает доступными все экспортированные привязки модуля, должна делать их мягкими ( using ), а не жесткими ( importall ) привязками. Предположим, модуль A использует модуль B и определяет foo(::Any) . Если более поздняя версия модуля B также определяет и экспортирует foo(::Any) , вы не хотите, чтобы они сбивали друг друга. Вы также не хотите, чтобы модуль B определял foo(::Int) и чтобы модуль A иногда вызывал этот метод вместо определенного им метода, потому что он более специфичен, или чтобы вам приходилось перечислять все идентификаторы, которые вы хотите получить из модуля B. чтобы избежать импорта одного конфликтующего идентификатора.

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

Короче говоря, мне нравится предложение @StefanKarpinski . Концептуально нам нужны как жесткие, так и мягкие привязки, но мы можем получить все полезные поведения с помощью одного ключевого слова.

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

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

Проблема с пустым завершающим двоеточием заключается в том, что в настоящее время мы ищем имя на следующей строке — другими словами, using Foo: считается неполным.

Связанный: #4600

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

Я также думаю, что текущая перегрузка использования Foo и использования Foo.Bar
проблемный; он смотрит внутрь Foo, но не внутрь Foo.Bar (если только Foo.Bar не
модуль?)

Я думаю, что в предложении Стефана это рассматривается с

используя Фу: Фу

В четверг, 14 августа 2014 г., [email protected] написал:

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

Я также думаю, что текущая перегрузка использования Foo и использования Foo.Bar
проблемный; он смотрит внутрь Foo, но не внутрь Foo.Bar (если только Foo.Bar не
модуль?)


Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/JuliaLang/julia/issues/8000#issuecomment-52202142 .

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

Это ошибка дизайна (и вызывает предупреждение), так что это не имеет значения

Мне больше всего нравится вариант, предложенный @ssfrr выше.

Существует ли базовое соглашение о том, что пакеты имеют одну и только одну точку входа модуля? -- To import Foo означает импорт модуля Foo из пакета Foo . Любой другой модуль должен быть подмодулем Foo . Это означает, что существует неявное соответствие между пакетом Foo, Foo.jl в пакете и module Foo внутри него.

Я спрашиваю b/c Мне интересно, можно ли также использовать import для локального/относительного импорта. Скажем, проект имеет src/foo.jl и src/bar.jl , а затем foo.jl :

import bar

будет импортировать из src/bar.jl . Это улучшение по сравнению с использованием include , поскольку оно работает в модульной системе.

Да, ожидается, что пакеты, точки входа пакетов и модули будут соответствовать друг другу. Вы можете нарушить это соглашение, если вам нужно, но в этом нет особой необходимости, поскольку модули предназначены только для именования и не являются функциональными единицами. Идея, которую вы поднимаете, - это # ​​4600, за исключением синтаксиса для относительного импорта: import .bar , тогда как import bar является абсолютным импортом.

@StefanKarpinski Действительно ли import .bar ищет «bar.jl» в локальном каталоге? У меня сложилось впечатление, что import .Bar относится только к подмодулю уже текущего родительского модуля.

ОБНОВЛЕНИЕ: Да, это то, что вы предлагаете в # 4600. Извиняюсь.

:+1:. Если мы собираемся сделать это, 0.4 будет подходящим временем для этого.

:+1: Пожалуйста, почистите кое-что из этого (для версии 0.4!)

вместо того, чтобы иметь два механизма импорта, чтобы различать расширение или нет, почему бы не сигнализировать о расширении на самом сайте расширения с помощью ключевого слова или аннотации в определении функции, например, переопределить ключевое слово перед функцией? Аналогично аннотации @Override в Java.

Это имеет то преимущество, что ясно видно, что функция переопределяет другую (и, следовательно, является методом).

Это уже возможно, @ssagaert. Вы делаете это, явно записывая имя модуля в определении функции (например, Base.print(…) = … ), и, похоже, это стиль, к которому сходятся многие люди. Единственным камнем преткновения является то, что синтаксис не работает для всех возможных имен (таких как .+ и т. д.).

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

:+1: к предложению @ssagaert использовать @override исходное сообщение об ошибке, когда кто-то пытается расширить метод без его предварительного импорта, выглядит следующим образом:

ERROR: error in method definition: function Foo.x must be explicitly imported to be extended

Так что, может быть, @extend больше подходит? Я не являюсь носителем английского языка, но я понимаю, что _override_ означает что-то вроде: отменить, аннулировать, аннулировать, отменить, аннулировать, аннулировать, прекратить и т. д.

Я думаю, что это более явно:

<strong i="13">@extend</strong> Base.show(...) = ...

Чем:

import Base: show

# ... several lines here

Base.show(...) = ...

@Ismael-VC Base.show(...) = ... уже работает, ничего не импортируя. import необходим только в том случае, если вы хотите, чтобы show(...) = ... расширил Base.show .

Слово переопределения @Ismael-VC было просто предложением. Это может быть расширение или что-то еще значимое. Также не должно быть @ , поскольку в julia это означает макрос ( @Override относится к Java, где это аннотация).

@simonster спасибо, я этого не знал!

@ssagaert , так ты имеешь в виду ключевое слово? Я пробовал что-то вроде этого, но я все еще не умею делать макросы:

module Extend

export <strong i="9">@extend</strong>


macro extend(x)
    mod = x.args[1].args[1].args[1]
    met = x.args[1].args[1].args[2]
    imp = :(Expr(:import, $mod, $met))
    :(Expr(:toplevel, $imp, $(esc(x))))
end


end

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

julia> using Extend

julia> type Foo end

julia> <strong i="13">@extend</strong> Base.show(x::Foo) = Foo
:($(Expr(:toplevel, :($(Expr(:import, Base, :show))), show)))

julia> parse("import Base.show; Base.show(x::Foo) = Foo")
:($(Expr(:toplevel, :($(Expr(:import, :Base, :show))), :(Base.show(x::Foo) = begin  # none, line 1:
            Foo
        end))))

Я думаю, что общий и работающий макрос @extend не изменит семантику механизма импорта.

@Ismael-VC Да, но мне также нравится «уловка» @mbauman .

Я думаю, было бы неплохо иметь возможность использовать . отдельно для обозначения «это». поэтому вы можете написать такие выражения, как:
import Base: . (что означает импорт Base.Base )
или
using ..

я почти уверен, что для этого потребуется https://github.com/JuliaLang/julia/pull/11891#issuecomment -116098481. может быть, достаточно допустить пробелы перед . , но не после него, чтобы разрешить случай неоднозначности?

Я считаю, что require уже устарела. Возможно, было бы неплохо добавить более гибкую точечную нотацию при импорте модулей в версии 1.0, но я сомневаюсь, что мы собираемся что-то изменить здесь до заморозки функций в версии 0.6.

После вчерашнего обсуждения этого вопроса я склоняюсь к чему-то вроде предложений в https://github.com/JuliaLang/julia/issues/8000#issuecomment -52142845 и https://github.com/JuliaLang/julia/ .

using A: x, y    # hard imports x and y from A

using A: A       # hard imports just the identifier `A`

using A: ...     # soft imports all of A's exports

using A     # equivalent to `using A: A, ...`

using A.B   # A.B must be a module. equivalent to `using A.B: B, ...`

using A: ..., thing1, thing2    # import all exports plus some non-exported things

Альтернативой может быть сохранение import вместо using :

import A             # hard binding for the module `A`
import A: ...        # soft bindings for all names exported by `A`
import A: x, y       # hard bindings for `x` and `y` from `A`
import A: x, y, ...  # equivalent to doing both of the previous two

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

Редактировать: есть желание добавить к этой схеме некоторое сокращение для import A; import A: ... , что примерно соответствует тому, что в настоящее время делает using A (единственное отличие состоит в том, что using A настоящее время программно импортирует A ); это может быть либо по-прежнему using A , либо было предложено import A... .

Да, я тоже думаю, что это хорошее предложение. В основном сводится к тому, предпочитает ли кто-то using или import .

В дополнении, мы могли бы сохранить using A в качестве сокращения для import A; import A: ... — в сочетании с избавлением от текущего поведения, когда каждый модуль имеет экспортированную привязку для себя, поэтому using A вызывает должна быть доступна мягкая привязка к A .

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

Мне нравится симметрия import и export . (Как кто-то где-то указал.)

Всегда делать «жесткие привязки» не кажется самым безопасным по умолчанию с точки зрения расширения метода. Было связанное, но несколько отдельное предложение о необходимости квалификации модуля для расширения метода, что могло бы устранить необходимость в «жестких привязках».

Для этой цели также необходимы жесткие привязки:

import Package: x
x = 1   # gives an error

И что особенно важно, это предложение не всегда будет делать жесткие привязки — только для вещей, которые вы явно перечисляете. Требование, чтобы кто-то говорил import вместо using , не добавляет безопасности.

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

У нас все еще есть предупреждение о перезаписи импортированной привязки?

Я думаю, что требование квалификации модуля является хорошей идеей. Прямо сейчас, чтобы узнать, вводится ли функция или расширяется метод, вам нужно просмотреть все содержимое пакета на наличие оператора import A: func .

У нас все еще есть предупреждение о перезаписи импортированной привязки?

Я считаю, что это на самом деле ошибка сейчас.

Существует еще одно предложение, которое сохраняет оба ключевых слова, но все же немного упрощает ситуацию:

  1. Добавьте синтаксис import A: ...
  2. Устаревший using A:
  3. В using A.B потребуйте, чтобы A.B был модулем, и задокументируйте, что это сокращение для import A.B; import A.B: ... .

Таким образом, все можно сделать всего за import , но для удобства доступно using X . К этому также будет особенно легко перейти.

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

Мой любимый подход:

  • Требовать явный префикс модуля для расширения
  • Если вам нужно только имя модуля, а не его символы, используйте using A: A
  • Удалите import и тип привязки, который он создает.

Что нужно сделать для реализации https://github.com/JuliaLang/julia/issues/8000#issuecomment -327512355:

  • изменить поведение using A на жесткий импорт A
  • удалить поддержку using A: x
  • удалить поддержку using A.x , где x не является подмодулем
  • удалить поддержку import A.x , где x не является подмодулем
  • добавить поддержку синтаксиса ... в import

using A: x часто используется и очень полезен. Вы говорите, что хотите x в своем пространстве имен, но не хотите его расширять. В import A: x вы говорите, что хотите иметь возможность расширять x . Существует существенное различие между наличием функции, доступной для использования, и возможностью ее расширения.

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

Я бы предпочел, чтобы был один способ написать import A: x вместо того, чтобы быть эквивалентным import A.x .

Я голосую за import A: x , так как мы тоже можем это сделать; import A: x, y, @z , но import A.x, A.y, a.@z выглядело бы некрасиво.

Означает ли это удаление из 1.0, что у нас останутся как using , так и import для 1.0? Это немного неудачно, на мой взгляд.

Как насчет:

  • Принудительный префикс модуля для расширения. Это сделает код более ясным, что расширение предназначено, а не случайно из-за импорта в какой-то другой файл.
  • using A становится import A: ...
  • using A.X ( X — это модуль) становится импортным A.X: ...
  • using A: X ( X не является модулем) становится import A: X
  • import A: X не изменяется, но вы не можете автоматически расширять X (см. первый пункт)
  • удалить ключевое слово using

Я пропустил какой-то вариант использования? Может это уже предлагалось...

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

Я думаю, что это по сути то же самое, что и мое предложение выше, которое получило достаточную поддержку. @JeffBezanson действительно хочет сохранить using A по крайней мере для простоты использования и using A: x , потому что, по-видимому (я не в восторге от этого), важно иметь возможность импортировать привязку в таким образом, что вы не можете расширить его. Есть несколько предложений пойти в другом направлении и заменить import на using , но ни одно из них не получило большой поддержки ( import кажется более фундаментальным).

Я думаю, что разница в:

  • import A: x, y # жесткие привязки для x и y из A

Предполагая, что значение «жесткой привязки» таково, что вы можете расширить его без префикса модуля, в моей версии нет жестких привязок. Если вы хотите расширить, вы добавляете префикс модуля именно там, где вы его расширяете. Никаких жутких операторов import в других файлах, меняющих значение того, является ли что-то расширением или нет.

и using A: x , потому что, по-видимому (я не в восторге от этого), важно иметь возможность импортировать привязку таким образом, чтобы вы не могли ее расширить.

Разве принудительный префикс модуля не связан с этим? Или мы говорим о не модулях, таких как:

module M
    x = 1
end

Оба import M: x; x = 2 и using M: x; x = 2 дают одно и то же предупреждающее сообщение, поэтому я не вижу, в чем проблема...

Сохранение using A для простоты более чем import A: ... кажется мне немного чрезмерным.

Разве принудительный префикс модуля не связан с этим?

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

Сохранение использования A для упрощения импорта A: ... кажется мне чрезмерным.

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

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

  • 1 ключевое слово + требуется префикс для расширения
  • два ключевых слова, одно для обычного (нерасширяющего) использования, а второе для расширенного использования. В этом случае я предлагаю using и extending . import — это хорошо, но extending делает очевидной причину существования второго ключевого слова.

В любом случае я предлагаю, чтобы using был таким, как сейчас, с добавлением одного из следующих элементов для привязки только модуля Foo :

  • using Foo: nothing (сейчас работает)
  • using Foo: Foo (сейчас работает)
  • using Foo: (можно добавить позже)

Тогда extending должен вести себя идентично using с той лишь разницей, что вы можете расширить привязки, введенные с помощью extending , и, возможно, запретить extending Foo , чтобы он быть явным.

Текущее поведение

| | сделать доступным (используя) | сделать расширяемым (импортировать)|
| ------------------- | -------------------------- | ---------------------- |
| только модуль | using module: module или using module: nothing | import module |
| все экспортируется | using module (побочный эффект: также действует как import module ) | ? |
| определенные вещи | using module: x,y | import module: x,y |

Предложение

| | сделать доступным (используя) | сделать расширяемым (импорт) |
| ----------------- | ------------------------ | -------------------------- |
| только модуль | using module | import module |
| все экспортируется | using module: * | import module: * |
| определенные вещи | using module: x,y | import module: x,y |

Хорошая вещь в этом заключается в том, что больше импорта соответствует написанию большего количества. Т.е. вы начинаете с using module , и если вы хотите импортировать переменную непосредственно в пространство имен, вы добавляете : x вместо удаления nothing или module . Это также означает, что самая короткая вещь, которую вы набираете, включает в себя минимум.

Вы также можете сделать using: *,x , чтобы сделать все экспортированное доступным, и x , что не было экспортировано.

Компромисс для обратной совместимости:

| | сделать доступным (используя) | сделать расширяемым (импорт) |
| ----------------- | ------------------------ | -------------------------- |
| только модуль | using module: | import module: |
| все экспортируется | using module: * | import module: * |
| определенные вещи | using module: x,y | import module: x,y |

оставьте около using module и import module с текущим поведением для обратной совместимости, но не рекомендуется.

@FelixBenning : import Module настоящее время (само по себе) не делает ничего расширяемым больше, чем using Module , он просто загружает код и вносит Module (и ничего больше) в пространство имен .

Просто чтобы отразить то, что я сказал о слабине, и чтобы это не исчезло в люфте:

Я не думаю, что установка using X: * по умолчанию для предоставления каждой экспортируемой вещи по сравнению с просто using X обязательно заставит людей более осторожно относиться к тому, что они импортируют. Я знаю, указывать на то, как это делают другие, считается дурным тоном, но Python в основном имеет эту семантику с их import X и import X: * , но их экосистема усеяна этими звездными импортами 🤷‍♂️ (и они классно ненавидят это) Я не думаю, что немного более длинный текст, который нужно набирать, мешает людям делать то, что они считают наиболее удобным: просто импортировать/использовать все и позволить компилятору понять это. Вот почему я с опаской отношусь к волшебному средству, заставляющему людей явно писать эту звезду.

Кроме того, import module: * и using module: * недоступны для предлагаемого значения. У него уже есть значение, поскольку * является действительным идентификатором Julia и может быть импортирован/использован так же, как + или слово mul .

@tpapp либо я снова неправильно понял документацию, либо import Module делает Module.x расширяемым. В то время как using Module: x не делает Module.x расширяемым. Следовательно, import Module делает что-то доступным для расширения, а using Module делает то же самое, поэтому я отметил, что использование имеет этот побочный эффект.
grafik
(из https://docs.julialang.org/en/v1/manual/modules/)

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

@mbauman хорошая мысль - я забыл об этом. Меня действительно не волнует * , просто структура using отражает то, что делает import , с разницей между импортом и использованием, независимо от того, становятся ли вещи расширяемыми. Итак, если есть более подходящий символ - all , __all__ , everything , exported , ...? Я полностью за. Я просто думаю, что импорт большего количества, вероятно, должен быть отражен, набрав больше.

Но если вы совсем этого не хотите, вы, конечно, можете пойти и на

| | сделать доступным (используя) | сделать расширяемым (импорт) |
| ----------------- | ------------------------ | -------------------------- |
| только модуль | using module: module | import module: module |
| все экспортируется | using module | import module |
| определенные вещи | using module: x,y | import module: x,y |

или

| | сделать доступным (используя) | сделать расширяемым (импорт) |
| ----------------- | ------------------------ | -------------------------- |
| только модуль | using module | import module |
| все экспортируется | using module: module | import module: module |
| определенные вещи | using module: x,y | import module: x,y |

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

using A делает A.f расширяемым, а _not_ f сам по себе. Чтобы расширить _just_ f , не объявляя, из какого модуля вы хотите его расширить, вы должны явно указать $# import A: f . В противном случае вам все равно придется его квалифицировать.

Проверьте следующее для семантики using

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> using .A

julia> f()
"no args in A"

julia> f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[5]:1

julia> f(x) = "one arg where?"
ERROR: error in method definition: function A.f must be explicitly imported to be extended
Stacktrace:
 [1] top-level scope at none:0
 [2] top-level scope at REPL[6]:1

julia> A.f(x) = "one arg where?"

julia> f(1)
"one arg where?"

И это для семантики import :

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> import .A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[4]:1

julia> A.f()
"no args in A"

julia> f(1)
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[6]:1

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[7]:1

julia> f(x) = "one arg where?"
f (generic function with 1 method)

julia> f(1)
"one arg where?"

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[10]:1

julia> A.f(x) = "one arg where in A"

julia> A.f(1)
"one arg where in A"

@FelixBenning : да, я думаю, ты неправильно понял. FWIW, я думаю, что "... делает Foo.x расширяемым" - это запутанный способ приблизиться к различию --- вы всегда можете определить методы для полных имен функций. Что происходит с using Foo: x , так это то, что сам Foo не будет перенесен в пространство имен.

Кстати, перечитывая эту тему, я задаюсь вопросом, привел ли # 25306 нас к своего рода локальному оптимуму, и единственное решение, которое остается, — нужен ли нам нерасширяемый импорт в пространство имен (в настоящее время using Foo: f ). Но поскольку это ломается, глава руководства, возможно, выиграет от переписывания тем временем, многие пользователи находят все это запутанным.

Вот как я бы подошел к статусу-кво в документах:

  1. using M загружает модуль и переносит M и его экспортированные символы в пространство имен. Это единственный случай, когда экспорт имеет значение.
  2. когда у вас есть M , вы можете использовать полные имена, такие как M.y , чтобы (а) получить доступ к неэкспортируемым символам и (б) добавить методы к функциям, независимо от того, экспортируются они или нет.
  3. Julia запрещает вам добавлять методы к функциям, если вы не используете полные имена, такие как M.f или...
  4. ... import M: f , тогда вы можете просто использовать f при определении методов.
  5. import M и using M: x предназначены для внесения в пространство имен только M или x (а не M ) соответственно. Некоторым людям нравится использовать эти формы в коде пакета, чтобы убедиться, что их пространства имен остаются чистыми (ссылка на соответствующие руководства по стилю здесь).

Порядок более или менее отражает варианты использования, встречающиеся при более сложном использовании. Все вышеперечисленное относится к подмодулям с M.A вместо M .

Что насчет этого:

  • using M включает в себя M
  • using M: x включает M.x в область действия (как x )
  • using M: ... включает все экспортированные символы M в область видимости.
  • using M: x, ... включает в область действия все экспортированные символы M , а также x (которые могут быть неэкспортированными)
  • Нет import . Вам нужно использовать полное имя для расширения функции. (Или using M; const foo = M.foo , как это уже можно сделать сейчас.)
  • Во всем вышеперечисленном M также может быть подмодулем, например, Foo.Bar и x также могут быть x as y с текущим значением.

Или мы используем import вместо using , что делает его равным https://github.com/JuliaLang/julia/issues/8000#issuecomment -355960915.

Очень распространенное использование (особенно в "скриптах", но и в пакетах для определенных стилей есть)

using Foo, Bar, Baz

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

@tpapp

Я думаю, вы неправильно поняли. FWIW, я думаю, что «... делает Foo.x расширяемым» - это запутанный способ приблизиться к различию - вы всегда можете определить методы для полных имен функций.

Итак, могу ли я продлить Foo.x после using Foo: x ? Потому что если да, то документация неполная (см. мой скриншот). Если нет, то я прекрасно понял, как работают эти операторы, и совершенно очевидно, что import Foo сделал что-то, чтобы сделать Foo.x расширяемым. Поэтому он буквально делает Foo.x доступными для расширения. Во всех смыслах этих слов. Конечно, это не делает x доступными для расширения, но import Foo: x для этого и предназначено.

Итак, могу ли я продлить Foo.x после using Foo: x ?

Нет, если вы каким-то образом не внесете Foo в пространство имен (например, using Foo ).

Я прекрасно понял, как работают эти утверждения

Я не совсем уверен в этом, однако, если у вас есть вопросы о модулях и пространствах имен, пожалуйста, используйте форум Discourse.

совершенно очевидно, что import Foo сделал что-то, чтобы сделать Foo.x расширяемым

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

тогда документация не полная

Я думаю, что это так, в некотором роде причудливым образом. В этом конкретном примере, если все, что вы делаете, это using MyModule: x, p ; тогда нет доступных методов для расширения, поэтому таблица верна.

Я согласен, что это могло бы быть написано лучше, как я сказал выше. Многих людей, не привыкших к пространствам имен, это сбивает с толку. И, TBH, весь этот цирк using / import слегка сбивает с толку, отсюда и эта проблема.

@tpapp

Итак, вот в чем дело: абсолютно не очевидно, что вы можете расширить каждую функцию полным именем, если модуль находится в пространстве имен. Я знаю, что это текущее поведение, но я не думаю, что кто-то, кто этого не знает, уже предположил бы это. Тем более, что нахождение в пространстве имен не всегда означает, что оно еще и расширяемое. Если я сделаю using module:x , я не смогу расширить x . Хотя я могу расширить x , если использую import module:x . Поэтому разумно предположить, что разница между using и import заключается в том, можете ли вы расширить импортированные функции.

Используя это предположение, было бы разумно, если бы using module позволяло использовать только module.x , но не разрешало расширение module.x . В то время как import module позволит расширить module.x . Итак, если вы примете это предположение и прочитаете документацию, вы обнаружите, что две вещи здесь неверны.

  1. using module позволяет расширить module.x . Таким образом, с точки зрения учащихся using module имеет побочный эффект , который также действует как import module , т.е. делает module.x расширяемым. И сделать вещи расширяемыми — это import , а не using
  2. в отличие от import , using не только делает доступным module , но и все в нем.

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

Хорошей альтернативой является полное удаление импорта, как предлагает @martinholters . У вас просто не может быть двух слов, которые просто случайным образом используются для определенных вещей. Если import означает расширение возможностей при импорте определенных функций, а using module: foo не позволяет этого, то такое же поведение должно происходить, когда вы включаете только module в пространство имен.

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

Либо быть в пространстве имен достаточно, тогда я также должен иметь возможность расширять x , если я включил его в пространство имен с помощью using module:x , либо быть в пространстве имен недостаточно , и тогда я также должен невозможно расширить module.x при использовании команды using .
Или третий вариант: нет import и вам просто нужно все время расширять функции их полным именем.

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

Я согласен, поэтому я выступаю за переписывание документов. Однако это не имеет отношения к текущему вопросу. Было бы лучше оставить (1) предложения по улучшению документации по текущему API и (2) предложения по его изменению отдельно, даже если они в чем-то связаны. Я планирую сделать PR для (1) в ближайшее время.

@tpapp Вы не можете документировать то, что по своей сути не имеет смысла. Ни один из этих документов не является правильным:

  1. «Вы можете расширить все, что находится в пространстве имен» (поскольку using module:x не позволяет вам расширять x )
  2. «Нахождение в пространстве имен недостаточно для расширения, поэтому существуют отдельные слова using и import » (ложь, потому что для полных имен на самом деле достаточно находиться в пространстве имен — остановив это быть достаточным было мое предложение)
  3. «Вы можете расширить что-либо в пространстве имен по его полному имени» (для чего потребуется import module:x , чтобы прекратить существование, чтобы быть полной правдой - предложение @martinholters )

Любой из них будет приемлемым. Но ничего из этого на самом деле не является реальностью. Реальность такова, что в настоящее время для «импорта материала» используются два разных слова, но на самом деле они не имеют отдельного варианта использования. Это похоже на то, что цикл for иногда ведет себя как цикл while , если вы перебираете логические значения. Никакое количество документации не сделает это не запутанным.

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

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

Ни один из этих документов не является правильным:

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

@tpapp , может быть, я должен был сказать,

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

Я имею в виду, что код — это вся документация, которая вам нужна, верно? Что в этом плохого? Время, необходимое для его переваривания. И в настоящее время я не вижу краткого способа описать, как это работает, потому что оно пронизано исключениями. Исключения, которых не должно быть в первую очередь.

Вы действительно не видите, что я пытаюсь донести?

Я думаю, что есть общее согласие в том, что текущая ситуация излишне сложна и недостаточно задокументирована. И, несомненно, упрощение механизма облегчит его документирование. Но такое упрощение будет ломающим, поэтому до версии 2.0 это сделать нельзя. Итак, вы считаете, что улучшение документации до этого не имело бы смысла? Тогда я не согласен. Я вижу две отдельные проблемы: упрощение, которое должно быть выполнено с 2.0 (о котором идет речь в этой проблеме), которое (надеюсь) будет включать необходимые обновления документации и улучшение документации текущей работы, которая, читая эту ветку, кажется крайне необходимой, но является другой проблемой. .

После выполнения #38271 я думаю, что нерешенные вопросы

Как обрабатывать добавление методов к функциям в других модулях

  1. всегда требуются полные имена ( Foo.bar() = ... ),
  2. также просто разрешить, когда символ находится в области видимости ( using Foo: bar; bar() = ... )
  3. (2), но привнести в область действия особым образом ( статус-кво , import Foo: bar; bar() = ... )

Какой синтаксис обрабатывает списки экспорта и только имя модуля

  1. using Foo переносит все экспортированные символы в Foo в область действия, import Foo только модуль ( статус-кво )
  2. using Foo: ... или другой подобный синтаксис, тогда using Foo просто модуль.

(1|2) & 2 позволит объединить в одно ключевое слово using или import за счет потери одной строки.

using LinearAlgebra, Random, StaticArrays

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

Это не одна из тех проблем, которые предлагают чистое решение, которое просто упустил первоначальный дизайн; есть компромиссы. Я бы подождал и посмотрел, может ли лучшая документация улучшить взаимодействие с пользователем при сохранении текущей настройки (1.0).

Я думаю, что для 2.0 было бы неплохо просто очистить синтаксис, чтобы он был более последовательным и описывающим то, что на самом деле происходит. Что-то типа:

| Перед | После |
|-|-|
| using Foo | useall from Foo |
| import Foo | use Foo |
| using Foo: a | use a from Foo |
| import Foo: a и import Foo.a | extend a from Foo |

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