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

欢迎!请参阅 关于 页面了解该工作原理的更多信息。

+8
Syntax and reader
编辑

问题

由于必须在 Clojure 代码中包含一些 JavaScript、XML 和 HTML,因此在这里和那里包含引号会导致非常恼火并且容易出错。在脚本编写和运行 shell 命令时,您可能会遇到复杂的转义场景。

解决方案

添加一个字符串字面量,它可以适应包含任何类型的字符串,而无需转义。

建议

文本块

某些其他语言提供了一种称为文本块的东西,您可以使用三个或更多引号来编写字符串,此时所有字符均受允许。

(println """
         This " is allowed,
         and no need to escape it.
         """

文本块通常附带其他功能,例如,第一个和最后一个换行符不属于字符串。三重引号在源代码中的位置界定引号中行的开始。因此上述代码会打印

This " is allowed,
and no need to escape it.

而不是

         This " is allowed,
         and no need to escape it.

尽管文本块在视觉上很整洁,因为它们在源代码中具有很好的对齐。但是,它们依赖于空白,而 Clojure 之前是一个不依赖于空白的语言,这意味着空白不重要。我认为最好保持这种方式。因此,下面是下一两个建议。

原始字符串

有时,不带“块”功能的文本块被称为原始字符串字面量

(println """This " is allowed,
and no need to escape it.
Also support multi-line, but
not the "block" style of text blocks.""")

因此

(println """
         This " is allowed,
         and no need to escape it.
         """

打印

         This " is allowed,
         and no need to escape it.

与文本块不同。

如果需要三重引号,只需将分隔符改为四重引号即可

""""This """ is now allowed as well.""""

原始字符串的问题是,如果您使用双引号作为分隔符

""This is a raw " string!""

但想要在开始或结束处放置单引号

"""{{hello}}"""

我想得到这样的字符串: "{{hello}}",而不是 {{hello}},但是原始字符串无法区分这两个,因为现在它认为这是一个三重引号分隔符。

一种解决方案是允许在开始或结束处使用转义引号

""\"{{hello}}\"""

但不在中间

""\"{{he\llo}}\"""

这是字符串: "{{he\llo}}"

因此,转义字符\可以在除开头外任何位置出现,如果其后跟着引号,并且如果其后跟着引号则可以在末尾出现。

我仍然觉得这不是最佳方案。规则太多,仍有需要转义的情况存在。

未转义的字符串(我最喜欢的)

这里的想法是允许任何字符串用作分隔符。因此,给定我们要在Clojure代码内部嵌套的任何可能的字符串,我们总能找到一个包含在这个字符串之外的字符串用作分隔符。

假设我们添加了文本读取宏#text,它期望以下形式是一个普通字符串,该字符串告知它接下来要读取的形式的分隔符。

(println #text "|" |"{{hello}}"|)

将打印:

"{{hello}}"

将#text的第一个参数告诉它接下来要读取的原始字符串的分隔符应是什么。这样,你绝对不需要在原始字符串内部使用转义序列。对于任何给定的字符串,你都可以找到一个不包含它的分隔符字符串来正确处理它。

我想出的一种疯狂的想法,只是随便说说,就是如果你使用足够随机的字符串作为分隔符,这可能是一种防止形式注入的奇怪方法。

(println #text "xIBgdSl4TCCOIdqdMu9G" xIBgdSl4TCCOIdqdMu9G
Can't nobody guess the delimiter to escape the string context :p
xIBgdSl4TCCOIdqdMu9G)

感谢

3 个答案

+1
by

这在Clojure的生命周期内已被多次提出并拒绝,我认为这里没有新的论据。

by
嗯,我首先尝试搜索有关它的内容,但没有找到任何东西。我受到关于Java JEPS的启发:[https://openjdk.java.net/jeps/355](https://openjdk.java.net/jeps/355) 和 [https://openjdk.java.net/jeps/326](https://openjdk.java.net/jeps/326)
如果您搜索“原始字符串”、“字符串字面量”、“heredoc”、“多行注释”等,在Clojure和Clojure-dev谷歌群组邮件列表上有很多讨论。

旧设计页面: https://archive.clojure.org/design-wiki/display/design/Alternate%2Bstring%2Bquote%2Bsyntaxes.html

我认为我应该在“拒绝”这个词上做点改动,用“不感兴趣”来替代;我认为Rich通常认为这类事物(尤其是工具)复杂且复杂,但相对的收益较低。
不采用三引号方法的一个论点是这已经是在Clojure中合法的方法

user=> (println """
这已经是在Clojure中有效的
""")
 
这已经是在Clojure中有效的
 
nil
user=>

它并不是你所期望的含义,但在今天这是有效的,即使我认为任何真正的代码都不可能是这样使用的)
好吧,我承认我只在谷歌上搜索了,但没有专门在邮件列表上搜索 :p。

我没有考虑文档字符串用例,但这也很好。

关于这可能会引入的工具复杂性,这是一个很好的论点。解析heredoc或类似的内容可能会更困难。

如果你看看Ruby、Perl和Python,你会发现似乎没有人能够就正确的做法达成一致,这三种语言都支持多种方法。

说到不值得为工具付出努力。事实上,工具也可以解决这个问题。虽然不是针对文档字符串用例,但对我的片段用例来说也是如此。我知道在IntelliJ中有一个模式,它可以为你打开另一个缓冲区来自由输入,并且会自动在字符串内部Java转义你输入的内容。也许我还会为Emacs做类似的事情。
@sean 抓得真准。无论是什么解决方案,我个人倾向于使用读取标签。比如 #s """ """ 或类似的。这样就能解决这个问题。
只是想提一下,Java 15 也引入了这一点(我知道这是一个仅限于编译器的特性,所以 Clojure 的实现与此无关),但也许解决方案空间增长了一点点。

只是想提一下,“原始字符串”,即需要较少引号处理的字符串,再加上读取宏,允许做很多事情,现在已经足够麻烦,以至于没有人愿意去麻烦。
+1

从现状看,将 XML 和 HTML 作为文本处理一直让非 Clojurists 落入困境,包括输出格式错误、注入、笨拙的转换,以及非常难以评估正确性的代码!

Clojure 部分得益于其数据(而非字符串)基础上的惯例,因此享有良好声誉。 “Hiccup”和“clojure.xml”惯例已证实在 HTML 和 XML 上非常有效,并且在(我的非量化的经验中)运行时效率惊人地低,这证明了避免在代码中嵌入大块数据的好处。如果您确实有数据,您可以尽早使用 Enlive 等工具解析数据,并将处理过程作为数据结构完成。

嗯,也许用 JavaScript 是一种拉伸。最好是直接从一个资源中获取?如果在 ClojureScript 中编写,您可以使用宏在编译时从与 cljs 文件相同的类路径中获取资源。

总的来说,我认为 Clojure 在不嵌入和修改代码中的 HTML 和 XML 文字面值方面是非常明智的(我们也是幸运的)。起初这可能会感觉像是一个缺失的功能,但是它的缺失确实让 Clojure 代码的可靠性得到了提升。

非常好的观点。至于 HTML 和 XML,我同意。我的驱动来自于做服务器端渲染,并且需要在 Hiccup 中嵌入一些 JavaScript。

我实际上曾经想过 CLJS 是否可以以这种方式嵌入,但我不认为它能。尽管如此,如果它能,那将会非常棒。
+1

重新提出

嗨,

我认为也应该支持在 clojure 中添加此功能。我认为一个很好的用途案例是支持类似 styled components(https://styled-components.npmjs.net.cn/)的功能。

const Button = styled.a`
  /* This renders the buttons above... Edit me! */
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;

  /* The GitHub button is a primary button
   * edit this to target it specifically! */
  ${props => props.primary && css`
    background: white;
    color: palevioletred;
  `}
`

上述代码的主要优点是大多数情况下都是纯 CSS - 无需转换为 clojure。

在 clojure 中添加原始字符串/块文本是否有问题?是的。
让我们就此进行讨论,看看我们能走多远。

是否有办法以类似于现在开发 Java JEP 的方式尝试 clojure 的功能 - 有预览版本以供发现和改进,直到最终确定并添加到核心中?https://openjdk.java.net/jeps/326

我对 Clojure 很新,对语言及其所有历史和传统不太熟悉。我喜欢挑战传统,因为世界在变化。使我们的做事方式变得合理的理由可能现在不再成立。

Clojure 没有类似于 Java JEP 的过程。如果 Rich Hickey 没有兴趣添加一个功能,那么他不会将其作为 Clojure 官方版本的一部分来整合。

世界上任何人都可以为 Clojure 源代码创建自己的局部更改并使用它,如果他们愿意的话。我听说有些组织运行针对自身使用的Clojure补丁版本。

在您的 Clojure 本地副本中添加此类功能很简单。但这不会帮助在分布式 Clojure 程序中使用此类功能,因为使用官方 Clojure 版本的人无法使用新功能编译和运行源代码。
我也希望在Clojure中有文本块。
我的用例是嵌入其他编程语言,比如Python。

“文本块”是实现这一点的必要条件,若不这样的话,引用问题会非常烦人。
...