请在 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,它通过在一个上应用转换器创建了一个新的迭代器。如果在这个特性上也能保持一致,那就太好了。

为什么要有这样的功能呢?眼前的问题是什么?
`transformer-iterator`更为特定,可能可以避免使用。我引入它是因为最终我在CLJS实现中使用了它。但再想想,`eduction`对我这种情况几乎一样好用,我可以继续使用它。
eductions 推进了迭代并提供了一些线程安全的内容,因此是首选。
0

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

迭代器在很大程度上是非Clojure式的。它们有状态,通常不是并发友好的。当Clojure可以在某些其他调用(特别是在转换器上下文中)中约束它们的用法时,它会依赖它们。

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

> 两者都是关于创建迭代函数的,但在这两个项目中都没有被调用过

亚历克斯,我想澄清,这是不正确的。Meander 通过生成的宏代码确实使用了 `iter` 函数来在不依赖 `iter` 的情况下更容易实现某些语义(更简单的代码生成)在一些情况下。虽然我现在意识到不依赖 `iter` 实现这一功能的机会,但在当时我并没有这样的解决方案,而 `iter` 解决了我的问题。

你知道这些情况存在,这也是一些 Clojure 内部函数还没有“隐藏起来”的原因。你认为威尔克提供的 JVM 实现 `iter` 是“完美无缺”的。然而,你认为它不应该成为公共 API 或者不是一个好主意,但你没有清楚说明你的想法。

> 我认为 `iterator` 不应该被广泛使用或推广

我同意。核心库中有许多函数既不常用也不被推广,但仍然存在并且具有实用价值。换句话说,推广程度或使用范围不能成为排除 `iter` 的理由。

> 通常,迭代器在 Clojure 中并不常见。

这是一个排他性的宿命论立场。就像,这只是你的观点,兄弟。:^)

Clojure 包括了使用“有状态的并且通常不适合并发”的众多机会,正如你所指出的,故意使这些资源在核心库中可用。所以,无论“clojurey”包含什么,从我的角度看,这似乎包括那些“不完全 clojurey”的事物。

现在,让我问一下,一个函数在被视为“应该”之前必须满足哪些任意标准?
退一步来看,决定核心 API/语言中包含什么,是收集人们遇到的问题,从中发现模式,并根据频率、严重程度、解决方案的工作量等因素决定哪些问题是解决的首要任务。

向这个过程提供最佳输入的方法是尽可能清晰地描述你遇到的问题。当你遇到这个问题 Meander 时,你想要迭代器做什么?实际的问题是什么?你考虑了其他什么?为什么它们不够充分?

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

我对迭代器持一般反对态度。迭代器比seqs和reducibles更危险,更具命令式,这并不是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
以下所有内容都用方括号括起来,“我已经derive出了一种新的解决方案,不再依赖于`iter`,但在当时我使用`iter`解决问题时,它确实完成了我想做的事情。”

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

在严格意义上,我最初并不想使用迭代器,但它最终“解决了”我的问题,下面我将详细说明。

>  你面临的是什么实际问题?

这里有几个相互联系的部分。Meander有一个模式替换的概念,你可以将其视为一种与模式匹配相反的过程。匹配将模式应用于对象并返回一组绑定,替换将绑定应用于模式并返回一个对象。替换模式的编译——特别是在包含称为“内存变量”的类型变量的重复模式中——正是这个问题所在。

内存变量是一种与FIFO绑定的变量。在模式“分散”或从FIFO中获取第一个值时,每个内存变量的出现都会使变量与FIFO中剩余元素的联系更新。这一直持续到FIFO为空,被称为“耗尽”。当内存变量出现在重复模式(如Kleene星)中时,重复模式会产生值,直到至少有一个内存变量未被耗尽。当重复模式编译为Clojure代码时,会检查模式中是否包含内存变量。如果是,则会为每个内存变量编译迭代器,每个迭代器都可以通过`hasNext`进行耗尽检查,当然可以使用`next`来获取值。虽然这不是一个理想的解决方案,但我想要的语义却很容易以这种方式实现。

您还考虑了哪些其他事物?

最初,我尝试了一个纯解决方案,但这导致了很多复杂性和困扰。这更多是因为设计质量不高,依赖`iter`,而不是纯方法。

我还尝试了`volatile!`,但在某些情况下产生了错误的结果。

虽然已经有一段时间了,但我想我考虑过使用数组,但由于`iter`已经存在并完成了我的需求,所以我没有去管它。

为什么它们不够用的?

因为它们要么包含了作为维护者我不愿参与的复杂性,要么导致了语义实现的错误。
我认为CLJS有`iter`不是一个好主意。

这正是问题的核心。CLJS一直被当作Clojure的方言来处理,导致了许多类似这种情况的出现。虽然我理解人们可能有偏向于形式化指定语言一部分的看法,但至少对原始语法的最小定义以及默认情况下可以解析的符号能够为未来的实现减轻这些问题。

如果未来语言的设计包括对原始语法的最小定义,以实现平台特定符号的清晰分离,那么CLJ和CLJS或其他实现之间的这些差异问题可能会消失。

仅供参考,我也在说从我自己的经验出发。在过去的5年中,我部分实现了Clojure的解释器和编译器,一个用于Clojure的小步骤符号解释器,以及Clojure的部分评估器。在每种情况下,都因为缺乏清晰的语言定义(包括语法和语义)而未能完成项目。例如,有一些特殊形式,如`case*`,没有文档记载,需要检查编译器来了解其语义。

无论如何,我这里不做任何要求。我只是想分享我的观察。我认为Clojure有一个可能的光明未来,其中不会存在这样的问题。作为一名在近十年内长期编写Clojure代码的专业人员以及业余爱好者,我觉得对语言的某些规范将为其未来铺平道路。
by
我不能代表开发并维护Clojure的核心团队,但考虑到过去十年中,在多个公开论坛上,其他人多次提出Clojure规范的疑问,我怀疑:(a)Clojure的核心团队对编写这样的规范没有兴趣,除了实现在文档字符串中包含的规范和clojure.org上今天已有的许多官方文档(以及仍计划编写的那些),以及(b)如果这是真的,唯一可能被编写的规范将是由不在此团队的人编写的,Clojure的核心团队没有理由去遵循它。
...