关于含义:Clojure中函数名称前面的-(减号)是什么意思?

What does the - (minus) symbol in front of function name within Clojure mean?

我无法理解以下内容。 在Clojure中定义主要功能(基于Leinigen生成的代码)时,在功能名称main前面有一个-符号。

我去了clojure.com上的原始文档,并发现了defndefn-等其他内容,请参见https://clojuredocs.org/search?q=defn。 我还在Google上进行搜索,发现有消息说main前面的-表示该函数是静态的(http://ben.vandgrift.com/2013/03/13/clojure-hello-world.html )。

-确实表示该函数是静态的吗? 我找不到其他证实这一点的消息来源。 同样,在调用main方法时也可以同时使用(main)(-main),而不会出现任何问题。

给定以下代码...

1
2
3
4
5
6
7
8
9
(defn -main
 "I don't do a whole lot ... yet."
  [& args]
  (println"Hello, World!"))

(defn main
 "I don't do a whole lot ... yet."
  [& args]
  (println"Hello, World!"))

我得到以下输出...

1
2
3
4
5
6
7
8
9
10
11
12
13
(main)
Hello, World!
=> nil
(-main)
Hello, World!
=> nil
Loading src/clojure_example/core.clj... done
(main)
Hello, World!
=> nil
(-main)
Hello, World!
=> nil

我没有发现任何区别。 两种功能的输出相同。 任何帮助表示赞赏!


首先,放在一边。

关于宏defndefn-,第二种形式只是"私有"功能的简写。长格式如下:

1
(defn ^:private foo [args] ...)

但是,这仅是向用户提示不应使用这些功能。测试等可以轻松解决此弱的"私有"限制。由于麻烦,我从不使用所谓的"私有"函数(有时我会使用元数据^:no-docfoo-impl之类的名称来表示fn并非面向公众的API的一部分,图书馆用户应忽略它们)。

Clojure程序中的"主要"功能

在Java中,总是通过在所选类中调用" main"函数来启动程序

1
2
3
4
5
class Foo
  public static void main( String[] args ) {
    ...
  }
}

然后

1
2
> javac Foo.java   ; compile class Foo
> java Foo         ; run at entrypoint Foo.main()

Clojure选择命名初始函数-main。函数名称-main中的连字符并不是很特殊,除了它使名称不寻常之外,因此它不太可能与您的代码库中的任何其他函数发生冲突。您可以在函数clojure.main/main-opt的定义中看到这一点。

您可以在gen-class的文档中看到hypen约定的部分起源(向下滚动以查看有关:prefix的部分)。请注意,如果对Java互操作使用gen-class,则可以使用连字符。

使用Clojure Deps和CLI工具,名称-main被假定为程序的起点。

如果使用的是Leiningen,它会更加灵活,并允许其覆盖程序的-main入口点。

在Leiningen项目中,如下所示的条目指示您键入lein run时从哪里开始:

1
2
; assumes a `-main` function exists in the namespace `demo.core`
:main ^:skip-aot demo.core

所以在这样的程序中:

1
2
3
4
5
6
7
8
9
10
11
(ns demo.core )

(defn foo [& args]
  (newline)
  (println"*** Running in foo program ***")
  (newline))

(defn -main [& args]
  (newline)
  (println"*** Running in main program ***")
  (newline))

我们得到正常的行为:

1
2
3
~/expr/demo > lein run

*** Running in main program ***

但是,我们可以用另一种方式调用程序:

1
2
3
> lein run -m demo.core/foo

*** Running in foo program ***

使foo函数成为"入口点"。我们还可以像这样更改:main设置:

1
:main ^:skip-aot demo.core/foo

并得到行为:

1
2
3
~/expr/demo > lein run

*** Running in foo program ***

因此,默认情况下,具有名为-main的Clojure程序的初始功能是大多数工具所必需的。如果使用Leiningen,则可以覆盖默认值,尽管这可能仅对测试和开发有用。

请记住,每个名称空间都可以有自己的-main函数,因此您只需更改调用的初始名称空间就可以轻松更改初始函数。

最后,-main中的连字符与用于通过defn-定义的伪私有函数的连字符无关。


所有clojure函数都是"静态的"(下面的题述)。在函数名称中放置-作为第一个字符不会对函数的行为产生任何影响。使用defn-宏而不是defn使函数私有。 -main是~~约定~~ Clojure程序的主入口点的名称,如果在项目定义中将ns指定为" main"命名空间,则Clojure运行时将查找名为-main的函数并调用它。

现在我考虑的不是真正的"按惯例"。由于无法使用标准工具进行配置。这是clojure.main将寻找的唯一名称。

https://clojure.org/reference/repl_and_main

实际上,所有clojure函数都是Java对象AFunction的实例,带有实例方法invoke。因此从Java的角度来看它们不是静态的,但是在Clojure中,我会说它们是静态的,因为它们没有您看到的实例。 gen-class也有特殊情况,您可以使用clojure定义Java类。在这种情况下,您可以将生成的java类的clojure函数标记为^:static。这将在生成的java类中创建一个引用AFunction实例的静态方法。