2024年Clojure状态调查!中分享您的想法。

欢迎!请参阅关于页面以获取更多有关如何使用本网站的信息。

+3
Clojure
修改

我看到几段代码使用clojure.lang.RT/iter来从Clojure集合中检索迭代器。

(iterator-seq (clojure.lang.RT/iter [1 2 3]))

例如:

https://github.com/noprompt/meander/commit/d2310daaa485afb4e15ceda72aa57f97ea90f284

还有

https://github.com/wilkerlucio/cljc-misc/blob/bb3c8016cace18db5caa5fe0aa5df7a507935f8d/src/main/com/wsscode/misc/coll.cljc#L262

为了确保babashka与Clojure的兼容性,我想知道是否应该公开clojure.lang.RT。我怕不一定应该这样做,因为clojure.lang.RT可能是Clojure的实现细节。

相反,可能Clojure可以将RT/iter方法公开为clojure.core函数?

我已经为本地的.m2“抓取”了clojure.lang.RT的使用情况,并排除了clojure本身

[clojure.lang.RT/loadClassForName 15]
[clojure.lang.RT/iter 5]
[clojure.lang.RT/loadLibrary 4]
[clojure.lang.RT/assoc 4]
[clojure.lang.RT/classForName 3]
[clojure.lang.RT/load 2]

请注意,ClojureScript有一个iter函数

$ plk
ClojureScript 1.10.597
cljs.user=> (iter [1 2 3])
#object[cljs.core.RangedIterator]
cljs.user=>

$ clj
Clojure 1.10.1
user=> (iter [1 2 3])
Syntax error compiling at (REPL:1:1).
Unable to resolve symbol: iter in this context

2 个答案

0

我同意@borkdude的看法,如果能在这门语言的一个核心函数中实现这个常见的功能,将会使代码更加便携。

虽然不是很重要,但在CLJS中也有一个transformer-iterator,它通过应用转换器创建一个新的迭代器。如果能在这个功能上也实现互操作性那就更好了。

by
为什么要有 *原因*?我们目前遇到了什么问题?
by
`transformer-iterator`比较特殊,可以避免。我之所以引入它,是因为我在我的CLJS实现中使用了它。但是考虑到这个事情,`eduction`在我的情况下几乎一样有效,我可以继续使用它。
by
封装迭代的同时提供线程安全的数据交换,这是首选的做法。
0
by

`RT`应该被视为内部实现,不应直接调用。

迭代器一般不符合Clojure风格。它们是具有状态的,并且通常不友好于并发。Clojure可以在某些调用(特别是转换器上下文)中约束它们的使用时,会在内部使用它们。

使用`(iterator-seq (clojure.lang.RT/iter [1 2 3]))`看起来不好,而`(seq [1 2 3])`在多个方面看起来更好。如果有一些用例让人们想要创建迭代器,我对这个问题很感兴趣,但是在这里我没有看到它。

作者
> 它们都是关于构建一个迭代器函数,但这些函数在那些项目中从未被调用过

亚历克斯,我想澄清一下,这是不正确的。Meander确实通过生成的宏代码使用`iter`函数,在某些情况下可以更轻松地实现一些语义(更简单的代码生成)。尽管后来我意识到可以不使用`iter`来实现这一点,但在当时我并没有这个解决方案,而`iter`解决了我的问题。

您承认这些情况存在,这就是为什么一些Clojure内部函数没有被“束之高阁”的原因。您承认威尔克提供的`iter`的JVM实现“完美无缺”。然而,您不认为它应该成为公共API的一部分,但您没有给出明确的理由说明您的这种想法。

> 我认为`iterator`不是一个应该广泛使用或推广的函数

我同意。在核心库中存在许多函数,它们不常用或不被推广,但它们确实存在,因为它们具有实用性。换句话说,推广程度或应用范围的广度不能成为排除`iter`的原因。

> 通常情况下,迭代器在Clojure中是很不典型的。

这是一个排他性的、本质主义的立场。就像是,这仅仅是你的个人看法。:^)

Clojure在核心库中提供了许多机会来使用“有状态且通常不友好于并发”的东西,并且正如你所指出的,故意使这些功能可访问。所以无论“clojurey”包括什么,从我这边看,它似乎是包括那些“不clojure”的东西。

现在,让我问你,一个函数在“应该”被考虑之前必须满足什么样的任意标准?
作者
退后一步来看,确定哪些内容应该纳入核心API / 语言的决策,是一个收集人们遇到的问题,筛选所有这些问题以寻找模式,并根据频率、严重程度、解决方案的程度等因素,确定哪些问题是需要优先解决的。

向此过程提供最佳输入的方式是尽可能清楚地描述你所遇到的问题。当你在Meander中遇到问题时,为什么你想使用迭代器?实际问题是什么?你还考虑了哪些其他因素?为什么它们不够充分?

