47

我是 Clojure 的新手,并且开始尝试构建应用程序。

到目前为止,我所看到的关于编译 Clojure 程序的教程的所有内容都涉及交互性。例如,“加载 REPL 并键入 (load-file "this-or-that") 运行。这很好,但还不够。

我已经习惯了 C 或 Delphi 等语言的编辑-编译-运行习语,以至于我本能地驱动进行编辑,然后点击“Mx compile”。

问题是“lein uberjar”,我理解它等同于“make”,即使是一个 hello world,执行起来也非常缓慢。所以我将不得不弄清楚这个“交互式开发”的东西是如何工作的,停止使用 uberjar 就像它是快速制作一样,并且只在一天结束时保存它。

我在构建(使用 lein uberjar)时注意到的另一件事是,我正在开发的小型 GUI 应用程序在编译过程中会弹出框架,就好像它们在编译时正在执行一样。这对我来说似乎有点违反直觉。它不像我想象的那样类似于“制造”。

我知道 Lisp 开发事物的方式是在 REPL 中以交互方式工作,我不想改变它:我想适应这种生活方式。不幸的是,我很少看到有关如何执行此操作的文档形式。例如,如何重置机器的当前状态。只是在运行中不断编译单个片段而无法进行某种重置,这似乎有点混乱。

我在 Clojure(和 Lisp)上看到的大多数教程通常都集中在 REPL 中的黑客攻击上。应用程序部署的最佳实践对我来说仍然是个谜。我的用户只是用户;他们不会成为将文件加载到 REPL 中的开发人员。

所以这是我的问题:关于构建 Clojure 应用程序(包括部署)的整个过程的良好信息或教程的任何资源?

(注意:我已经安装并运行了所有先决条件(例如 Emacs、Slime、Leiningen 等),所以这不是一个问题)。

4

2 回答 2

26

一些快速提示,然后是一些链接:

不要lein uberjar在开发过程中使用;喜欢lein jar。不同之处在于lein uberjar将所有依赖项放在生成的jar(包括 Clojure 本身)中,这样您的单个 jar 就是一个完全自包含的包,其中包含您的应用程序;lein jar只压缩你自己的代码。该uberjar方法对部署有明显的好处,但对于开发,您应该能够在运行应用程序时只使用适当的类路径,从而为自己节省准备 uberjar 所需的时间。如果您不想手动管理测试运行的类路径,请查看lein run插件

此外,您的大部分代码很可能实际上不应该经过 AOT 编译。在某些 Java 互操作场景中,AOT 是必要的,但大多数情况下,它会稍微提高启动速度,并且会出现与 Clojure 不同版本的二进制兼容性问题。我想后一个问题与uberjar-ed 独立应用程序类型的项目无关,但如果可能的话,至少应该将任何库代码留给 JIT-ed。使用 Leiningen,您可以在表单中放置一个:namespaces子句来确定要编译哪些名称空间;默认情况下,您遗漏的任何内容当前都会被 JIT 编辑。旧版本的 Leiningen 过去默认编译所有内容,这实际上是升级的好理由!defprojectproject.clj

至于在编译期间弹出的窗口,我猜您要么在宏扩展期间运行弹出窗口的代码,要么在任何函数定义或类似构造之外运行。(类似于(println "Foo!")顶层的 a 。)我想这只是你不应该做的事情——除非你打算将你的代码作为脚本运行,无论如何。为避免该问题,请将具有副作用的代码包装在函数定义中,并:main使用project.clj. (如果您说:main foo,那么命名空间中的-main函数foo将用作您的应用程序的入口点。无论如何,这是默认设置,至少上面提到lein run的名称似乎是硬编码的——不确定 lein 本身。)

至于重置 REPL 的状态——你可以重新启动它。使用 SLIME,Mx slime-restart-inferior-lisp 将做到这一点,同时保持 Emacs 会话的所有其他状态。

另请参阅 Clojure Google 组上的这些讨论:

  1. Clojure 用于系统管理
  2. 准备用于包装的 clojure(原为:Re: Clojure 用于系统管理)
  3. Leiningen、Clojure 和图书馆:我错过了什么?
于 2010-03-06T08:03:00.387 回答
13

不,您不要在 REPL 上输入函数。

您像往常一样编辑源文件。Lisp 的优势在于您让系统同时在后台运行,因此您可以从源文件编译各个函数并将它们放入正在运行的系统中,甚至在那里替换它们。

If you use Slime, you press C-c C-c in your source file to compile and load the function at point. You can then switch to the REPL to test and explore, but anything you want to persist as source, you put into your source files.

Tutorials usually start by typing things on the REPL, because there is not much you need to set up for this, but serious development integrates the running system and source file management.


Just to illustrate, my usual workflow (I am using Common Lisp, but Clojure is similar) is like this:

  • Start Emacs
  • M-x slime to start Slime, the Lisp system, and connect the two via Swank
  • , (command) load-system foo to load the current project (compiling only if necessary) into the image
  • C-x b switch to a source buffer
  • C-c ~ make the source directory the current directory and the source package the current package of the REPL

Now, I'm set up with my system running in the background. Working is then:

  • change or add a function or class definition
  • C-c C-c to compile and load it into the image
  • switch to REPL, test
  • debug

There are no significant compilation pauses, because I never compile the whole thing at once, just individual definitions.

于 2010-03-06T11:58:04.157 回答