Julia: 允许ab字段访问语法的重载

创建于 2013-01-10  ·  249评论  ·  资料来源: JuliaLang/julia

在这里长大: https

decision parser

最有用的评论

这是一个有趣的三行实现:

diff --git a/base/boot.jl b/base/boot.jl
index cd3ae8b..a58bb7e 100644
--- a/base/boot.jl
+++ b/base/boot.jl
@@ -266,6 +266,9 @@ Void() = nothing

 (::Type{Tuple{}})() = ()

+struct Field{name} end
+(::Field{f})(x) where {f} = getfield(x, f)
+
 struct VecElement{T}
     value::T
     VecElement{T}(value::T) where {T} = new(value) # disable converting constructor in Core
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index b4cb4b5..59c9762 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -1685,7 +1685,7 @@
     (if (and (pair? e) (eq? (car e) '|.|))
         (let ((f (cadr e)) (x (caddr e)))
           (if (or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$))
-              `(call (core getfield) ,f ,x)
+              `(call (new (call (core apply_type) (core Field) ,x)) ,f)
               (make-fuse f (cdr x))))
         (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e)))
             (make-fuse (undotop (cadr e)) (cddr e))

我的想法是a.b实际上应该调用投影函数Field{:b}()而不是getfield ,这样您就可以免费获得像x->x.a这样的函数。 这也允许getfield始终表示低级字段访问。

上面的实现完全可以使用,但是在编译器上很难(sysimg + 5%,这确实是一个令人惊喜的惊喜)。 因此,这将需要一些专门的启发式方法,并且需要更新一些早期的优化方法,但是希望这将是可行的。

所有249条评论

将点用作变异器/访问器方法的语法糖的功能对于很多事情来说都是不错的。 我一直以提供它的语言来欣赏它,因此您可以在不破坏API的情况下将结构字段转换为更复杂的抽象。

+1

我有一个很棒的方法来实现这一点。

有兴趣谈论吗? 我知道汤姆·肖特(Tom Short)对在DataFrames中使用它确实很感兴趣,尽管我开始越来越怀疑使用此功能的智慧。

这将使调用Python代码(通过PyCall)明显更好,因为当前我被迫执行a[:b]而不是a.b

@JeffBezanson ,是否有机会获得0.3? 对于PyCall和JavaCall(cc @aviks)而言,对于跨语言互操作都非常有用。

@JeffBezanson如果为I have an absolutely awesome way to implement this.

以我的经验,没有比让Jeff实现他不喜欢的版本更快速,更可靠的方法了;-)

基本思想是您实施

getfield(x::MyType, ::Field{:name}) = ...

这样您就可以按字段重载它。 这样可以访问“实际”字段,以保持透明状态。 使用适当的后备getfield(::MyType, ::Symbol)也可以。

最大的问题是模块相对于.具有特殊的行为。 从理论上讲,这只是getfield另一种方法,但是问题在于我们需要解析模块引用_earlier_,因为它们的行为基本上像全局变量。 我认为我们必须在.的行为中保持特殊情况。 由于分析了(#类型)*(#个字段)额外的函数定义,因此还有一点编译器效率问题。 但是为此,我们将只看到发生了什么。

@JeffBezanson您是否还在模块中引用const行为? 用户类型模拟模块并在动态字段查找的结果实际上是常量时告诉编译器将非常有用。 (另一种方法是从一个实际的模块开始,并能够“捕获”失败的jl_get_global并根据需要注入新的绑定)

我发现与#5395结合使用非常有用。 这样一来,我们便可以拦截对未定义函数或方法MyMod.newfunction(new signature)的调用,并根据需要生成与(可能很大的)API的绑定。 然后,我将其作为常规的const绑定进行缓存。

让我,一个简单的Julia新手,有点担心:我认为重载点运算符的可能性可能意味着以某种方式丢失了字段访问“纯度”。

如果执行ab仅仅是对参考值/值的访问,或者如果后面可能有巨大的功能机制被调用,则用户通常会失去知识。 我不确定那有多糟糕,这只是一种感觉...

另一方面,我看到对于许多情况(PyCall,Dataframes ...),这确实是一个语法糖的大愿望,这是完全可以理解的。
也许是时候该..#2614?

我支持这样做。

但是纯度确实有话要说,即使人们可以使用names(Foo)来弄清楚Foo的真正成分是什么。

pure的论点与我所关心的主要实际问题密切相关,当类型的字段与您可能希望使用的名称发生冲突时,它就是如何处理名称冲突。 在DataFrames中,我认为我们可以通过禁止使用columnscolindex作为列名来解决此问题,但想知道人们对此的计划是什么。

我猜getfield(x::MyType, ::Field{:foo}) = ...必须被禁止时, MyType有一个字段foo ,否则进入到真正的现场将会丢失(或一种强制访问现场将必须可用)。
但是getfield只能为具体类型定义,因为抽象字段对字段一无所知。

(与此同时,我偶然发现了有关C ++的信息。)

这不是主要问题。 我们可以提供类似Core.getfield(x, :f)来强制访问真实字段。

好吧,也许我被卖了。 但是,然后定义一个指向Core.getfield(x, :f)的快捷方式(例如x..f )将是不错的选择,否则对于所有符号(数据框,可能是字典)来说,内部类型的代码过载.挤满了Core.getfield s。

我不担心纯度方面的问题-直到我们有了这之后,唯一的代码
完全应该使用字段访问的是属于
给定类型的实现。 如果字段访问是api的一部分,则您
必须将其记录下来,就像使用任何api一样。 我同意这可能很方便
虽然在编写core.getfield的一些快捷方式语法时
实现。

在#4935中已经指出了这一点,但让我们将其介绍到这里:如果使用不当,点重载可能会与传统的Julian多重分配有些重叠,因为我们可以开始

getfield(x :: MyType,:: Field {:size})= .........
对于i = 1:y.size .....

代替

size(x :: MyType)= ..........
对于i = 1:size(y)....

尽管该点非常适合访问集合中的项目(数据框,Dicts,PyObjects),但它可以以某种方式更改对象属性(而非字段)的访问方式。

我认为要考虑的一件事是,如果您可以重载访问字段,则还应该能够重载一个字段。 否则,这将是前后矛盾且令人沮丧的。 你可以走那么远吗?

@nalimilan ,除了getfield之外,绝对还需要setfield! getfield 。 (类似于setindex!getindex[] )。 我不认为这是有争议的。

同意@stevengj :DataFrames肯定会为列实现setfield!

我支持这个。

使用其他语言(例如C#和Python)的经验确实表明,点语法确实具有很多实用价值。 通过专用方法实现的方法很大程度上解决了性能下降的问题。

但是,重要的是要确保此方法不会严重影响方法的_inlineability_。 例如,类似f(x) = g(x.a) + h(x.b)的东西在落地后不会突然变得不可内联。

如果我们决定实现这一点,那么还可以提供一些宏来简化属性的定义,这可能很有用:

# let A be a type, and foo a property name
<strong i="10">@property</strong> (a::A).foo = begin
    # compute the return the property value
end

# for simpler cases, this can be simplified to
<strong i="11">@property</strong> (a::A).foo2 = (2 * a.foo)

# set property 
<strong i="12">@setproperty</strong> (a::A).foo v::V begin
    # codes for setting value v to a property a.foo
end

在幕后,所有这些都可以转换为方法定义。

我不相信<strong i="5">@property</strong> (a::A).foo =getproperty(a::A, ::Field{foo}) =容易得多...

无论如何,可以在基本功能登陆后添加更好的语法糖。

关于内联,只要在决定是否内联周围功能之前先对内联访问进行内联,那么我不明白为什么会影响它。 但这可能不是当前完成内联的顺序吗?

getproperty(a::A, ::Field{:foo}) =冒犯了我,因为冒号太多了:-)我同意这是一件小事,也许我们现在不必担心这一点。

我担心的是这是否会导致性能下降。 我对内部代码生成机制不太清楚。 @JeffBezanson可能会对此说些什么?

字段访问是非常低级的,因此在确保性能不变的情况下我不会这样做。

毕竟,我不认为重载字段是个好主意。 有了这个提议,总会有两种设置属性的方法: x.property = valueproperty!(x, value) 。 如果实现了字段重载,我们将需要一个非常强大的样式指南,以免陷入混乱,在这种情况下您永远不会事先知道作者为给定类型选择了哪种解决方案。

然后是字段是公共字段还是私有字段的问题。 不允许字段重载会使类型系统更清晰:字段将始终是私有的。 方法将是公共的,类型将能够声明它们实现接口/协议/特征,即它们提供了给定的方法集。 这与@stevengjhttps://github.com/JuliaLang/julia/issues/1974#issuecomment -12083268有关在字段中使用方法重载以避免破坏API的做法背道而驰:仅提供方法作为API的一部分,而从不提供方法。

我唯一会后悔字段重载的地方是DataFrames ,因为df[:a]不如df.a 。 但这听起来并不像应该单独进行这样的重大更改。 另一个用例似乎是PyCall,它可能表明应该允许字段重载,但仅适用于高度特定的非朱利安用例。 但是,如何防止人们滥用某个功能呢? 将其隐藏在特殊模块中?

@nalimilan ,我想说的是应该尽可能多地使用x.property语法。 问题是人们确实喜欢这种语法–这非常令人愉快。 采取这样一种不错的语法,特别是说只能将它用于内部访问对象似乎是完全错误的:“哈哈,这种不错的语法存在;不要使用它!” 使访问私有事物的语法变得不那么方便,美观,而不是强制API使用更丑陋的语法,这似乎更为合理。 对于..运算符来说,这也许是一个很好的用例:私有_real_字段访问运算符。

我实际上认为,这种改变可以使事情变得更清晰,更一致,而不是那么不那么。 考虑范围–当前, step(r)r.step样式之间存在某种可怕的组合。 特别是自从我引入FloatRange以来,这很危险,因为只有使用step(r)才是正确的。 混合的原因是,存储了范围的某些属性,并计算了一些属性,但是这些属性随时间变化,实际上对于不同类型的范围而言是不同的。 如果除step(r)本身的定义之外的所有访问都是step(r)样式,则样式会更好。 但是,对此存在一些严重的心理障碍。 如果我们使r.step方法调用默认为r..step ,那么人们可以做他们自然倾向于做的事情。

要扮演恶魔的拥护者(和我自己),我们应该写r.length还是length(r)吗? 泛型函数和方法之间的不一致是困扰Python的问题,而Ruby完全致力于r.length样式。

.. +1为Core.getfield

@StefanKarpinski很有意义,但是您需要为私有字段添加语法,并且接口必须同时指定方法和公共字段。 实际上,您需要样式指南来确保一定的一致性。 在length的情况下是一个困难的例子,但是还有例如size ,这非常相似,但是需要一个维数索引。 这个决定打开了一罐蠕虫。

在这种情况下,我还支持..来访问实际字段,并支持.来访问字段,无论是方法还是实数值。

要(和我自己)扮演恶魔的拥护者,我们应该写r.length还是length(r)吗? 泛型函数和方法之间的不一致是困扰Python的问题,而Ruby完全致力于r.length样式。

对于此问题可能会造成歧义的关键因素是您是否希望能够将某些东西用作高阶函数。 即ff(x)是你可以map一个集合,而fx.f不是(短写作x -> x.f )–对于单调度语言中的所有方法,情况相同。

为什么要停止现场访问? 拥有相当于getfield(x::MyType, ::Field{:foo}, args...) = ... x.foo(args...) getfield(x::MyType, ::Field{:foo}, args...) = ...怎么办? 然后我们可以沿第一个维度获得x.size(1)的大小。 (不知道我是否喜欢我的建议,但也许要考虑一下。或者可能不会,因为人们只会编写面向对象的相似代码?)

使用此功能将是可能的。 这是让我停下来的一件事。 我对这样的oo风格代码没有问题-正如我说的那样,它相当令人愉快,人们非常喜欢它-但它确实在编写方式方面引入了足够的选择,我们确实需要关于您应该做什么的强有力的政策因为您将无所不能,无所不能。

当我开始学习Julia时,无点语法帮助我在精神上放开了OO编程风格。 因此,仅出于这个原因,我认为我的建议是错误的。

此外,对于简单的重载(即a.b sans (args...) ),我同意@nalimilan的上述评论。 在问题#4935中,共识似乎是字段不应该是API的一部分,而应该只是方法; 因此,似乎该问题已经解决。 具有. -overloading语法将使人们更加清楚正常字段不应该是API的一部分,并且可能会鼓励使字段成为API的一部分。

但是,是的, .语法很方便...

怎么样:单个.应该是getfield(x::MyType, ::Field{:name}) = ...语法糖,并且字段访问是_only_到.. (即现在的. )。

这将使您有明显的区别:

  • .用于公共API来访问类型实例的类似值的事物
  • ..用于字段访问,通常不应在公共API中使用

当然,这将是一个巨大的变化。

这基本上就是我的建议,只是.默认为..所以它没有中断。

抱歉,我应该重新阅读!

但是我认为未默认设置为...实际上可能很好(除此之外,它正在中断),因为这将迫使开发人员决定什么是公共API,哪些不是。 另外,如果用户使用.. ,则他可以预期自己的代码可能会损坏,而.则不会。

那是个很好的观点。 我们可以通过将a.b默认设置为a..b并带有弃用警告来走这条路线。

从样式的角度来看,我认为我更愿意看到

a = [1:10]
a.length()
a.size()

a.length
a.size

我认为这有助于保留调用函数的想法,而不仅仅是检索以某种方式存储在类型中的属性(回到上面的“纯度”问题)。 我想知道是否有一种方法可以帮助确保这种风格,以使事情不会像其他某些语言那样混乱。

我不太喜欢

a.length()

从那时起,我无法确定原始类型中是否存在功能字段。 如果.从不访问字段,那么显然不是问题。 否则,这似乎使我感到困惑。

先验地,我觉得我们不应该做a.length()a.length 。 但是问题是为什么呢? 是什么使r.stepr.length ? 不一样吗如果它们没有什么不同,我们应该使用step(r)length(r)还是r.stepr.length吗?

使用Stefan建议的语义以及我的加法,很显然.始终是一个函数调用(就像+ ),而..始终是一个字段访问。

关于a.length等是否是一个好主意: .访问应该只用于访问类型中的实际数据,或多或少会像使用dict的条目那样。 而我们坚持使用无数据属性的函数,例如sizelengthstep等。因为其中一些需要额外的参数,我认为a.size(1)的语法类型错误。

这是我对这个主题的看法:

  • 点语法仅应用于类型/类的属性。 请记住,这不仅与获取程序有关,而且与设置程序有关,诸如a.property() = ...感觉完全错误。
  • 尽管我有点像函数定义公共API且字段是私有的当前情况,但我也与Stefans共享观点,即点语法太好了,不能被公共API禁止使用。 但是请让我们将此限制为简单属性。 a.length是一个很好的例子, a.size(1)不是因为它需要一个附加参数。
  • 请让.默认为.. 。 茱莉亚(Julia)并非典型的语言。 让我们保持这种方式

请让.默认为.. 。 茱莉亚(Julia)并非典型的语言。 让我们保持这种方式

我确实同意这一点。 甚至设置合成属性的语法也只是a.property = b ,而不是a.property() = b

当然,我只是想弄清楚为什么a.property()作为语法不好看

或更明确地说:关于点语法的重要一点不是让函数与类型/类关联,而是以一种不错的方式编写getter / setter的能力。 而且,getter / setter对于数据封装很重要(保持接口稳定,但更改实现)

从API设计人员的角度来看,此更改将是巨大的,但我同意应随附某种样式指南以限制将来的任何不一致之处。

这将使Ruby像DSL一样...

amt = 1.dollar + 2.dollars + 3.dollars.20.cents 

但要为Java做疯狂准备:

object.propert1.property2.property3 ....

只是一些想法:

  • 我最想以符号作为键的Dicts的.语法。 使用d.key然后使用d[:key]更好。 但是最后并不重要。
  • 我认为a->property读取效果优于a..property 。 但是同样,这没什么大不了的,我不知道它是否适用于julia语法。

@BobPortmann我不同意。 字典是一个容器对象,容器对象的API是obj [index]或obj [key]。 现在,由于我们在Julia中没有属性,因此容器API重载以在PyCall和OpenCL之类的库中提供此功能。 此更改有助于加强容器API的区别,因为它不会过载以提供其他功能。

a->property用于私有字段将是使C黑客远离Julia的好方法;-)