Wilker自己实现其自定义集合类型的迭代器方法,并为后备集合实现描述是一个有用的例子。在核心库中添加`iter`是一个可行的解决方案。另一个选择是使用现有的Java API编写自己的函数,就像他那样做。另一个可能是将构建自定义集合类型的通用需求封装到Clojure内或库中。我注意到,这里的需求范围并不常见(大多数人不会创建自定义集合类型),他没有使用`iter`函数,而是使用可用的Clojure/Java API解决了他的问题,我还没有其他遇到相同问题的例子(但也许他们存在)。

我总体上是反对迭代器的。迭代器在某种程度上比序列和可归约性更危险和命令式,这并不是Clojure API没有包含其意外之举。这不仅仅是我的观点,这是Rich的观点,我在传达。他在很多地方都写过关于这一点。

* https://clojure.org/about/functional_programming#_extensible_abstractions
* https://clojure.org/reference/sequences
* https://clojure.org/news/2012/05/08/reducers
* https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/ClojureIntroForLispProgrammers.md

此外,我们关于这一点在设计实施transducers期间进行了广泛讨论,transducers在实现上严重依赖于迭代器,但将它们的使用封装起来以确保安全。

即便如此,我认为这并不离谱,但迄今为止,我认为需求水平并不足够高,以至于可以克服那种偏见。如果您想改变我的看法,请给我带来问题。
by
以下内容均用括号括起来,因为我后来找到了一种更新的解决方案,不依赖于`iter`,但在当时我为了解决我的问题时使用了`iter`。

> 当你在Meander中遇到这个问题时,为什么你想使用迭代器?

我在此是有些挑剔,我最初并不想使用迭代器,但它最终“解决”了我的问题,具体细节如下。

>  你遇到的实际问题是什么?

这里有几个相互关联的部分。Meander有一个模式替换的概念,可以把它看作是模式匹配的对偶关系。匹配将一个模式应用于一个对象,并返回一组绑定,替换将绑定应用于一个模式,并返回一个对象。在编译替换模式时——特别是包含称为“记忆变量”的类型的变量重复模式——这个问题就出现了。

内存变量是一种绑定到FIFO(先进先出队列)的变量。每当内存变量在模式中出现“分散”或从FIFO中取出第一个值时,它都会更新变量与FIFO中剩余元素的绑定。这个过程会持续到FIFO为空,这时它被称为“耗尽”。当内存变量出现在重复模式中(例如Kleene星号)时,重复模式会产生值,只要至少有一个内存变量没有被耗尽。当重复模式被编译为Clojure代码时,会检查模式中是否包含内存变量。如果是这样的话,它就会为每个内存变量编译迭代器,每个迭代器都可以通过`hasNext`来测试是否耗尽,当然值也可以通过`next`来分散。虽然这不是最佳解决方案,但我想要的语义以这种方式实现起来很简单。

你还考虑了哪些其他因素?

最初,我尝试了一个纯解决方案,但这导致了更多复杂性和头痛。这更多是关于设计的质量问题,该设计依赖于`iter`,而不是纯方法。

我还尝试了`volatile!`,但这在某些情况下产生了不正确的结果。

已经有一段时间了,但我认为我曾考虑过使用数组,但是`iter`已经存在,并且它已经完成了我想让它完成的工作,所以我并没有再去管它。

为什么它们不充分?

因为要么存在我作为维护者不想处理的复杂性,要么因为它们导致了语义实现的错误。
> 我认为CLJS有`iter`不是一个好主意。

这是问题的核心。CLJS在过去一直被视为Clojure的一种方言,由此导致了许多类似这种情况。虽然我理解存在对语言部分形式化的偏见,但至少对原语和空命名空间中默认可解析的符号有一些最小定义可以用来减轻未来实现的问题。我个人认为`ns`宏是部分问题所在,因为它无处不在(`in-ns`更原始但很少使用;CLJS需要它),而且它指向核心,其内容是任意基于实现认为应该存在的内容;像这个问题这样的问题是自然的结果。

如果语言的未来设计包括原语的最小定义,以便能够轻易地实现平台特定符号的干净分离,那么CLJ和CLJS或其他实现之间差异的问题可能会消失。

根据我的经验,我在过去5年里只部分实现了Clojure的解释器/编译器为Ruby 2.X.X版本,一个小的符号解释器和Clojure的局部评价器。在每种情况下,由于缺乏明确的语法和语义定义,我都无法完成项目。

无论如何,我在这里没有任何要求。我只想分享一下我的观察。我认为Clojure有一个可能的未来,在那个未来中,这样的问题不会存在。作为一个近十年来不仅作为专业编程人士而且作为爱好编程Clojure的人,我认为对语言的某些规范将铺平通往那个未来的道路。
我不能代表开发和维护Clojure的核心团队发言,但鉴于过去十年内,在各个公开论坛上,其他人多次提出了关于Clojure规范的问题,我怀疑(a)Clojure核心团队对写一个这样的规范没有兴趣,除了实现中包含的文档字符串和在clojure.org上今天(以及计划继续编写的那些)的许多页面正式文档之外;(b)如果这是真的,那么可能永远只会有人不在该团队中编写的规范,而Clojure核心团队也不会有兴趣遵循它。
...