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

欢迎!请查阅 关于 页面以了解更多信息。

0投票
语法和读取器

当前,规范不允许包含多个斜杠的关键字和符号,但由读取器允许。
这个微小的补丁使得读取器也无法读取它们。

user=> :foo/bar/baz :foo/bar/baz

user=> :foo/bar/baz RuntimeException Invalid token: :foo/bar/baz clojure.lang.Util.runtimeException (Util.java:221)

9 答案

0投票
by

评论者:jafingerhut

也许与 CLJ-1527 有重叠?

0投票
by

评论者:zamaterian

请注意,具有多个斜杠的关键字在不同的 Clojure 版本(1.5 和 1.6)中有不同的哈希码。

当在 Clojure 1.6 下使用兼容 Clojure 1.5 的 datomic 版本,并且模式中有一个或多个带多个斜杠的键时,这会引发问题。

0投票
by
评论者:zcaudate

请重新考虑此“修复”,以下是一些原因

- (keyword "foo/bar/baz")也需要修复以保持一致性
- 它将破坏我的代码的用户
   - http://docs.caudate.me/adi/adi-guide.html
   - http://docs.caudate.me/adi/adi-walkthrough.html#schoolyard

请在下面的讨论中查看此主题


dm3 [5:04 PM]
是否存在原因解释为什么 `(read-string "a/b/c")` 是可接受的,而 `(clojure.tools.reader/read-string "a/b/c")` 会因为 `Invalid token` 而失败?

hiredman [5:04 PM]
有一个修复 read-string 的票据

dm3 [5:05 PM]
所以正确的表现应该是失败吗?

hiredman [5:05 PM]
http://dev.clojure.org/jira/browse/CLJ-1530

dm3 [5:06 PM]
谢谢,看起来是一个破坏性的变化 :simple_smile

hiredman [5:07 PM]
文档已经禁止了类似a/b/c这样的符号一段时间了,而这些未指定符号的读取行为似乎在某个时刻发生了变化

lucas [5:09 PM]
加入了 #clojure

zcaudate [5:16 PM]
@hiredman:真惨。我真的对这一点很生气,因为我已经使用`:a/b/c`关键字一段时间了……甚至还写了一个整个的库来处理这个问题 http://docs.caudate.me/hara/hara-string.html#api---path (编辑中)

[5:16]
现在他们要将它移除

[5:18]
我认为应该首先移除`::foo/baz`这样的关键字

[5:20]
 ```user=> (require '[clojure.walk :as walk])
nil
user=> ::walk/hello
:clojure.walk/hello
```

[5:20]
这造成了很多问题

[5:20]
尤其是与代码分析器一起使用时

[5:22]
https://github.com/jonase/kibit/issues/14
 
GitHub
Kibit在命名空间关键字出现问题时断开 · Issue #14 · jonase/kibit · GitHub
如果代码中包含了有别名的问题命名空间关键字,Kibit将抛出无效令牌异常。以下代码演示了这个问题 - ;;; foo.clj (ns foo) ;;; bar.clj (ns ...)

[5:25]
@dm3 如果确实存在问题,你可以修补它

[5:25]
https://github.com/helpshift/hydrox/blob/master/lein/src/leiningen/hydrox/setup.clj
 
GitHub
helpshift/hydrox
hydrox - 深入了解你的代码

dm3 [5:31 PM]
是的,不幸的是,我们还需要修补 cljs.tools.reader :confused

[5:31]
我想我会就此解决这个问题

bronsa [5:36 PM]
@zcaudate:`:::foo/bar`样式的关键字是按设计加入的,不会移除,而`:foo/bar/baz`从一开始就按规范无效且是未定义的行为(编辑中)

[5:37]
@dm3:改变未定义的行为算是破坏性的改变吗? :simple_smile

dm3 [5:37 PM]
破坏性的理解为破坏人的代码 :simple_smile

[5:37]
例如 zcaudate

bronsa [5:38 PM]
如果使用了无效的Clojure,那这段代码本身就已经是破烂的。这只是一个意外

dm3 [5:39 PM]
我从实用的角度来考虑问题。从理论上讲,你是正确的 :simple_smile

[5:40]
我也没有做评判

bronsa [5:40 PM]
从实用的角度看,`:foo/bar/baz`是一个即将发生的错误。`(namespace :foo/bar/baz)`返回什么?

dm3 [5:40 PM]
目前它返回的内容

[5:41]
我是说它有点由实现定义

sveri [5:41 PM]
@dm3 @bronsa 我不同意你的理论上的正确性。一旦足够多的人适应了有缺陷的代码,只要双方足够长时间的接受,它就会像某种类似普通法的东西一样被接受。

bronsa [5:42 PM]
比如 `(keyword "foo/bar" "baz")`和`(keyword "foo" "bar/baz")` 的`namespace`呢?