我有点像..语法。

a->property语法已经讲过了-这是一个匿名函数。 但是, a..b运算符已经争夺了一段时间。 在某些情况下,您需要类似于dict的内容,但具有许多可选字段。 为此,使用getter / setter语法比dict索引语法更好。

“已经讲了a-> property语法–这是一个匿名函数。”

当然是。 ->周围没有空格,看起来不像。

作为样式指南,如何建议将property(x)用作只读属性,并建议x.property用于读/写属性?

对于可写属性,x.foo = bar实际上比set_foo!(x,bar)好得多。

foo(x)来阅读,用x.foo来写是非常令人困惑的。 实际上,这就是属性如此吸引人的原因。 具有相同的读写访问语法,即一种可以获取的最简单语法(用于getter和setter)

关于样式,存在一个很大的问题,我们是否要同时实现x.lengthlength(x)如果实现了此功能,或者是否应该弃用和删除后面的形式。

我的意见是,我们应该只有一种方式可以做,将来只使用x.length 。 关于样式,我认为它非常简单。 属于类型简单属性的所有内容都应使用字段语法来实现。 其他所有功能。 我在C#中经常使用属性,而很少发现不确定某个东西是否应该为属性的情况。

我反对将随机选择的1参数函数集更改为x.f语法。 我认为@ mauro3提出了一个很好的观点,即这样做掩盖了语言的本质。

a.b至少在视觉上是一种作用域构造。 b不必是全局可见的标识符。 这是至关重要的区别。 例如,具有较高部分的矩阵分解具有.U属性,但这并不是通用的东西---我们不希望使用全局函数U 。 当然,这有点主观,尤其是因为您可以轻松定义U(x) = x.U 。 但是length是另一回事。 成为一流的(例如map(length, lst) )会更有用。

这是我建议的准则。 foo.bar表示法在以下情况下适用:

  1. foo实际上有一个名为bar的字段。 示例: (1:10).start
  2. foo是一组相关类型的实例,其中一些实际上具有一个名为.bar的字段; 即使foo实际上没有bar字段,该字段的值也由其类型隐含。 示例: (1:10).step(0.1:0.1:0.3).step
  3. 尽管foo没有显式存储bar ,但它以更紧凑或更有效的形式存储了等效信息,使用起来不太方便。 示例: lufact(rand(5,5)).U
  4. 您正在模仿其他类似Python或Java的API。

在情况1和情况3中可以分配bar属性,但在情况2中则可以分配。在情况2中,由于您不能更改值的类型,因此不能对bar属性进行突变。这种类型暗示的。 在这种情况下,您可能希望禁止使其他相关类型的bar属性发生突变,方法是使它们不变或显式使foo.bar = baz错误。

@tknopp ,我不建议使用x.foo进行写作,使用foo(x)进行阅读。 我的建议是_if_一个属性既可读又可写,那么您可能想_both_用x.foo读写它。

@StefanKarpinski :但是,不是length是3的情况。这里的大小通常是什么,而length是大小的乘积?

我看到Jeffs指出,尽管这一更改将使这些功能不再是一流的。

@stevengj :我明白了。 很抱歉使您感到困惑。

@tknopp –长度是从尺寸中得出的,但并不等同于尺寸。 如果您知道尺寸,则可以计算长度,反之亦然。 当然,这有点模糊。 lufact可以接受的主要原因是,我们没有找到比这更好的API。 另一种方法是定义upperlower泛型函数,它们给出普通矩阵的上三角和下三角部分。 但是,例如,这种方法不能推广到QR因式分解。

它告诉您,只有极少数情况下_really_似乎要求使用这种语法:pycall,分解和数据帧。
我很担心最后会随机产生f(x) vs. x.f ; 这会使系统变得更难学习。

@StefanKarpinski列表的第1点是否表示类型的任何字段自动属于公共API?

