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是否可以向clojure.core公开RT/iter方法作为一个函数呢?

我已经“掌握了”关于clojure.lang.RT的本地.m2使用情况,并排除了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风格。它是具有状态的,并且通常不太适用于并发。当可能将它们的使用限制在某个其他调用的内部(尤其是在transducer上下文中)时,Clojure会依赖它们。

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

> 它们都是关于构建一个迭代函数,但在那些项目中它们从没有被调用

Alex,我想澄清这一点是错误的。Meander确实通过生成的宏代码使用`iter`函数来在某种情况下更容易地实现某些语义(更简单的代码生成)。尽管我后来意识到没有`iter`也可以实现这一点,但在当时我没有那个方案,而`iter`解决了我的问题。

您承认这些案例确实存在,这也是为什么一些Clojure内部函数没有被“锁起来”的原因。您承认Wilker提供的`iter`的JVM实现“非常完美”。然而,您认为它不应该成为公共API的一部分,但您没有给出明确的理由来说明这些想法。

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

我同意。在核心库中存在许多很少使用或推广的函数,但它们仍然因其实用性而存在。换句话说,推广能力或使用范围不能是排除`iter`的原因。

> 通用迭代器在总体上很不Clojure。

这是一个排他性的本质主义立场。就像,你的观点,哥们。:^)

Clojure包含了许多机会在核心库中使用“有状态且通常不友好的并发”事物,正如你所言,故意使这些事物可访问。因此,无论“Clojure”包含什么,从我坐的地方来看,这似乎包括那些“不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年中,我曾部分实现了一个针对Ruby 2.X.X的Clojure解释器/编译器、一个小型的Clojure符号解释器,以及Clojure的部分评估器。在每种情况下,由于缺乏对语法和语义的明确语言定义,我都无法完成这些项目。

无论如何,我在这里不做任何要求。我只想分享我的观察。我认为Clojure有可能达到这样一个未来,其中不存在像这样的问题。作为一个将近十年以专业和工作爱好在Clojure中编程的人,我认为对语言的一些规范将铺平通往这一未来之路。
我无法代表开发和维护Clojure的核心团队讲话,但鉴于在过去十年中,许多人多次在各种公开论坛上提出了Clojure规范的问题,我怀疑(a)Clojure的核心团队对撰写此类规范没有兴趣,除了在实现中包含的文档字符串以及clojure.org今天的许多官方文档(以及计划要编写的那些),(b)如果这是真的,唯一可能编写的规范将由团队之外的人编写,并且Clojure核心团队没有任何理由遵循它。
...