dm3 [5:43 PM]
我同意当前实现的语义是混乱的

[5:43]
但我想要说明的是,这仍然是一个破坏性的变化

[5:43]
并不是说这是一个“不好的”变化

[5:43]
这是一个判断

bronsa [5:43 PM]
@sveri: 我会同意你的说法,只要有我们接受为规定的未定义行为不会导致无法修复的语义。

[5:44]
这就是例如,使数字开头的符号非法时的修补被打回的原因

[5:45]
它破坏了现有的代码,它没有导致奇怪的语义,因此被撤销。与`:foo/bar/baz`的情况不一样

[5:45]
@dm3:你可以说修正任何错误都是破坏性的变化——人们可能会依赖于那个错误。

dm3 [5:46 PM]
是的,我想重要的是错误的明显程度以及有多少人依赖于它

bronsa [5:46 PM]
如果文档明确说明“你可以在一个符号中使用一个`/`”,那么如果你使用了多个你就在写无效的Clojure,你应该期望它可能会破坏(编辑中)

dm3 [5:47 PM]
我真的还没想过在Clojure中使用多个斜杠的问题,也不曾注意过文档,这已经三年来一直在使用Clojure了

[5:47]
我今天的初步想法是这是允许的

[5:47]
并且命名空间将是第一个斜线之前的第一个分段(已编辑)

bronsa [5:51 PM]
嗯,这并不很合理。在Clojure中,`/` 表示“命名空间分隔符”。如果我看到 `FOO/BAR`,无论 `FOO` 和 `BAR` 是什么,我都会知道 `FOO` 是命名空间,`BAR` 是名称。如果您想像 @zcaudate 的库那样使用关键词表示路径,您应该在您的关键词中使用没有特殊意义的分隔符,比如 `.`(例如,`:foo/bar/baz` -> `:foo.bar.baz` 或 `:foo/bar.baz`)(已编辑)

dm3 [5:51 PM]
我不想争论语义。只是分享一个观点

bronsa [5:52 PM]
我的观点是,务实观点(尤其是在它们与现有文档相悖时)只有在它们所隐含的语义清晰且无歧义时才应予以考虑

sveri [5:53 PM]
@bronsa: 很好的解释,谢谢 :simple_smile

dm3 [5:53 PM]
yep :simple_smile

[5:54]
我同意这个观点,因为最后你们必须做出决定


zcaudate [9:07 PM]
@bronsa: 书面形式的沟通有时会让人觉得事情比实际更严重

[9:08]
坦白说…我知道从1.6版本开始EDN解析器就会破坏我的代码了

[9:09]
可能是我没有早点沟通的错吧,不过, 我们都必须顺应潮流

bronsa [9:09 PM]
@zcaudate: 没关系的,我仅仅是因为你提到了这个库才用它作例子

zcaudate [9:10 PM]
话虽如此,你可以想象我有多么失望,因为我已经根据关键词 `:foo/bar/baz` 功能(现在是错误)设计了整个查询语义(已编辑)

[9:11]
http://docs.caudate.me/adi/adi-walkthrough.html#querying

[9:11]
你注意到了我并没有在我的文档中使用 `(adi/select ds {:student/classes/teacher/name "Mr. Blair"})`

[9:12]
因为在测试中它开始崩溃

jstew [9:12 PM]
@zcaudate:你提供的质量内容太多了,我都在想你是否还会睡觉!
1  

bronsa [9:12 PM]
@zcaudate:幸运的是,修复应该是简单的:简单微笑:只需将 `/` 替换为 `.`

zcaudate [9:12 PM]
不行!

[9:13]
问题是… datomic 有 `account.type/user` 这样的东西

[9:13]
所以我就不得不进行 clojure 的翻转会: `account.type$user`

bronsa [9:13 PM]
(顺便说一句,这是一份高质量的文档,做得很好)

zcaudate [9:14 PM]
@bronsa: hahaha 谢谢… 所以也许你可以把修复推送到 1.10

[9:14]
这样我就能有几个更多的时间了

[9:15]
好像不是什么大问题… 但我认为 `/` 调用的路径结构跟映射的嵌套之间有相似之处

[9:16]
因此,有一个对应:`{:student {:classes {:teacher {:name '(?fulltext "Blair")}}`

[9:17]
`{:student/classes/teacher/name "Mr. Blair"}`

