Julia: 模块和导入别名

创建于 2012-09-05  ·  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(...)

我仍然认为将“import as”表示法作为语法糖会很好。

当我在这里思考时,如果有一种方法可以在同一导入行中导入多个特定函数,就像@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。

当我们导入一个函数时,我们要么计划使用它,要么覆盖它。 给定
以上,与

将 Base.bar 导入为 foo

我们绝对可以使用 foo 作为 Base.bar,但定义 foo 也会覆盖
Base.bar? 应该是?

这是一个老问题,但我认为是时候重新审视这个问题了,因为我们正在接近 0.2

在写代码的过程中,我发现我一直想写import NumericExtensions as ne并最终提醒自己这不被支持。

我想这实现起来并不太难,而且在我看来,这比写作要好得多

import NumericExtensions
const ne = NumericExtensions

+1

+1

这仍然相关吗? 我个人不介意只为模块别名做额外的foo = Foo ,但听起来似乎有一些共识支持别名糖。 感觉足够强烈的人可能不得不自己做公关。
但是请注意, x as y是 #1470 中提到的convert(y,x)语法的最强竞争者之一。 似乎消除两者的歧义并不难,但只是注意到。

此功能的价值在于您可以避免导入原始名称。

+1
有这个功能会很棒。

+1

与其引入新的关键字as ,不如使用=>运算符,如

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

这将正交地允许使用相同语法的模块和任何变量子集的别名。

我不知道,这对我来说真的很难看。

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

这样更好吗? 我发现它的可读性要差得多。

我个人发现as=>更不言自明,这似乎意味着任何数量的事情。

全面披露:python 偏见。

我比as更喜欢=>语法。

我喜欢卡在纳什均衡中的自行车棚。

为什么要重新绑定模块的内部符号? 它很丑,因为你真的不应该那样做。

我喜欢 Haskell 的导入语法。 我觉得 Python 的命名空间比 Julia 的模块更细化。 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 中最不喜欢的语法选择之一。 要稍微改变意思,你必须从根本上重新排列整行,改变初始关键字和所有内容的顺序。 它看起来与其他进口产品完全不同,掩盖了他们做几乎相同事情的事实。 这是一个糟糕的句法设计,imo,过于强调表面上的英语风格。

:+1: 代表as , :-1: 代表from

import a => b: c => d, e => f对我来说有点过于 perl-esque。

我从未听说过纳什均衡,在阅读了它之后,我认为这不构成一个。 但有趣的是,虽然这个自行车棚在一小时内有 10 条回复,但在 #6984 上的一个让我觉得非常重要(但更困难)的提案却在 4 小时内没有任何评论!

至于as=> :对于目前的情况,两者都将是一个很好的改进。

好点@BobPortmann在#6984 上。

我认为=>非常适合这个,FWIW。

但有趣的是,虽然这个自行车棚在一小时内有 10 条回复,但在 #6984 上的一个让我觉得非常重要(但更困难)的提案却在 4 小时内没有任何评论!

在帕金森最初的类比中,这个问题是自行车棚,#6984 是核反应堆。

:)

我讨厌搅拌这个锅,但我想我更喜欢as=>有点模糊,与字典共享语法很奇怪。 也许只是=有意义: import Foo: x = y ,类似于您现在要做的const x = Foo.y

如果不出意外,Haskell 文档中的那个表真的很有帮助。 我经常(尤其是在第一次学习 julia 时)对何时使用import / using / require或何时使用import Mod.thing感到困惑import Mod: thing 。 我实际上只是在寻找有关导入的冒号语法的解释,但找不到。

@jakebolewski似乎 Haskell 语法非常接近,Julia 的using相当于 Haskell 的(非限定) import ,Julia 的import相当于 Haskell 的import qualified 。 那正确吗?


作为一个起点,谁知道更好的确认这是对当前可用的导入/使用语法的一个很好的总结? 假设我们有一个模块Mod导出函数xy ,并且还有非导出函数pq

import Mod引入了Mod.xMod.yMod.pMod.q 。 导入的函数都可用于方法扩展

using Mod引入xyMod.xMod.yMod.pMod.q . xy不可用于方法扩展,但Mod.*可。

import Mod.x, Mod.p引入xpMod.xMod.yMod.pMod.q . 它们都可用于方法扩展。

import Mod: x, pimport Mod.x, Mod.p相同

using Mod.x, Mod.p引入xpMod.xMod.yMod.pMod.q . xp不可用于方法扩展,但Mod.*可。

