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
eductions 封装了迭代操作,并且可以让您在进程间传递一些安全的数据,所以绝对更受欢迎。
0
by

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

迭代器总体上并不符合 Clojure 的精神。它们是状态化的,通常不友好于并发。Clojure 在能够限制它们使用的某些其他调用(尤其是转换器上下文)中内部依赖它们。

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

by
> 两者都是关于构建一个迭代器函数,这两个函数在该项目中从未被调用过。

亚历克斯,我想澄清这一点是错误的。Meander确实通过生成的宏代码使用了`iter`函数来在一些情况下更容易地实现一些语义(更简单的代码生成)。虽然我后来意识到不需要使用`iter`就能实现这一点,但是当时我没有这种解决方案,而`iter`解决了我的问题。

你承认这些情况存在,这也是一些Clojure内部组件没有被“锁起来”的原因。你承认威尔克提供的`iter`的JVM实现“非常好”。但是你并不认为它应该是公共API的一部分,或者认为这不是一个好主意,但你没有给出明确的理由。

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

我同意。核心库中存在许多不广泛使用且未被推广的函数,但它们仍然存在,因为它们有其自身的用途。换句话说,推广度或使用广度不能成为排除`iter`的原因。

> 总体来说,迭代器在Clojure中并不常见。

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

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

现在,让我问一个问题,一个函数在被认为是应该使用之前必须满足哪些任意标准?
by
退一步说,决定什么进入核心API / 语言是收集人们遇到的问题,并筛选出所有这些问题以找到模式,并基于频率、严重性、解决方案的难度等因素,决定哪些问题是需要优先解决的。

向该流程提供反馈的最好方式是尽可能清晰地描述你所遇到的问题。当你在Meander中遇到这个问题时,你为什么想要一个迭代器?实际的问题是什么?你还考虑了哪些其他方法?为什么它们不够充分?

威尔克的描述表明,他需要在自己的集合类型中实现迭代器方法,并使用后端集合,这是一个有用的例子。将`iter`添加到核心解决方案之一。另一种可能是像他那样使用现有的Java API编写自己的函数。另一种可能是将构建自定义集合类型的一般需求封装在Clojure内部或库中。等。然而,我注意到这里的需求范围并不常见(大多数人不会创建自定义集合类型),他没有使用`iter`函数,通过可用的Clojure/Java API解决了他的问题,而且我没有其他与类似问题的人的例子(也许他们存在)。

我的总体偏见是反对迭代器。迭代器在某种程度上比seqs和reducbles更危险和命令式,这并不是Clojure API没有迭代器的原因。这不仅是我的观点,也是里奇的观点,我正在传达。他在很多地方都有过这样的写作和谈话。

* 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

此外,在设计和实现转换器期间,我们对此进行了广泛的讨论,转换器在实现中大量使用迭代器,但将其封装以提高安全性。

即使如此,我认为这并不是不可能的,但在我看来,需求的程度似乎不足以克服这种偏见。如果你想改变我的看法,带来问题。
by
以下是我在“我已经找到了一个不依赖`iter`的新解决方案”中的一些内容,在我遇到`iter`来解决我的问题时,它做了我需要的事情。

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

严格来说,我开始时并不需要迭代器,但最终它“解决”了我的问题,以下我将详细说明。

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

这里有几个相互关联的部分。Meander有一个模式替换的概念,你可以把它看作是模式匹配的对立面。匹配将模式应用于对象并返回一组绑定,替换将绑定应用于模式并返回一个对象。特别是当出现重复模式并且包含称为“内存变量”的类型变量时,这个过程会引发问题。

内存变量是与FIFO(先进先出队列)绑定在一起的变量。内存变量在模式中出现时,每次都会“分散”或从FIFO中取出第一个值,并更新变量与FIFO中剩余元素之间的绑定。这会持续到FIFO为空,这时我们说它是“耗尽”的。当内存变量出现在重复模式中(比如Kleene星号)时,重复模式在至少一个内存变量未被耗尽的情况下产生值。当重复模式被编译成Clojure代码时,会检查模式中是否包含内存变量。如果包含,则对每个内存变量编译迭代器,每个迭代器都可以通过`hasNext`进行检查是否耗尽,同时可以通过`next`发散值。虽然这不是一个理想的解决方案,但我想要的语义实现起来很简单。

你还考虑了其他什么问题?

最初,我尝试了纯解决方案,但这导致了很多复杂性和头疼的事情。这更多与设计质量不佳有关,它依赖于`iter`,而与纯方法关系不大。

我也尝试了`volatile!`,但有时会产生错误的结果。

虽然已有一段时间了,但我认为也考虑过使用数组,但由于`iter`已存在并且满足了我的需求,我没有进一步考虑。

为什么这些方法不够充分?

因为要么是由于复杂性过多,我不愿意处理,要么是因为它们造成了语义实现的错误。
by
我不认为CLJS having `iter`是一个好主意。

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

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

从我过去5年的经验来看,我也部分实现了Clojure的解释器和编译器(用于Ruby 2.X.X)、一个小的Clojure符号解释器和一个Clojure的部分评估器。在这些情况下,我未能完成项目,因为缺乏对语言语法和语义的明确定义。例如,有一些特殊形式如`case*`没有文档,需要检查编译器来确定它们的语义。

无论如何,这里不提出任何要求。我只是想分享我的观察。我认为Clojure有可能在未来解决类似的问题。作为一名近十年来专业和业余编程Clojure的人,我认为对语言的一些规范将铺平这一道路。
by
我无法代表开发和维护Clojure的核心团队发言,但是鉴于过去十年中,别人多次在不同公共论坛上提出Clojure的规范问题,我怀疑(a)Clojure核心团队除了今天在clojure.org上包含在实现中的文档字符串和许多官方文档页面(以及已计划书写的内容),对撰写这样的规范不感兴趣;(b)如果这是真的,唯一可能被写出的规范将由团队外的人创建,而Clojure核心团队没有理由遵循它。
...