[9:17]
`我以为这在我的观点中更漂亮

bronsa [9:17 PM]
@zcaudate: 不好意思,如果这不是很清楚,但实际上,我并不控制 Clojure 或它包含的何时或是什么,我只是个贡献者:简单微笑:所以有可能 clojure/core 团队会做出不同的决定,实际上拒绝那个票据(如果是那样,我会非常失望的!)如果发生这样的事,我显然将更改 `tools.reader` 允许它如此, (已编辑)

zcaudate [9:18 PM]
@bronsa: 该死。

[9:19]
嗯…也许你可以突出这个事实

[9:19]
如果修复被实施,也需要修复 `(keyword "foo/bar/baz")`(已编辑)

bronsa [9:20 PM]
我不认为那会完成。验证 `keyword/symbol` 等输入已经被多次请求/讨论,并且出于性能原因被反复拒绝

zcaudate [9:20 PM]
所以这是个一致性的问题。

bronsa [9:20 PM]
(虽然我并不认同这个决定,但看来Rich在这个问题上不会改变主意)

[9:21]
@zcaudate:符号/关键字在运行时可以是什么,与有效的读取时符号/关键字是不同的

zcaudate [晚上9:21]
而且,这也意味着我可以设置一个读取宏#k foo/bar/baz并获得相同的效果

[9:22]
虽然看起来非常丑陋,但我相信它将会起作用

bronsa [晚上9:23]
但“namespace”和“name”如何使用的不确定性仍然存在,所以我也不知道

[9:23]
@zcaudate:无论是否通过http://dev.clojure.org/jira/browse/CLJ-1530,如果它被接受,那么 neither :foo/bar/baz 也不再是有效的了

[9:24]
@zcaudate:顺便说一下,如果你对此强烈反对,我建议你在那里的一项任务中记录你的问题

新消息
[9:25]
我 **怀疑** 答案将是“你应该使用在Clojure中不具有特殊意义的分隔符”,但我可能完全错了(我发现核心团队并不经常同意我的观点 :)),特别是如果你指出你的库将会损坏。(已编辑)

zcaudate [晚上9:30]
@bronsa:谢谢提醒。我会留下一个评论,并祈求bdfl的保佑。
0投票

评论者:alexmiller

Chris -

  • (关键字 "foo/bar/baz")仍然可以正常使用。可以为任何字符串创建程序性关键字——这故意比读取器支持作为代码中的字面量更广泛,并且这是一个广泛使用的特性。在未来某个时间点,可能存在一种转义机制,可以用于具有规范之外字符的符号或关键字,使读取器能够读取它们,但这超出了这个范围。
  • 您的API正在使用根据https://clojure.org/reference/reader不合法的关键字,您不应该期望它们可以工作。我认为您应该更改您的库。
0投票

评论者:zcaudate

Alex

  • 我不会一定称它为非法,因为edn.reader中当前的行为是在1.6版本中添加的,没有警告。
  • 此外,如果允许存在 (keyword "foo/bar/baz"),那么@bronsa所强调的不可确定的命名空间/名称问题仍然存在。我会主张一致性,如果:foo/bar/baz在读取器中是非法的,那么它应该在各个地方都是非法的。
  • 我的库应该没什么问题...但库的用户可能需要更改他们的查询。
0投票

评论者:alexmiller

阅读页面明确指出 "'/' 具有特殊含义,它可以在符号的中间 一次 使用,以区分命名空间和名称”并且关键字是“类似于符号”。这一表述自从我能在互联网档案馆找到的最古老版本(2008年7月)以来就一直出现在阅读页面中。edn(尽管它与Clojure有相似之处)是另一回事,并且与此无关。

可以使用 {{keyword}} 的 2 参数形式而不会有歧义:{{(keyword nil "foo/bar/baz")}}。1 参数形式将根据第一个遇到的 '/' 分割(在这个例子中是 "foo" 和 "bar/baz")。我认为没有必要对这个进行更改。

0投票
by

评论者:zcaudate

我不同意在关键字中 《/》 有特殊性的说法,特别是如果它被允许作为一个字符串。

这导致了一个输出的歧义问题

user=> (keyword "foo" "bar/baz")
:foo/bar/baz

user=> (keyword nil "foo/bar/baz")
:foo/bar/baz

除非输出显示为 :foo//bar/baz,否则不明确命名空间在哪里,如果输出不能作为数据读回,那么“代码是数据”的概念就会减弱。

还涉及到符号的问题
user=> (symbol "foo/bar/baz")
foo/bar/baz

嗯...至少所有列出的例子都是一致的“非法”

无论如何,即使文档中没有明确说明,:foo/bar/baz 自 Clojure 之初就存在了,并且在我看来,它看起来更像字符串而不是符号。讽刺的是,我确信我通过阅读我开始做 adi 时的 datomic 文档得到了使用多个斜杠在关键字中的想法。

最终,这不是我的决定权,我确实很重视 Clojure 团队对于语言发展的指导。然而,我确实希望我的意见能被认可并加以考虑。

0投票
by

评论者:zcaudate

我将引用 Rich 在 5:10 的 https://www.youtube.com/watch?v=P76Vbsk_3J0

"你考虑的许多问题(在 Lisp 中)是特性...到头来..."

0投票
by
参考:https://clojure.atlassian.net/browse/CLJ-1530(由 bronsa 报告)
...