using Mod: x, pusing Mod.x, Mod.p相同

据我所知, using Mod是唯一关心Mod导出的内容的用法。

在 Module 的文档中提供此摘要将非常有用! 有人可以证实这一点,所以我可以准备 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

我发现我非常喜欢作业版本。 毕竟,导入是一种分配形式。

在 Module 的文档中提供此摘要将非常有用! 有人可以证实这一点,所以我可以准备 PR 吗?

@brk00 ,加油!

@kmsquire ,我今天会努力的!

但在此之前,我想确保我能很好地理解它。 在@ssfrr示例中,假设我在 REPL 上并输入using Mod 。 导入的函数xy不可用于方法扩展。 这意味着,如果我声明另一个 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)

如果我在 _using_ 我刚刚使用using Mod导入的函数之前为x分配了一个新函数,则不会引发错误。 但是,例如,如果我在新分配之前调用x() (就像您在示例中所做的那样),则确实会引发错误。

啊,有趣! 我不知道那件事。 我只是碰巧打电话给x来演示using之后的命名空间。

也许其中一位核心开发人员可以提供一些启示。 当x被调用时会发生什么触发尝试重载的错误?

我发现上面@carlobaldassi建议的别名在实践中非常有用。

我想知道import是否可以只返回模块对象,以便它可以直接使用赋值来别名,有或没有const ? 例如

const Shortname = import ModuleWithLongName

代替

import ModuleWithLongName
const Shortname = ModuleWithLongName

这不需要新的语法,只需要现有语法元素的组合。 我意识到目前import是一个“声明”,纯粹是为了副作用而调用的,没有任何价值。 如果解析器不允许这样做,我更喜欢import("Module")或类似的东西。

我认为别名模块的选项可能很好,但我也认为它有一个没有人提到的缺点:它可能会显着模糊代码。 从代码阅读器的角度来看,一个模块只有一个描述性名称是很好的。

看起来很微不足道吗? 缩短的表格也可能会标准化为np for numpy等流行包。

我仍然更喜欢numpy到标准化np甚至Arraysnumpynumpy听起来像是 1950 年代的舞蹈动作和致命瘟疫的混合体。

可以很容易地写:

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

这不需要 1.0 的语法

我觉得我们应该为此制定一个语法。 是的,我们可以不用它,因为你可以做requireconst赋值,但是使用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

@Ismael-VC

尝试

<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 决定,因为所有建议的语法要么是错误,要么完全是胡说八道。

当原始模块被重新定义时,模块别名会发生什么,例如,通过 REPL 上的reload("...") ? 使用const M = MyModule方法, M在重新加载后仍然引用旧模块,因此MyModule中任何新定义的名称只能由MyModule.name访问,但不能M.name

在这种情况下,别名语法应该不仅仅是const M = Module的语法糖,而是要确保M始终引用与MyModule相同的东西。

这也将简化 REPL 工作流程,因为我可以在开发过程中继续import一个模块,但可以通过更短的别名引用它,而无需每次重新建立(非const )别名reload

截至目前, reload已被删除,因此此问题不再适用。

这也适用于include ,还是我错过了什么? 通常,当重新定义一个模块时, import ed 的模块名称指的是新定义,而由赋值定义的别名指的是旧定义,对吧?

不,这不应该是一个问题。 重新定义模块时,导入的名称将永远不会更改,包括正常的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

并在 REPL 中重新include它,模块及其别名的行为不同:

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是无操作的,删除它后,您的代码不再有任何importusing ,因此与此问题无关。 请更改它,以使模块不在与import相同的范围内定义。

import Tst as Tst2import Tst在绑定到该名称方面的工作方式相同,这始终是一种绑定,而不是您想要的任何魔法。 这不是关于在定义模块时添加别名。

换句话说,你的代码基本上是,

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

抱怨b = a的行为没有任何意义,因为a根本没有被a = a绑定。 在一个不好的例子中,您所看到的只是在与导入相同的范围内定义模块(即在同一范围内的a = aa = Ref(...) )的副作用。

好的,我只是想指出,在某些情况下,由分配定义的别名不会按预期工作。 也许我选择了一个不好的例子。 我认为这对于创建模块别名的语法可能很重要(无论是否与import相关),但现在无论如何这都是假设的。 对不起,噪音。

这个问题已经存在很长时间了,即使一些扩展仍然应该讨论,

import LongPackage as longpkg

本身似乎很合理。

撞。 我想知道现在最好的解决方案是什么样的? 这就是我正在做的事情:

import LinearAlgebra
const linalg = LinearAlgebra

如果有更好的解决方案,请纠正我!

这就是现在的标准。

~ 语法呢? 我不知道后果,但它有一个很好的审美。 此外,~ 用作近似的符号。

import LongPackage ~ lp

@JeffreySarnoff引用 Stefan

@StefanKarpinski

这是我在 Python 中最不喜欢的语法选择之一。 要稍微改变意思,你必须从根本上重新排列整行,改变初始关键字和所有内容的顺序。 它看起来与其他进口产品完全不同,掩盖了他们做几乎相同事情的事实。 这是一个糟糕的句法设计,imo,过于强调表面上的英语风格。

在这种情况下,~ 与 Python 语法的类英语方面无关。 我想当我看到~时,我只是认出了近似的数学概念。 10.001 ~ 10。

所以,从这个意义上说,我觉得~其实很不错。

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

:-)

好的——忘记python——我正在删除该评论以支持

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

我最初并不喜欢它,但很多人似乎很自然地想用as来写这个我不得不说做其他事情似乎有悖常理。 它不必保留为关键字或任何东西,因为这是一个明确的句法上下文。

一种_trys_让人们知道它意味着什么的编程语言!?!

@杰弗里萨诺夫

一种试图比 Python 更好的编程语言!!!

@neilpanchal Julia 绝对是一种在很多方面都试图比 Python 更好的语言。 另一方面,与 Python 做对了的 Python 相比,它很明显并没有走出它的方式_不同_。 Julia 从 Python left 和 right 借用了语法约定。

请采用 python as 。 他们做对了。

julia> 将 LightGraphs 导入为 lg
错误:语法:表达式结束后的额外标记“as”

只是另一个支持as的 0.02 美元评论远离 Python。 fwiw,现在我再次使用有规律的 Julia,我想念这个。 不像在 Python 中那样,而是在 Clojure 中(参见示例),其中包作用域的命名空间(有利于消除歧义)可以被简化,以使库的显式使用更加实用。 由于我倾向于避免using (即通配符导入)对import Gadlfy as gf有一些语法糖,而无需添加const gf =行(我目前所做的)会得心应手。

另一个支持as语法的评论:heart:我希望未来的 Julia 版本真的很难。

我也希望这发生。 特别是因为它不是一个重大变化(对吧?)。

我认为这将在某个时候实现(如果我能抽出时间,我会尝试自己),但与此同时, 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的解决方案,因为它符合 Julia 的整体“一切都是一种表达”的方法。

唯一的问题是,为了保持一致,导入表达式仍然必须执行将全名纳入范围的副作用(也许不是灾难)。 也可以特例不导入全名,但特例是毛病。

在 OCaml(具有最佳模块的语言!)中,您可以执行以下操作:

module LG = LightGraphs

这里的重要区别是,使用 OCaml 程序编译的每个模块的限定名称都自动在范围内。

当然,使用import LightGraphs as LG的好处是“每个人”都已经从 Python 中知道了它,但我个人的喜好是,最好对字面上是赋值的东西重用赋值语法。

或者更广泛的变化是将“命名空间”作为类型引入(如果已经完成,则为 idk)。 然后import可以将模块/任何内容扩展为命名空间并返回命名空间。 但这会破坏所有 Julia 代码,因为简单地执行import mod_name不会使mod_name命名空间在全局范围内可用。 如果在使用 import 而没有前面的赋值时,可以以某种方式使mod_name命名空间对全局可用,那么这不是问题。

或者只是添加一个新函数import_as_ns(mod_name) :: Namespace这将是一个纯函数,它返回单个命名空间中的所有函数/模块等。

它符合 Julia 的整体“一切都是一种表达”的方法

但是, importusing是特殊的,因为它们仅用于其副作用,并且仅用于顶层。

顺便说一句,鉴于这个问题已经公开了很长时间,我想知道 triage 是否愿意重新审视它并做出决定。 我会提倡关闭,因为

  1. 当用户不介意命名空间中的Foo时, import Foo; const Bar = Foo提供了一个完美的解决方案,
  2. ImportMacros.jl应该处理其余的,
  3. 两者似乎都没有在实际代码中经常使用以保证特殊语法。

我会说,经过这么长时间,我仍然怀念这个(我也在自己的代码中使用 ImportMacros)。

我确实认为您所写的咖啡风格会影响对此的需求(或愿望)。 当我在编写 ML 或科学代码时,我发现我并不太怀念它。 当我编写了其他使用许多长名称包的代码时,我发现我最常使用 ImportMacros。