目前,我可以知道什么是模块的公共API:所有导出的函数和类型(但不包括其字段)。 进行此更改后,将无法确定哪些字段应该属于公共API,哪些不属于公共API。 我们可以像在python中那样开始命名私有字段a._foo左右,但这似乎不太好。

我个人认为DataFrames案例有点多余。 如果这样做,我将功能添加到DataFrames中,但是发现一致性的损失比保存几个字符要麻烦得多。

我也不会根据DataFrames,PyCall做出决定(Gtk也希望如此)。 我们之所以想要它,是因为我们认为字段应该是公共接口的一部分(因为它看起来“不错”),或者我们不想要它。

... pycall ...

和JavaCall

由于此操作的主要用例似乎是与非Julia系统的交互,因此如何使用建议的..运算符而不是使.重载呢?

我不知道这里是否有一个更简单的解决方案是面向OO的更一般的技巧:

#we already do
A[b] => getindex(A,b)
#we could have
A.b(args...) => b(A, args...)
# while
A..b => getfield(A,::Field{:b})
# with default
getfield(A, ::Field{:b}) = getfield(A, :b)

看起来这将允许JavaCall / PyCall在“类”中进行方法定义,同时如果人们希望拥有一些OO类型代码,则还允许使用通用样式,尽管它非常透明A.b()只是重写。 我认为这对于来自OO的人们来说是很自然的。
也有新的getfieldA..b允许在那里重载,尽管强烈建议不要在这里重载,并且仅用于类似字段/属性(我怀疑它不会被广泛使用由于稍微有点过载getfield(A, ::Field{:field})

@ mauro3

@StefanKarpinski列表的第1点是否表示类型的任何字段自动属于公共API?

那是何时可以使用foo.bar表示法的列表,而不是在必要时使用的列表。 您可以为“私有”字段禁用foo.bar表示法,然后只能通过foo..bar进行访问。

@karbarcca :我对您在这里提出的建议不太清楚。

首先,我喜欢采用成人同意的方式,并使.完全可重载。 我认为双点建议会导致更多的混乱,而不是更少。

@ihnorton –您反对使用a..b作为字段访问的(unoverloadble)核心语法,还是反对使用a..b作为可重载语法?

朱莉娅最大的特色之一就是它的简单。 重载x.y感觉就像是迈向C ++的第一步。

@StefanKarpinski,但是这意味着范式从默认的私有字段到默认的公共字段已经发生了很大的转变。

我刚刚意识到,可能一直以来其他人都清楚。 完全的OO风格的编程可以通过基本的. -overloading(尽管很丑)完成。 定义

getfield(x::MyType, ::Field{:foo}) = args -> foofun(x, args...) # a method, i.e. returns a function
getfield(x::MyType, ::Field{:bar}) = x..bar+2                  # field access, i.e. returns a value

然后x.foo(a,b)x.bar工作。 因此,关于应该执行x.size(1)还是仅执行x.size的讨论是没有意义的。

@StefanKarpinski反对通常超载的a..b略微冷淡a..b -> Core.getfield(a,b)

我确实开始看到这里需要另一个运算符,但是a..b不太令人信服。 需要两个角色感觉很……第二等。 也许a@ba$ba|b (按位运算符不经常使用)。 外部可能性也是a b`,解析器可能会将其与命令区分开。

我可以使用“ ugly”运算符进行原始字段访问。 我认为经验表明,由于这是一种具体的操作,因此很少使用,使用起来确实很危险。

我建议允许通过约定/重写模拟OO单个调度:

type Type end
# I can define methods with my Type as 1st argument
method(T, args...) = # method body
t = Type()
# then I can call that method, exactly like Java/Python methods, via:
t.method(args...)
# so
t.method(args...) 
# is just a rewrite to
method(t, args...)

这里的理由是我们已经对getindex / setindex!进行了类似的语法重写,因此让我们允许使用完整的OO语法。 这样,PyCall和JavaCall不必做

my_dna[:find]("ACT")
# they can do
my_dna.find("ACT")
# by defining the appropriate find( ::PyObject, args...) method when importing modules from Python/Java

我喜欢它,因为它是一个非常清晰的转换,就像getindex / setindex一样,但是如果需要,可以模拟单个调度OO系统,特别是对于OO语言包。

然后,我建议使用..运算符进行字段访问,并带有重载选项。 这里的用法是允许PyCall / JavaCall通过重载对..调用来模拟字段访问,允许DataFrames重载..以进行列访问,等等。这也将是PyCall / JavaCall中新的默认字段访问。一般适用于任何类型。

我确实对纯语法重写情有独钟。 可以说现在可以编写a.f(x)并使其正常工作是一件坏事,但这意味着它与大多数OO语言有所不同。

当然,硬币的另一面是可怕的样式碎片,而且a.fa.f()没有什么共同之处,导致幻觉迅速消失。

朱莉娅最大的特色之一就是它的简单。 重载x.y感觉就像是迈向C ++的第一步。

同样的感觉。 我正在考虑,如果实际的实际需求确实是有限数量的互操作类型,那么仅在类型声明中明确要求时才使其有效呢? 例如,除了typeimmutable之外的其他关键字可以是ootype或其他。

而af与af()没有任何共同点这一事实,导致幻觉迅速消失。

您能否阐明@JeffBezanson的含义?

如果a.f()有效,我希望a.f是某种方法对象。

知道了是的,您肯定无法执行map(t.method,collection)

我要同意@ mauro3的观点,即允许obj.method(...) ,就有新用户可能会觉得julia是试图与python,ruby等竞争的另一种面向对象语言的风险。多派遣的令人敬畏。 另一个风险是,标准的oo风格随后变得占主导地位,因为这是用户更加熟悉的,而不是到目前为止开发的更朱利安的风格。

由于除DataFrames之外的用例仅限于与oo语言进行互操作,是否可以全部由宏来处理? 即<strong i="8">@oo</strong> obj.method(a)变成method(obj,a)

@karbarcca,这意味着可以自动以两种方式编写所有内容:

x = 3
x.sin()
sin(x)
x + 2
x.+(2) # ?!

@karbarcca https://github.com/JuliaLang/julia/issues/1974#issuecomment -38830330

t。方法(参数...)
#只是重写为
方法(t,args ...)

这对PyCall来说不是必需的,因为可重载的点仅可用于通过pyobj.func调用pyobj[:func] pyobj.func 。 那么pyobj.func()实际上是(pyobj.func)()

a.foo(x)重写为foo(a, x)不能解决PyCall的问题,因为foo不是,也不可以是Julia方法,这是我需要在运行时动态查找的东西。 我需要将a.foo(x)重写为getfield(a, Field{:foo})(x)或类似的名称(或可能重写为getfield(a, Field{:foo}, x) ),以便我的getfield{S}(::PyObject, ::Type{Field{S}})可以做正确的事。

@JeffBezanson https://github.com/JuliaLang/julia/issues/1974#issuecomment -38837755

我确实开始发现这里需要另一个运算符,但是a..b并不是很令人信服。 需要两个角色感觉非常...二等

我想说,另一方面, ..的键入比$@|键入要快得多,因为不需要按下换档键,并且是两个字符时,手指停留在同一键上:微笑:

@stevengj啊,我明白了。 但是我的观点仍然是,重写可以使用宏来完成。

对于JavaCall,实际上我实际上只需要一个unknownProperty处理程序。 我实际上不需要重写或拦截现有属性的读写。 因此,“仅当x不是现有属性时,ax才被重写为getfield(a,:x)”的规则有助于使事情保持理智吗?

@simonbyrne ,需要一个宏会打败干净,透明的type Foo; p::PyObject; end ,并且对于对象f::Foo您想要执行foo.p.bar ,其中bar是Python属性查询。 很难想象有一个宏可以可靠地区分foo.p.bar两个点的含义。

老实说,我认为风格没什么大不了的。 高质量的软件包将在可能的情况下模仿Base和其他软件包的样式,并且无论我们做什么,有些人都会编写奇怪的代码。 如果我们将点重载放在手册的后面部分,并建议仅在一些经过精心选择的情况下使用点重载(例如,语言间的互操作性,读/写属性,也许是为了避免诸如factor.U ,并且通常作为foo[:bar]的更干净的替代品),那么我认为我们不会因为使用dot来封装所有内容而超出我们的范围。 最主要的是确定_we_将使用什么并将其推荐给您,并且也许我们应该使推荐用途的列表很短,并且仅在实际需要时才进行扩展。

我们不会在foo.bar(...)添加像type Foo; bar(...) = ....; end这样的非常简单的类似于OO的语法,所以这也将限制新手的诱惑。

我基本上完全同意@stevengj在这里。 我喜欢a..b进行实地访问,因为它

  1. 看起来类似于a.b
  2. 不太方便,因为应该
  3. 只是_不太方便
  4. 没有现有的意义,而且一年多来我们还没有发现任何令人信服的用途
  5. 不像a b`那样怪异

有了这一更改,并且可能(https://github.com/JuliaLang/julia/issues/2403)几乎所有Julia的语法都可以重载吗? (三元运算符是我唯一想到的例外)对于我来说,几乎所有语法都简化为可重载的方法分派。

我同意这实际上是一种简化。 三元运算符以及&&||确实是控制流,因此有所不同。 当然,这种说法反对将a..b设为实际字段访问,因为_that_将是唯一不可重载的语法。 但是我仍然认为这是一个好主意。 一致性很好,但就其自身而言并不是最重要的。

哦,还有不能重载的函数调用。 所以基本我忘了它。

这就是#2403问题解决的问题。

是的但这比发生的事情要现实得多。

美中不足的是,对模块使用真正的字段访问运算符真的很不错,但是由于没有人愿意写Package..foo ,所以这可能不会发生。

制表符在圆点后变得有点难看; 从技术上讲,您必须检查x.可能调用的方法,以查看列出对象字段名称或模块名称是否合适。 我希望没有人尝试定义getfield(::Module, ...)

我认为可以像这样完成制表符: foo.<tab>列出“公共字段”,而foo..<tab>列出“私有字段”。 对于模块,只允许Mod.foo的默认实现为Mod..foo并告诉人们不要将getfield方法添加到Module吗? 我的意思是,您已经可以用该语言重新定义整数加法-所有地狱都会崩溃,您会遇到段错误,但我们不会尝试阻止它。 这不会比这更糟,对吗?

实际上,这要比这稍差一点,因为编程语言实际上只关心命名。 解析名称比添加整数重要得多。

我们没有太多选择,只能将Mod.foo默认设置为Mod..foo ,但是在某些地方可能必须使用Mod..foo进行引导。 ..运算符在这里非常有用,因为没有它,您甚至不能调用Core.getfield来定义后备。 有了它,我们可能只删除Core.getfield而只有..

这是一个公平的观点–命名在编程中很重要:-)。 似乎是一种不错的选择-仅..而没有Core.getfield

这两个想法,

[...]将点重载放在手册的后面部分,并建议仅在一些经过精心选择的情况下使用它@stevengj https://github.com/JuliaLang/julia/issues/1974#issuecomment -38847340

[...]首选项应该是尽可能使用x.property语法@StefanKarpinski https://github.com/JuliaLang/julia/issues/1974#issuecomment -38694885

明显反对。

我认为,如果要选择第一个想法,那么为那些“精心选择的案例”创建一个新的..运算符就更有意义了。
作为优势,在当前使用[:name] (DataFrames,Dict {Symbol,...})的情况下使用..name更加易于键入/语法友好,同时明确指出与字段访问有所不同发生了。 此外, ..name的双点可以看作是旋转的冒号,是符号语法:name的暗示,并且制表符补全也没有问题。
不利的是,在PyCall等人中使用。 不会非常接近原始语法(甚至在确实必须使用.的情况下可能会造成混淆)。 但是老实说,Julia永远不会完全与Python语法兼容,并且在某些情况下,总是必须使用PyCall在Julia中键入很多内容才能在Python中执行其他简单的指令。 该..效仿.可以在这里得到了很好的平衡。 (请不要误会我的意思,我真的很喜欢PyCall,并认为它是一项重要功能,值得特别注意)

我目前更喜欢第二个想法,它决定何时必须使用property(x)x.property ,这需要一个优雅,清晰和明确的定义(如果存在)。 。
看来,如果人们想要一个可重载的. ,那是因为他们首先喜欢x.property API样式。
无论如何,我更希望将.视为不是可重载的字段访问运算符,而是作为可重载的“属性”访问运算符(也许是getprop(a, Field{:foo}) ?),它默认为不可重载的字段运算符..
还必须采取其他决定,例如,将在具体的实现代码中使用... ? 例如,对于“范围”步骤示例,哪个习惯用法? step(r::Range1) = one(r..start)step(r::Range1) = one(r.start) ? (更不用说step必须是方法还是属性的问题了)。

这就是为什么我放弃这个角度并提出以下标准的原因: https :

阅读这个有趣的话题时,只有一个念头突然出现。 导出可用于声明公共字段,而所有字段在定义模块中均可见,例如:

module Foo
   type Person
     name
     age
   end
   export Person, Person.name
   <strong i="6">@property</strong> Person :age(person) = person..age + 1
end

在这种情况下,导出的Person仍然看起来像“名称”和“年龄”,除非在这种情况下,年龄是通过添加一个函数的方式只读的。 导出所有人员可以与导出人员*或类似操作一起完成。

[pao:引号]

@emeseles请小心使用反引号来引用类似Julia代码的内容-这样可以确保格式保持不变,并防止Julia的宏为类似名称的用户创建GitHub通知。

...令人困惑:清晰易记的sintax很好

我真的很希望能够做到这一点。 这是否足够大以至于可以将其(或#5848中的WIP)标记为0.4项目?

是的,这绝对是一个项目。

我认为我们大多数人都同意,至少在开始时应该限制其建议的用法。 我的感觉是,最初建议仅将其推荐给两种用法:互操作性(与其他语言,如PyCall中的语言,更普遍的是点符号是自然的外部库),以及可能具有可变状态的对象(因为get_foo(x)set_foo!(x, val)很难看)。

即使我们仅建议将其用于外来呼叫互操作性,单以该目的也足以证明该功能的合理性。 对于像Julia这样的新语言,与软件的其余部分进行顺畅的交谈非常重要。

史蒂文(Steven),我不太了解getter / setter,因为我担心它将很快导致不一致,但是我同意其他用例。 最重要的是,我们在Gtk.jl中具有动态属性,该属性也将从语法中受益。 我个人最喜欢的是Stefan在#5842中概述的枚举实现。

磕碰。 是什么阻碍了这一问题的进展? 是否需要做出决定,还是这个问题取决于尚未完成的其他内部更改,还是仅仅是编码?

是什么阻碍了这一问题的进展?

有人在做这项工作,并且对是否应该做的事情有些犹豫。

请注意, @ ihnorton已经在#5848上进行了早期实施。 我认为工作停滞不前的主要原因是,Julia核心团队需要明确声明这是否是所需功能。

我对此表示赞同。 @JeffBezanson似乎在篱笆上。

对我来说,拥有此功能将使我们从大型Python代码库向Julia的过渡更加容易。 为了向学生解释,如果他们使用Python代码,则他们需要的语法与他们习惯的语法完全不同,这可能会变得很困难。

我们在此线程中进行了上面的讨论,但我仍然看不到完全同意。 当前,一些人认为公共API是由函数/方法组成的,而私有API是复合类型的字段。 我可以从该方案中看到非常罕见的例外。 (LU分解中的.U ?)

这并不意味着我反对这一点,因为Python访问和枚举在某些情况下很有用。 仍然有人可以质疑这里的迫切需求,以及在开发周期结束时明智地推动这一需求。

@ ufechner7 ,我同意主要的动机是语言间互操作。 @tknopp ,我们永远不会在这样的事情上获得一致同意。 最终,它取决于@JeffBezanson@StefanKarpinski的决定。

我认为很多犹豫来自我想像是杰夫最糟糕的噩梦:

module DotOrientedProgramming
  Base.getfield(x, ::Field{:size}) = size(x)
  Base.getfield(x, ::Field{:length}) = length(x)
  ⋮
end

我也非常不喜欢这种方法-任何决定以这种方式滥用它的软件包都会在系统中的_all_类型(包括我自己的)上施加滥用。 此功能非常强大,它将改变Julia的书写方式。 变得更好,(也许,但希望不会)变得更糟。

是的,史蒂文,可以肯定,我的措词可能不正确。 我想说的是,这种改变会对语言的发展产生重大影响。 另外,我们在另一个问题中拥有的“正式接口”思想也通过使.重载而产生影响。 是的,让@JeffBezanson@StefanKarpinski决定。 问题仍然在于是否必须立即执行该决定。

对于它的价值,我开始赞成使几乎所有语法都可重载,然后依靠文化来避免过于繁琐。

+1 。 我认为这里有一个很强的类似于call超载的哲学(并且可能是实际的...)类比。 该手册需要一个标题为Don't do stupid stuff: we won't optimize that 。 (当然, call超载在某种程度上是出于性能方面的原因,但是它很容易被滥用)

  • :100:做到这一点。 我认为文化应该足以激励人们不要滥用这一点

总的来说,我赞成。 潜在的虐待不是我最大的担心。 对我来说最大的问题是

  • “实际”字段访问的可接受语法。 我不太喜欢a..b
  • 模块。 合格的名称非常重要。 用方法分派来表达它们是可能的,但是有实际困难。 也就是说,您需要经历多个编译器阶段(甚至可能是通过内联),才能知道您有一个合格的名称。 对于编写使用AST的工具的人来说,这使工作变得更加艰难。 这也使得使用此类工具错误地处理这种情况非常容易。

这些问题可以通过使用相同的语法一次解决,但几乎无法想象此时除了对模块使用.任何其他方法。 _内部地_肯定会有模块引用的抽象语法; 如果没有很好的方法来揭露它,那将令人沮丧。

我在这个问题上的两分钱:为什么不使用:作为合格名称? 它已经被用于类似的东西:

import Base: call, show, size

这会给像

module Foo
    module Bar
        f(x) = 3*x
    end
    const a = 42
end

<strong i="10">@assert</strong> Foo:a == 42

Foo:Bar:f(789)

还是他们已经过多使用了:符号? ::符号(C ++样式)对我来说似乎太冗长了。

:已经是Julia中最重载的符号,因此恐怕不会有所帮助。

我们可以通过使module.name不可重载来简化合格的命名问题吗? 由于模块绑定是恒定的,因此只要知道a.b的LHS是一个模块,就可以使我们保持相同的语义,但可以将所有常规逻辑短路以进行合格的名称查找。 我认为,不允许人们改写在模块中查找名称的含义是很合理的。

我更喜欢a..b语法用于实地访问。 您对此有何异议?

另外:我有点希望我们可以使用( )来导入导入列表,例如某些功能语言。 即:

import Base (call, show, size)

我的理由是我们可以使逗号为可选,并允许尾随逗号。 确实让我感到烦恼的是,所有导入的名称都需要尾随逗号,但最后一个不能有一个。

是的,我只想提到使a.b意思是“如果a是一个模块,则先进行模块查找”。 这可能会有所帮助,而且我们当然不想覆盖模块查找的含义。 不过,它确实要花费一些复杂性,因为我们不能将a.b为调用getfield(a,:b) 。 它必须是带有隐式分支的特殊AST节点。 当然,我们可以使用_explicit_分支,但是我担心AST膨胀。

解决前端和后端需求之间如此巨大的冲突似乎并不容易。

如果其他人都喜欢a..b我想我可以学会忍受它了。 在我看来,这意味着完全不同,也许是一个间隔。

我也不喜欢a..b ,但想知道为什么根本需要它。 在阅读此线程时,会给人一种印象,即重载将仅用于不需要实时字段访问的语言包装器和动态用例中。

因为在某些时候,您需要访问对象的表示形式才能对其进行任何处理。 有人可能会争辩说这将是相对罕见的,因此可能像get_actual_field(a,:x)一样丑陋,但这似乎太重要了,因为它没有语法。

我看到了,但这听起来像我们在寻求一种我们不希望任何人使用的语法正确吗?

对于动态用例,不提供..是一种表示是的方式,而对于点式编程则不提供

我不认为这会阻止面向点编程。 您仍然可以使用上述

虽然a..b语法确实看起来像是一个间隔(我已经这样使用过),但我只是不认为间隔算术需要自己的输入语法-编写Interval(a,b)只是很好,没有其他人想使用该语法,因为多年以来它一直是Julia的运算符,而且没人在许多事情上使用它。 看起来也像现场访问。

我们可以用m..name代替丑陋的module_name ,这是一线希望。 无法访问Module对象的字段已成为问题。

是的,我只想提到使a.b意思是“如果a是一个模块,则先进行模块查找”。 这可能会有所帮助,而且我们当然不想覆盖模块查找的含义。 不过,它确实要花费一些复杂性,因为我们不能将a.b为调用getfield(a,:b) 。 它必须是带有隐式分支的特殊AST节点。 当然,我们可以使用_explicit_分支,但是我担心AST膨胀。

我们可以通过使a.b无条件地表示getfield(a,:b) ,然后将与getfield(::Module, ::Field)方法相交的方法添加到getfield来使错误吗? 强制执行该行为是一种奇怪的方法,但最终会产生相同的效果。 然后降低就可以利用我们知道您不能这样做来欺骗和降低module.name到合格的名称查找这一事实。

好的,我用另一种方式陈述它:该线程中的任何人都将使用.. ,如果是,那将是一个示例用例? (即可能完全遮盖内部字段访问就可以了)

@StefanKarpinski是的,可能可行。 可能是我们想要某种“密封”方法的另一种情况。

@tknopp访问module..namemodule..parent :)另外,为了澄清起见,您是否主张像get(obj,:field)这样的函数调用语法用于低级字段访问?

不,我不主张使用某种语法。 我只是认为最好确定为什么需要此功能以及用例是什么。 对于动态用例,可以的是

  • 如果未定义Base.getfield(a, ::Field{:b})a.b是字段访问
  • 如果已定义Base.getfield(a, ::Field{:b})a.b是动态版本。 在这种情况下,实地访问可能会被遮盖

我的问题是是否存在用例无法屏蔽阴影的用例。

是; 你可能希望定义pyobject.x使x总是抬头在的PyObject的字典里,所有x 。 然后需要一个单独的机制来访问pyobject的julia字段。

啊,所以全部还是全部? 我以某种方式给人的印象是

type A
  c
end

Base.getfield(a::A, ::Field{:b}) = 3

a = A(1)

a.c # This still calls the field access
a.b # This calls the function

是的,您可以这样做,但并非所有对象都可以。 有些人会想要定义getfield(a::A, ::Field)来拦截所有字段。

好,谢谢,现在我明白了。 所有动态用例都需要getfield(a::A, ::Field) ,因此需要任何方式来调用内部字段。

那么我的看法是,除非有人发现令人讨厌的实际用例,否则Core.getfield就足够了。

这可能是给定的,但我们也将允许覆盖setfield! ,对吗? 我真的很想将可变视图公开到行成为类型的数据库中。

是的,那是我的印象。

好的,恕我直言,是使用..进行实地访问还是使用Core.getfield并不是什么大问题。 可以将一般功能介绍为实验性功能,并对此进行更改。

问题是这是否适合0.4的时间范围。 因此,#5848接近最终实现并且该模块可解决吗?

@johnmyleswhite :我也将投票赞成使它对称,并允许setfield! 。 在Gtk.jl中,我们将两者同时使用。

似乎不太清楚何时使用此功能以及何时不使用此功能的规则是什么。 我看到了PyCall的意义,其中必须动态查找方法/字段,因此不能是Julia方法/复合类型(并且生成的语法更接近Python)。 但是,为什么还要将其用于Gtk.jl? 如果它开始执行foo.bar = x而不是setbar!(foo, x) ,那么标准的Julia代码也将不可避免地也开始使用此模式:这是我们想要的吗? 也许是,但是让我们澄清一下。

使用此功能来实现为抽象(和具体)类型定义的属性获取器和设置器,是否可以接受/建议使用?
我猜想这将避免使用用于从不同类型的不同模块获取属性的方法的名称冲突。

参考: https : : //groups.google.com/forum/#!msg/julia -users / p5-lVNlDC8U / 6PYcvvsg29UJ

@nalimilan :Gtk具有动态的属性系统,它与获取器/设置器无关。

@tknopp啊,好的,的确如此。 但是对于大多数常用属性,您都具有(快速)getter / setter函数以及动态属性。 因此,您是否建议使用getter / setter函数(如果可用),而字段重载语法仅用于没有属性的属性? 对我来说听起来不错-但最好对此IMHO制定明确的政策。

在我在这一点上(我认为我们需要这方面的一些试验,以找出正确的规则)视图, f(x)是更好时, f有意义的,因为像“一般的独立概念长度”,而x.f应时,可以使用f不是真正独立的x 。 为了使我的前面的示例适应这一点,具有通用的step函数并不是真正有用的,因为大多数矢量和集合都没有任何步进概念–仅当您有范围时,某种。 因此,以x.step作为获得范围x 。 这有点判断力,但是我想生活中充满了这些。

我不喜欢..因为它无法传达直接给我的信息。 foo.bar.末尾的额外点如何将其固定为直接访问。

也可以选择一个unicode符号:我们还有很多符号...

@GlenHertz ,如果您必须链接字段访问,那实际上就不起作用。

@simonbyrne ,我通常反对在核心语言或标准库中使用Unicode进行任何操作。 允许它是一回事,强迫人们使用它完全是另一回事。

在我看来,这时(我认为我们需要对此进行一点试验以找出正确的规则),当f像“ length”这样的通用独立概念有意义时,f(x)更好,而应该使用xf当f不是真的独立于x时。

我使用此功能的个人规则将是:仅将其用于语言互操作或用于“几乎是字段”或“增强的字段”的事物。 例如,我可能会用它来更新取决于类型中所有字段值的缓存。

我对此功能有一个大疑问:它如何与类型推断交互? 似乎您即将定义一个函数getfield(x::T, s::Symbol) ,该函数针对s不同值生成不同类型的输出。 那仅因为getfield是神奇的而起作用吗? 您可以在程序的任何位置重新定义固定的xsgetfield(x, s)输出吗? 如果是这样,那又如何与无法重新定义类型?

似乎您即将定义一个函数getfield(x::T, s::Symbol) ,该函数针对s不同值生成不同类型的输出。

这就是为什么计划将其表示为getfield{s}(x::T, f::Field{s}) ,其中s是符号。

我错过了。 谢谢你让我挺直。

@nalimilan :是的,重载字段仅用于动态属性。 这就是詹姆森想解决的方式,我认为这很好。 所有真正的getter和setter都是自动生成的,但是在没有所有get / set命名的情况下仍然可以运行。 GAccessor模块中的实时代码(简称G_

关于语法,为什么不使用<-进行实地访问?
它类似于c ++中的->用于lamda,但<-当前未使用。
可以从类型中读取,直接获取值。

对于那些仍希望定期使用它的人来说,它会留下..未使用的空间,并且会用光一个未使用的货币对,到目前为止我还没有想到其他用途。

我们不要将R的赋值表示法用于字段访问。

我们可以使用->直接镜像C / C ++并获取新的语法
匿名函数。 我从来都不在乎匿名功能
语法,因为它有点简洁/不可读。 也许我们可以做
就像是

func(x)x ^ 2结尾

或更长,更一致

函数(x)x ^ 2结束

也许有一种方法可以提出不需要的良好语法
用尽。

不必过多地改变讨论,但这绝对是有道理的
使用->进行实地访问。

在2015年1月28日星期三上午8:49,John Myles White [email protected]
写道:

我们不要将R的赋值表示法用于字段访问。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/1974#issuecomment -71857083。

@quinnjfunc (x) x^2 end已经可以使用。 但是对匿名函数使用非常简洁的语法很高兴: map(x->x^2, 1:10)

我认为字段访问不需要特殊的语法(如@simonbyrne建议的Unicode字符也可以作为选项使用)。 我不想失去x -> x^2

看来这个问题仍在讨论中吗? 阅读这里有关点运算符的所有各种评论很有趣。

是否有建议添加其他新的操作员令牌? 使用:>可能是一个不错的选择。 它与|>具有相似之处,并且可能具有更原生的Julia _feel_:

c = foo.a + foo.b
pyobj:>obj:>process(c)

尽管仍然比以下内容容易编写得多:

pyobj[:obj][:procees](c)

或比较:

someobj :> array |> length 
# vs
length(get_array(someobj)) 

我是朱莉娅(Julia)的新手,但是我很快就对多派遣方法深感赞赏。 面向对象的编程(尤其是科学编程)使许多任务变得更加繁琐。 我担心OO范式/语法会负面影响Julia的风格发展。

或者,因为我忘记了字符串中间化和/或引用,所以:

someobj <: field_name |> length

@elcritch<:当前用作Julia的“是”的子类型,并且如果引入:>由于该原因,我们可能希望将其用于与类型相关的事情遗产。

如果使用instance..member是一个问题,则有一些可能。 遮住你的眼睛! 这些情况中的每一个都可能更糟:

  • instance^.member (胡萝卜点)
  • instance~.member (波浪点)
  • instance:.member (冒号)
  • instance*.member (星点)
  • instance-.member (破折点)
  • [email protected] (符号点)
  • instance&.member (安培点)
  • instance$.member (美元点)
  • instance<.>member (飞船点)

老实说,我认为(a) ..看起来足够好,(b)是否看起来不错并不重要,因为这始终是语言的晦涩之处。 大多数人会使用instance.member因为他们只会有一个字段或getfield方法,而不会同时有这两个方法。

(为此,大多数想同时使用成员和方法的人可能甚至都不想去学习.. 。他们只会为foo.member定义一个方法,并将其命名为“ real “ field foo._member 。无论如何,这可以说是更好的样式-这意味着当您阅读type定义时,将立即显而易见, _member不应该是某种东西您可以或应该直接访问。这可以证明是使..看起来像:.这样丑陋而晦涩的东西,而不是占用宝贵的标点符号房地产。)

我会错过使用..作为固定间隔运算符的功能,但是可重载的字段访问是一个值得权衡的问题。 虽然我很犹豫,害怕温和增加任何更多的含义结肠, :.似乎并没有那么糟糕。

请注意, :.目前实际上是symbol(".")有效语法,因此使用它可能不是很好。 ..可能有用的观点已被很好地理解; 我们不应该将其浪费在几乎没人会使用的语法上。 我非常乐意使用甚至更丑的东西,例如@. (因为.不是有效的宏名称,也不能以标识符开头,所以这似乎不与任何东西冲突)。 再次,这将成为Julia的一个晦涩的角落,以至于没有必要使它变得漂亮。

+1即可使用..完成此操作,并忽略任何潜在的丑陋

是的,如果可以使用.. ,我们还是去.. ,那我认为这将是一个_range_构造函数,但是嘿,例如冒号已经在那了。 start:stop

最后一个平底锅: .:怎么样? 拥有a,a。(b),a。(:b)和a.:b是否太微妙了?

@hayd ,这似乎太微妙了,很容易被偶然使用。

@ihnorton ,是否有可能复活#5848版本? 我们可以对语法问题进行讨论,仅使用Core.getfield(x, Field{y})访问“ real”字段。

除了Core.getfield语法之外,还有什么实质性的问题吗?

在#5848中, @ tknopp建议仅使“真实”字段访问可重载,这与@JeffBezanson提出的一切都应可重载的建议相反。 就个人而言,我很乐意不可能重载实际字段,除了语言的动态性质可能会使实现起来更加复杂。 例如,使用“一切都可以重载”的方法,如果您有x::Vector{Any} ,那么可以解释getfield(x[i], Field{:y}) x[i].y来执行getfield(x[i], Field{:y}) ,并且调度系统将做正确的事,而不管y是一个实数字段,而如果您只想为“虚拟”字段调用getfield ,则代码生成器将必须实现调度系统的微型子集,以便对x[i]进行运行时检查

另一个问题是Module.foo是否应该可重载。 一方面,对所有内容使用getfield具有一定的一致性,并且上面提到的Vector{Any}示例可能具有Module数组成员,因此我们必须处理这种情况无论如何。 另一方面, @ JeffBezanson指出,这可能会使编译更加困难,并使function Base.sum(...)类的声明行为难以理解。 我的偏好是使Module.foo不可重载,至少在目前,在任何情况下,只要编译器知道它正在使用Module (即不是Vector{Any} ) ; 为了保守更改内容,略有不一致似乎是值得的。

+1不允许Module.foo超载。

在这里,OO编程和语法实际上优于FP的科学计算领域是基于代理的建模。 尽管我错过了建立代理层次结构的具体继承和多重继承,但是Julia的轻量级,快速抽象和快速原型制作是令人惊奇的-已经出现了一些ABM框架。

在ABM中,点表示法优选用于表达代理交互:Agent1.dosomething(Agent2)与dosomething(Agent1,Agent2)。

这显然不是最大的用例,但是最好保留这种语法糖来思考和编码ABM。

我也非常想在Julia中使用此语法。 如
我非常欣赏设计中面向功能的方法
角度来看,方法调用语法非常有用,并且在几种情况下可读
域。 如果Ab(C)等于b(A,C),那就太好了。
2015年4月22日上午8:50,“ datnamer” [email protected]写道:

在这里,OO编程是科学计算的一个领域
实际上,语法优于FP是基于代理的建模。 虽然我
想念具体和多重继承来建立代理层级,
朱莉娅的轻量级,快速抽象和快速原型制作是
惊人的-已经出现了一些ABM框架。

在ABM中,点表示法优选用于表达代理相互作用:
Agent1.dosomething(Agent2)与dosomething(Agent1,Agent2)

这显然不是最大的用例,但是最好保留这个用例
用于思考和编码ABM的语法糖。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95218555。

在ABM中,点表示法优选用于表达代理交互:Agent1.dosomething(Agent2)与dosomething(Agent1,Agent2)。

为什么这样更好? 编辑:我的意思是在这个ABM上下文中。

拜托,不要让我们因为拼写而陷入宗教战争。 @ dbeach24 ,没有人提议a.b(c)在Julia中等于b(a,c) ; 那不会发生。

重载点对于与其他语言进行自然互操作至关重要。 足够的理由。

Subject.Verb(DirectObject)

在几种情况下是很自然的。 许多OO程序员习惯于
它,尽管它只是对函数(A,B)的重新排序,但该重新排序
IMO在可读性方面做了很多工作。
2015年4月22日上午10:32,“ Andy Hayden” [email protected]写道:

在ABM中,点表示法优选用于表达代理相互作用:
Agent1.dosomething(Agent2)与dosomething(Agent1,Agent2)

为什么这样更好?

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95256390。

我正在提议。 (对不起,我不是故意发动战争,也不知道
这个建议是不受欢迎的。)我以为我以前见过
在论坛上,但没有意识到它已经被取消为
馊主意。 请问为什么? (你能指出我一个话题吗?)

谢谢。
2015年4月22日,上午11:09,“史蒂文·约翰逊(Steven G. Johnson)” [email protected]
写道:

拜托,不要让我们因为拼写而陷入宗教战争。 @ dbeach24
https://github.com/dbeach24 ,没有人提出ab(c)是
在Julia中相当于b(a,c); 那不会发生。

重载点对于与其他语言顺利互操作至关重要。
足够的理由。

-
直接回复此电子邮件或在GitHub上查看
https://github.com/JuliaLang/julia/issues/1974#issuecomment -95266671。

不这样做的原因之一是a.bb的范围内查找a ,而仅bb中查找封闭范围。 如果有时无法在左侧对象中查找虚线访问,那将非常令人困惑。

顺便说一句,Julia中的功能不是在对象内部而是在当前范围内查找的,被认为是一项功能。 我相信人们会开始使用在对象内部查找的函数的担心是阻止点重载的原因之一。

@toivoh ,点重载的任何实现都将使用现有的方法dispatch,因此不会改变作用域的行为。 @ dbeach24 ,不鼓励随意使用a.b(c)的基本原因是,如果您有太多语法无法执行同一操作,则语言和库会变得一团糟。 最好选择一个拼写并坚持使用,Julia的多次派发支持b(a,c)因为很明显b不“拥有” ab(a,c)方法由_both_ ac相等地确定。

当然,最大的例外是使用Python或C ++等语言调用外部库,能够很好地镜像正在调用的库的点语法。 (这样,例如,人们可以将文档和示例直接转换为Julia,而无需进行太多更改。)

我全都湿透了,但是ab(arg)是不是要取一个存储在a字段b中的匿名函数,然后使用给定的参数求值?

从我的iPhone发送

2015年4月22日,下午5:06,史蒂芬·G·约翰逊[email protected]写道:

外部

@ScottPJones目前可以正常工作,但通常不认为是好的样式。

我不关心样式与否,我只是认为,因为样式已经具有含义,这与Julia的工作方式(即,能够在字段中存储匿名函数)一致,所以它是一个很好的论点尝试将ab(arg)视为b(a,arg)。
我可能会使用具有成员存储匿名函数的结构(类型),在这里我从数据库加载用Julia编写的函数,然后对其进行解析,然后将函数存储到对象中。
做这样的事,哪种更好的“朱利安”风格会更好?

谢谢!

@ScottPJones我认为已经达成共识,这些不应等效*。

“样式”规则可能会有例外,但是必须有令人信服的案例才能在字段中使用put函数,这与点重载相同。 我认为问题是人们不应该这样做,因为他们可以这样做。

那可能是一个例子,但也可能有更好的方法(肯定不是唯一的方法)...

99%以上的时间最好使用typeof(a)进行调度; 没有功能字段,没有点重载。

* _但是,我想每个人都知道这是降落的第二秒……

在D中,它们甚至以a.b(arg)的名称具有“统一函数调用语法”的名称,并且非常流行,但是我认为通用函数,Julia工作的多种分派方式对它非常奇怪。 如果有问题的函数是匿名的或完全是鸭子式的,那么我想一切都会起作用,但这是限制性的IMO。 除了基于类的传统OO语言的习惯外,我认为没有太多理由将函数字段存储在复合类型中。 如果要从某个地方加载泛型函数,则最好在模块中存储泛型函数。

但是我们现在离“实质性问题”还差得很远。 我也赞成在允许重载getfield,不允许在模块上重载getfield以及不为“ true getfield”的特殊语法过分担心的情况下保持保守。

@stevengj :是的,正如我想说的那样,这是一个根本原因,无论我们如何处理点重载, a.b(c)等于b(a, c)永远不会发生。

关于邮件列表,有一些讨论。 从我的角度来看,与此线程最相关的帖子(@nalimilan撰写)是: https ://groups.google.com/d/msg/julia-users/yC-sw9ykZwM/-607E_FPtl0J

添加到@johnmyleswhite关于何时使用此功能的个人政策的评论-令我惊讶的是,一些HTTP想法在这里可能有用,并且getfield()应该没有副作用,而setfield!()应该是幂等的(也就是说,多次调用相同的值应该具有与一次调用相同的效果)。 不一定是编译器强制执行的严格规则,而是使用指南,以防止事情变得太疯狂。

我已经发布了使用带有指针参数的参数类型的变通方法,并在设置字段时转换为调用自定义设置程序:
帖子: https: //groups.google.com/forum/#!topic/julia -users / _I0VosEGa8o
代码: https

我想知道是否应该在包中使用此方法作为解决方法,直到setfield! 是否可以重载,或者对参数类型系统的压力太大?

我想提一下getfield / setfield!超载的另一个好处,我希望这是正确的选择,否则对不起。 (相关主题出现在https://groups.google.com/forum/#!topic/julia-users/ThQyCUgWb_Q上)

朱莉娅与getfield / setfield! 重载将允许在_external package_中实现自动重载功能的出色实现。 (要获得此功能,请参阅IPython自动重载扩展https://ipython.org/ipython-doc/3/config/extensions/autoreload.html中必须进行的所有艰苦工作。)自动重载的思想是在使用REPL时可以修改外部模块中的功能和类型。

TLDR: getfield / setfield!重载,字典和类似于https://github.com/malmaud/Autoreload.jl的软件包应该可以解决问题。


更具体地说,请想象一个类似于Autoreload.jl的软件包,该软件包可以执行以下操作。

首先创建一个模块M.jl:

module M
type Foo
  field1::Int64
end
bar(x::Foo) = x.field1 + 1.0
end

在REPL中,键入

julia> using Autoreload2
julia> arequire("M")
julia> foo = Foo(42)

然后将M.jl更改为

module M
type Foo
  field1::Int64
  field2::Float64
end
bar(x::Foo) = x.field1+x.field2

这将自动重新加载并转换为

# type redefinition removed as already done by Autoreload.jl
const field2_dict = Dict{UInt64,Float64}()
setfield!(x::Foo, ::Field{:field2}, value) = field2_dict[object_id(x)] = value
getfield(x::Foo, ::Field{:field2}) = field2_dict[object_id(x)]
<strong i="25">@do_not_inline</strong> bar(x::Foo) = x.field1 + x.field2

然后在REPL中,你可以做

julia> foo.field2 = 3.14
julia> println(bar(foo)) # prints 45.14

性能不会比使用Python差,因此从IPython自动重装迁移工作流的人们不会在性能方面损失任何东西。 重新启动REPL后,您将恢复完整的性能。

我厌倦了编写a[:field][:field2][:morestuff](b[:random_stuff])因为它不是很可读。 所以我写了这个小宏,可以在0.4和0.5的用例中使用
https://github.com/sneusse/DotOverload.jl

TL; DR
转换表达式AST的宏a.b -> getMember(a, :b)

从0.6中删除,因为尚无共识,认为这是个好主意,并且对点语法的处理提出了相互矛盾的建议。

@Keno :您是否有指向冲突提案的链接?

不要以为@StefanKarpinski尚未编写它,但我希望很快就会有Julep。

我发现object.fieldname比诸如fieldname(object)get_fieldname(object)类的getter函数更好。 可能有object.fieldname (或object$fieldname )被称为getpublicfield (也许有更好的名字),而object..fieldname是实际的getfield (私有)可能是个不错的选择。 这样,类型应该定义getpublicfield而不是getter,并且尝试执行object.fieldname应该给出一个错误ID,该字段为private(如果没有为该字段定义,则为private getpublicfield )。

我添加了决策标签。 这个问题已经讨论了很长时间,无论是否必须完成。 在阅读#5848时, @ JeffBezanson @StefanKarpinski@stevengj似乎想要这个。 如果是,那么这个问题应该成为一个里程碑,这样就不会被遗忘。 否则关闭。 无论如何,我认为这是应该在1.0之前完成的更改。

@JeffBezanson和我昨天才讨论这个问题。 初步结论:(i)是的,我们应该有这个; (ii)不允许Module点重载(将进行特殊处理); (iii)不要为Core.getfield提供任何特殊语法(因为没有紧迫的要求,需要使重载的getfield与“ real”字段具有相同的名称;后者可以以下划线)。

@stevengj :听起来像是一个合理的计划。 您能否指出是否将其限制为单参数或是否也应支持多参数版本a.fieldname(b) ? 这将为上述讨论得出结论。 此外,最好在此标签上贴上适当的里程碑标签(1.0?)。 谢谢!

杰夫和我没有讨论多参案。 我的感觉是,我们最好也支持它,因为您可以通过从无参数的情况下返回一个函数来模拟它(但是出于相同的原因立即执行并不重要)。

我使用转换器来转换值并验证数据。
像这样:

abstract AbstractAge{T}
abstract AbstractPerson
type PersonAge <: AbstractAge{AbstractPerson} 
    value::Int64
end

Base.convert(t::Type{AbstractAge{AbstractPerson}}, value::Int64) =  begin
  if value < 140 && value > 0
    PersonAge(value) 
  else
     throw(ErrorException("ValueError"))
  end
end

type Person <: AbstractPerson
  age::AbstractAge{AbstractPerson}
end 

a = Person(32)
a.age = 67

这是一个有趣的三行实现:

diff --git a/base/boot.jl b/base/boot.jl
index cd3ae8b..a58bb7e 100644
--- a/base/boot.jl
+++ b/base/boot.jl
@@ -266,6 +266,9 @@ Void() = nothing

 (::Type{Tuple{}})() = ()

+struct Field{name} end
+(::Field{f})(x) where {f} = getfield(x, f)
+
 struct VecElement{T}
     value::T
     VecElement{T}(value::T) where {T} = new(value) # disable converting constructor in Core
diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm
index b4cb4b5..59c9762 100644
--- a/src/julia-syntax.scm
+++ b/src/julia-syntax.scm
@@ -1685,7 +1685,7 @@
     (if (and (pair? e) (eq? (car e) '|.|))
         (let ((f (cadr e)) (x (caddr e)))
           (if (or (eq? (car x) 'quote) (eq? (car x) 'inert) (eq? (car x) '$))
-              `(call (core getfield) ,f ,x)
+              `(call (new (call (core apply_type) (core Field) ,x)) ,f)
               (make-fuse f (cdr x))))
         (if (and (pair? e) (eq? (car e) 'call) (dotop? (cadr e)))
             (make-fuse (undotop (cadr e)) (cddr e))

我的想法是a.b实际上应该调用投影函数Field{:b}()而不是getfield ,这样您就可以免费获得像x->x.a这样的函数。 这也允许getfield始终表示低级字段访问。

上面的实现完全可以使用,但是在编译器上很难(sysimg + 5%,这确实是一个令人惊喜的惊喜)。 因此,这将需要一些专门的启发式方法,并且需要更新一些早期的优化方法,但是希望这将是可行的。

我很惊讶测试可以通过该实现。 这种语义是否使代码生成无法优化模块引用? 似乎还会使全局变量变得更加昂贵(速度和内存)。 看来这不太可能出现在sysimg中。 尽管推论降级似乎应该使sysimg变小,但变差5%似乎并不是一个好的开始)

是的, jl_resolve_globals有代码可以将它们转换为需要更新的GlobalRefs。 除此之外,它们应该内联,以便代码生成器可以处理它们。 推断而言,我们可能只需要一个类似于元组getindex的特殊情况。

我们可能只需要一个类似于tuple getindex的特殊情况

该特殊情况希望并假定不会定义与内置getindex方法签名相交的方法。 我不认为这种情况在这里特别适用。

我会为::Module添加一个方法,并说不允许您对其进行阴影处理---可能由于实际错误而强制执行。

@JeffBezanson您是否有一个包含这三行的分支? 我试图将它们自己添加到本地版本中,但茱莉亚现在似乎已经过时了,我无法使其正常工作。

我应该说,如果我只希望在julia 1.0中有一个功能,就是这样。 这将解决我在MimiQuery中都存在的两个长期存在的问题。

我认为查询的情况非常普遍。 我相信可以使用该版本编写NamedTuples ,而不必在宏中生成新类型,这反过来又使我能够编写生成的函数,该函数返回带有一系列字段的NamedTuple在生成的函数中计算。 我认为这将使我能够编写类型稳定的blackrock / NamedTuples.jl#4版本,这是我目前在Query.jl中最大的绊脚石。

长话短说,这对于茱莉亚的整个数据处理故事来说是非常宝贵的。

以下语法可用吗?

function (obj::MyType).plus(n::Int)
       return obj.val + n
end

您为什么要在世界上使用该语法?

想要写obj.plus(n)而不是obj + n是一些严重的斯德哥尔摩综合症。

好的,给定的例子是一个琐碎的例子(一个不好的例子)。
这只是使用这种语法而不是重载getfield并能够使用参数的想法。

我最关心的功能。 对于getfield来说,使用缩写语法显得不那么重要,也不值得陷入争论。 此外, getfieldsetfield!直接反映getindexsetindex! ,因此在Julia中有些自然。 最后,广泛使用OO样式并不完全适合Julia的惯用语,而且我认为我们不希望通过类似OO的方法定义语法来鼓励它(但请参见上面有关参数的

我发生的一件事是$运算符已被弃用。 现在,这显然使得像$(x, sym::Symbol) = ...这样的事情已经可用,但是我们还可以接受更高级的语法重写,例如:

x$y          => $(x, ::Type{Val{:y}})
x$z(args...) => $(x, ::Type{Val{:z}}, args...)

我认为这已经涵盖了本期提到的大多数情况,而没有进行全面的getfield重载。 坦白说, .运算符在Julia中在语义上已经很饱和,因此类似这样的东西更容易理解,也很方便,仍然有用。

@quinnj ,已在#18696中提出。

但是,由于我们已经.进行字段访问,所以似乎不宜使用两个类似字段访问的运算符,一个可重载,而另一个不可。 对于跨语言调用Python和其他OO语言(几乎普遍使用. ,这有点不自然。

我不认为与其他语言的互操作性是引入类似内容的有效论据。 就像在说:“ Python代码看起来像这样,因此要假装成Python,我们也应该这样做。” 我还没有看到这样的论点,它可以使Julia本身变得更好和/或更一致。 众所周知,Julia不提供OOP样式的x.f()语法; 允许这样的事情要求不一致。

@stevengj ,虽然我来自哪里,但实际上是x.f _isn't_字段访问的事实。 没有实际的字段成员f 。 整个问题都与getfield的重载有关,我认为主要的担忧是x.f实际上是指向一个字段或正在做其他事情的潜在混乱。

我提出的语法重写的优点是,它可以完成语言互操作的故事,而不会引起与getfield的其他混淆。 那就是我提到.会过饱和的意思。 x$f真的会那么麻烦吗? 我只是不明白为什么这个_has_要使用点运算符。

整个问题都与getfield的重载有关,我认为主要的担忧是x.f实际上是指向一个字段或正在执行其他操作的潜在混乱。

我不认为@JeffBezanson的三行建议中可能会引起混淆: a.b始终降低为投影函数调用,而从未调用getfield 。 然后在base中有一个默认/后备方法,用于调用getfield的投影函数。 用户可以根据需要将自己类型的方法添加到该投影函数中。 getfield在该提议中始终意味着低级别的字段访问,用户不会向getfield添加方法(我想这就是您“重载getfield”的意思)。 在我看来,这很干净。 话虽如此,但我仍不清楚该提案是否仍在讨论中,似乎有些编译器问题令我不明白:)

我真的很想拥有这个功能(我一直在Scala中很喜欢它)。 恕我直言,直接访问字段在Julia中非常自然(与Java定义所有内容的getter和setter的方式相比)。 但是如果没有添加“虚拟”字段的能力,就很难在不破坏大量代码的情况下发展/重构结构。

我知道它已被标记为v2.0,但我想再次提出来,希望重新考虑v1.0。 对于包/库开发人员来说,此功能将非常有价值。

在为用户构建程序包时,使属性访问器超载将使程序包维护者可以呈现更加简洁的用户界面。 我会说

complex_data_structure.attribute

容易打字比

get(complex_data_structure, :attribute)

其中get是获取:attribute描述的数据所需的函数。

这也可能会提高REPL的可发现性,并有利于语言代码和数据探索。

另外,setter属性拦截器也将具有不可估量的价值。 考虑下面的示例(目前已经在我的许多脚本中找到了:),

set(complex_data_structure, :attribute, modify_attribute(get(complex_data_structure, :attribute), additional_arguments))

如果将属性获取器和设置器作为Julia的一部分包括在内,则以上内容将变为以下内容。

complex_data_structure.attribute = modify_attribute(complex_data_structure.attribute, additional_arguments)

以我的拙见,这更具可读性。 (旁注:我可以使用管道组成使其更具可读性,但是additional_arguments再次会使它复杂化。这里的论点仍然成立。)

此外,约束用户提供的值确实是很常见的用例,在v1.0中具有很大的意义。 它将大大改善多个软件包的用户界面。 目前,据我所知,似乎没有办法进行以下操作(如果我错了,有人可以纠正我)吗?

module point

mutable struct Point
    x::Int
    y::Int
    function Point(x, y)
        if x < 0 || y < 0
            throw(error("Only non-negative values allowed"))
        end
        this = new(x, y)
    end
end

end
# point

p1 = point.Point(-1, 0)
# Only non-negative values allowed

# Stacktrace:
# [1] point.Point(::Int64, ::Int64) at ./In[30]:8
# [2] include_string(::String, ::String) at ./loading.jl:515

p1 = point.Point(0, 0);
p1.x = -1;
p1
# point.Point(-1, 0)

构造函数可以将域输入限制为不可变的结构,但是用户仍然可以输入无效的值。 属性获取器和设置器在这里会有所帮助,因为它们可以使对数据有效性的反馈更加及时,这将使使用该语言的用户界面更加简洁。

这也将极大地有利于其他软件包,例如PyCall.jl和DataFrames.jl,以为用户构建可以说更好,更直观的界面。 这些软件包的作者上面也表达了对具有此功能的兴趣。

有什么想法吗? 通读该线程,看来这已经非常接近final了。 我很好奇作者对此有何想法?

在此处通过类型稳定的可选宏实现: https

@bramtayl ,我认为必须在a.b表达式之前放置@overload_dots来解决这里的问题。

我并不是在建议将宏添加到基本库中。 我建议将宏使用的策略烘焙到解析器中。 但是,它是一个宏,可以在整个文件上运行,甚至可以入侵到IDE中。

这将需要将a.b解析为Expr(:., :a, :(Val{:b}())并降低为get_field_overloadable(a, Val{:b}())

@bramtayl ,请参见上述Jeff的

重载getfield的一个问题是某些库没有与数据结构内部的合理接口。 当我只想修改结构中的数据点以便可以运行代码并为明天做报告时,我并不特别希望在依赖项深入三层的库中编辑代码,以便能够以合理,快速的方式直接访问数据点。 我想感觉自己可以控制正在使用的数据结构。

第二点是,当我使用getfield和setfield时,我希望我可以直接访问数据结构而不是某些庞大的函数机制,这是合理的期望。 此外,关键字struct用于定义类型,使您想起C,而且我认为它使您期望getfield访问应该是直接的。

因此,我认为使用$运算符是一个合理的折衷方案,因为不希望像getfield这样的访问是直接的,在其他情况下它已经是特殊字符,因此不会太令人惊讶当以这种方式使用它时,它很容易被弃用,因为很少有人使用它,并且因为它将不再是一个函数,并且它在R中的用法类似。

重载getfield的一个问题是某些库没有与数据结构内部的合理接口。

从上面的@JeffBezanson的实现不会使getfield (或setfield )过载。

我也希望将其设置为1.0。 我经常发现自己在编写吸气剂/设置以使我的代码保持某种灵活性(然后想知道如何命名它们)或“允许/鼓励”我的代码用户使用直接字段访问之间感到困惑。

在我看来,防御性地编写大量的getter和setter似乎并不像Julian一样-毕竟,Julia没有私有/受保护的字段,因此鼓励用户直接访问字段。 接下来是一个问题,即如何命名许多getter / setter函数而又不冒与其他程序包发生大量冲突的风险。 另外,为getfieldsetfield! (或类似的)添加方法,并在Val{:fieldname}左右调度,这似乎也不是很友好。

另一方面,如果直接字段访问基本上永久地锁定了所有结构,则显然不应该鼓励这样做-特别是对于寿命长的代码。 @JeffBezanson的实现似乎摆脱了这种困境。

是的,@ davidanthoff。 我必须将getfield重载和.字段访问语法的重载混为一谈。 我的意思是重载字段访问语法。

有向后兼容的方法可以添加此功能,因此可以在1.x版本中添加它,但在1.0中不会发生。 即使我们有一个完整的设计,也没有时间实施它。

在指向(C-)结构的指针上使用点语法非常方便。

我首选的语法是只使用点来移动和转换指针,并使用[]进行解引用。

因此,构造somepair a :: Int b :: Int end和p :: Ptr {somepair},然后pa是指向a字段的指针,我用pa [] = 3写入它,或者用x =读取pa []。

接下来,我们只需要类型的前向声明,可能还需要一种导入C标头的方法,然后包装C变得轻而易举。

ps,澄清一下:

function getfield(p::Ptr{T}, fn::Symbol) # dispatch on values of T and fieldname
ftype = fieldtype(T, fn)
offset = fieldoffset(T,fn)
return convert(Ptr{ftype}, p+offset)
end

getindex(p::Ptr{T}) where T = unsafe_load(p)
setindex!(p::Ptr{T}, v) where T = unsafe_store!(p, convert(T,v))

为了获得加分,可以导出julia运行时库中的类型,并且pointer_from_objref实际上可以为我们提供一个类型明确的指针,以更方便地进行内省。

我觉得只要有两个具有相同偏移量的字段,工会就应该可以自动工作?

@chethega我认为您正在寻找的是https://github.com/JuliaLang/julia/pull/21912 ,它提供了大致相同的功能,而没有围绕C内存模型的采用的问题(违反类型的行为, -边界访问,内部指针的别名等,这限制了该语言可能的性能优化)。

持续传播是否使它更可行?

请将里程碑更改为1.0! :)

@ Liso77是什么意思? 如关闭时的状态所示,这已在git master上实现。

@nalimilan对不起,如果我理解错了! 但我认为,将1.x标记为已推迟的事情,将1.0现在解决

开源是一个分散的社区。 里程碑设置了1.0应该完成的内容,但是贡献者可以根据自己的需要进行工作。 在这种情况下,有人希望在1.0中使用它,因此他们贡献了代码来实现这一目标。

@ Liso77据我了解,这不会出现在v1.0中。 Julia v1.0功能的冻结日期设置为12月15日,但是此问题已于12月17日关闭,因此我认为我们可以在1.x版本中看到它。 如果我的解释不正确,核心开发人员可以纠正我。

不,它将合并到master中,并将在下一个版本中发布。

:) 好! 我只是认为最好将1.0标记为1.0。 如果不想要,那么抱歉打扰! :)

我认为NEWS文件是查看1.0版本的更好方法。

里程碑的添加是指“可以在不破坏1.x版本系列的兼容性的情况下实现”,但是由于代码已经准备好,因此无论如何在1.0功能冻结之前就已经合并了。 为了清楚起见,我删除了该里程碑,但此时,在master中合并的所有内容都将在1.0中。

感谢您的澄清! 这真让人兴奋! 新闻文件也特别具有启发性。

感谢您删除! 我也很高兴它将在1.0中推出! :)

我想知道是否有一种方法可以在自动完成中支持这些新的“动态定义的字段”,例如,通过允许重载fieldnames ?。 对于交互使用而言,这可能非常强大,例如,在处理具有许多列和/或长列名称的DataFrame s(假设将来它们将支持df.column_name )时。

我猜目前REPL(制表扩展),IJulia等是在看类型定义,而不是实例? 但是也许可以更改以进行交互使用。 但是,由于在编码过程中实例不可用,因此在像Juno这样的IDE中可能无法支持。

@oschulz ,您已经可以重载fieldnames


julia> struct Foo; foo; end

julia> fieldnames(Foo)
1-element Array{Symbol,1}:
 :foo

julia> Base.fieldnames(::Type{Foo}) = [:bar, :baz]

julia> fieldnames(Foo)
2-element Array{Symbol,1}:
 :bar
 :baz

REPL看起来是fieldnames

julia> x = Foo(3)
Foo(3)

julia> x.ba<tab>
bar baz

@yurivish权利-但是目前这样做是否“安全”? 我不确定fieldnames还依赖什么。

如果不安全,则应该可以定义complete_fieldnames(x) = fieldnames(x) ,对REPL完成使用complete_fieldnames ,对自定义完成使用重载。

是的,这就是我提出这个问题的原因-万一需要在Base中更改/添加某些内容,以便REPL和朋友以后可以使用它。 鉴于0.7功能冻结...

这就是为什么我很高兴我们调用函数getproperty 。 它有助于弄清楚它与fieldnames 。 干扰fieldnames肯定会引起严重的问题。

我们可能需要一个对应的propertynames来提供有效的属性值。 当然,这可能不是一个定义明确的概念,所以也许我们想要一些更特定于完成的内容。

我觉得这可能需要专家进行更深入的分析和投入(谢谢!)-我们是否应该为此开设一个新刊物?

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

felixrehren picture felixrehren  ·  3评论

omus picture omus  ·  3评论

StefanKarpinski picture StefanKarpinski  ·  3评论

TotalVerb picture TotalVerb  ·  3评论

Keno picture Keno  ·  3评论