【问题标题】:Best practices in building and deploying Clojure applications: good tutorials?构建和部署 Clojure 应用程序的最佳实践:好的教程?
【发布时间】:2011-01-24 09:42:14
【问题描述】:

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

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

我已经习惯了 C 或 Delphi 等语言的编辑-编译-运行习惯用法,以至于我本能地想要进行编辑,然后点击“M-x compile”。

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

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

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

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

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

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

【问题讨论】:

    标签: deployment lisp clojure leiningen


    【解决方案1】:

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

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

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

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

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

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

    1. Clojure for system administration
    2. Prepping clojure for packaging (was: Re: Clojure for system administration)
    3. Leiningen, Clojure and libraries: what am I missing?

    【讨论】:

    【解决方案2】:

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

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

    如果您使用 Slime,您可以在源文件中按 C-c C-c 来编译和加载该函数。然后,您可以切换到 REPL 进行测试和探索,但是任何您想作为源代码持久化的内容,您都可以放入源文件中。

    教程通常从在 REPL 上输入内容开始,因为您不需要为此进行太多设置,但认真的开发集成了运行系统和源文件管理。


    为了说明,我通常的工作流程(我使用的是 Common Lisp,但 Clojure 类似)是这样的:

    • 启动 Emacs
    • M-x slime 启动 Lisp 系统 Slime,并通过 Swank 连接两者
    • ,(命令)load-systemfoo将当前项目(仅在必要时编译)加载到映像中
    • C-x b 切换到源缓冲区
    • C-c ~将源码目录设为当前目录,源码包设为REPL的当前包

    现在,我的系统已在后台运行。工作是:

    • 更改或添加函数或类定义
    • C-c C-c 编译加载到镜像中
    • 切换到 REPL,测试
    • 调试

    没有明显的编译暂停,因为我从不一次编译整个东西,只是单独的定义。

    【讨论】:

    • 还有C-c C-z 用于从任何 Lisp 源缓冲区切换到 REPL 缓冲区。一般来说,学习正确利用 SLIME 和 Paredit 是重中之重——然后还有许多不太重要但仍然非常有用的东西,例如ido-mode(或达到类似目的的东西——可能是冰柱或anything.el)。 (感谢您的编辑,顺便说一句!)
    • 我在C-c s 设置了粘液选择器,它提供了更多的缓冲区切换选项。
    猜你喜欢
    • 2013-06-03
    • 1970-01-01
    • 1970-01-01
    • 2011-05-19
    • 2015-08-05
    • 2020-11-14
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    相关资源
    最近更新 更多