ImportMacros 就足够了。 我只是同意它看起来不那么干净的评论。

我也期待有一天我可以在我的编辑器中使用自动格式化程序对我的导入进行排序......但不太可能知道如何处理@using ...

我也同意@kmsquire的观点,认为这不是什么大问题,ImportMacros 应该就足够了。 但是任何方法都应该被记录下来,这样它就不会再出现了。

虽然我还是喜欢m=import Module糖😅

我并不打算让我的信息暗示这没什么大不了的。

我仍然更喜欢这种语法。

ImportMacros 就足够了。 我只是同意它看起来不那么干净的评论。

我同意。 此外,对于:

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

在向新手或编程初学者介绍语言时解释为什么给定脚本以这种不一致开始时会有点烦人,因为它会使流程复杂化并转移教学重点。

两者似乎都没有在实际代码中经常使用以保证特殊语法

人们在实际代码中没有大量使用 ImportMacros 的原因可能是由于权衡。 例如,我几乎每次都会使用import ... as ... ,但可能不会以必须先导入另一个包来执行此操作为代价。 不仅仅是一个额外的依赖,它还对“感觉”有影响(有些人喜欢他们的代码看起来很圆滑(尤其是在 python 或 Julia 中)🤷‍♂️,而这个using/@using给出了一个相当“ hacky”的氛围来引入脚本)。

所以是的,“ImportMacros 就足够了”。 对于真正想使用它的人。 但这就是问题所在,这不是一个至关重要的问题,人们可以在没有它的情况下工作。 尽管如此,我坚信通常是这些小东西使语言的语法保持不变,因此这个怪物线程将其支持添加到 base.

我认为import ... as ...是绝对值得的语法糖,它非常容易理解和解释,并且在 Julia 等语言中特别有用,其中包的名称往往很长(显然,这很重要仅在人们不想做using ...的假设下)。

Python首先实现它的事实不应该真正相关。 Julia 在尝试与其他语言保持距离和/或强调自己的身份之前,应该努力拥有最好的语法。

有这个语法和流行模块的约定(我在想它是如何总是import numpy as npimport matplotlib.pyplot as plt )也可以提供一个简洁的替代方法来做using SuchAndSuch和依赖export ed 名称(这是我在“库”代码中通常避免使用的)。

@DominiqueMakowski

向新手或编程初学者介绍该语言,以解释为什么给定脚本以这种不一致开头

我不明白你为什么认为这是不一致,它只是命名空间管理,这是大型编程的正常部分。

此外,重命名模块可能不是 Julia 新手首先会遇到的事情。

我并不是说没有用例,但证明自己的关键字的合理性可能很少见。 目前,在 Github 上搜索using ImportMacros会得到 <10 个独特的结果。

我几乎每次都会使用 import ... as ... ,但可能不会以必须先导入另一个包为代价来做到这一点

这从两个方面都削减了:如果使用像ImportMacros或额外符号( import LongFoo; const Foo = LongFoo )这样的轻量级包的(非常小的)成本不值得收益,那么收益可能不会那么大.

Julia 在尝试与其他语言保持距离和/或强调自己的身份之前,应该努力拥有最好的语法。

我不确定您为什么认为这是本次讨论的动机。

在某些情况下,您可能希望在const分配不起作用的情况下使用as

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是普遍同意的“最不令人惊讶”的语法,这似乎是我们应该采用的。 所有这些都需要有人做一个 PR 来实现它。

新的 Julia 用户在这里。
我主要来自 R 背景,也使用 Python。 我对using之类的加载机制导致的包公用名称空间有不好的体验。 冲突的风险是一个,但仅从可读性的角度来看,它们是不断思考每个方法的模块的精神负担。

我不知道添加这种语法有多复杂? 新手可以用吗?

可能不是很容易。 它将涉及破解用 Scheme 编写的解析器代码并通过 AST 连接更改,还可能涉及一些 C 代码。

这将是一个很好的功能,可以避免多个模块中的函数名冲突。

从关于松弛的讨论中,我添加了一些想法

我想知道这是否会让人们在选择函数名和正确地向正确的函数中添加方法时不那么小心。 事实上,例如,

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

相关问题

i-apellaniz picture i-apellaniz  ·  3评论

Keno picture Keno  ·  3评论

musm picture musm  ·  3评论

helgee picture helgee  ·  3评论

TotalVerb picture TotalVerb  ·  3评论