Clojure:在REPL上加载依赖项

时间:2012-03-21 18:42:13

标签: clojure read-eval-print-loop

我最近在REPL ---

学到了(感谢技术)

此操作失败:

user=> (:require [clojure.set :as set])
java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)

然后成功:

user=> (require '[clojure.set :as cs]) 
nil

加载clojure.set类。

上下文:前一行是从命名空间源文件中复制的。

我的主要问题是:我们所做的改变是什么,通过交换:和'字符,现在允许后一个命令成功?

我的第二个问题是,一般来说 - 与在普通的clojure源文件中做事相比,在REPL中做什么的准则是什么?这里假设我们可以加载我们的repl从LEININGEN项目的根目录开始,所以至少这些jar将在依赖项子目录的磁盘上可用。

3 个答案:

答案 0 :(得分:50)

我会从高级到你的特定问题:

Clojure(或LISP)如何正常工作

REPL或Read-Eval-Print循环是LISP设计的核心:

  • 阅读器将字符流转换为数据结构(称为Reader Forms)。
  • 评估者收集读者表单并对其进行评估。
  • 打印机会发出评估者的结果。

因此,当您在REPL中输入文本时,它将执行以下每个步骤来处理您的输入并将输出返回到您的终端。

读者表格

首先是一些clojure读者形式。这将非常简短,我建议您read或关注它(part 1part 2)。

clojure中的符号是可以表示特定值(如变量)的形式。符号本身可以作为数据传递。它们类似于c中的指针,只是没有内存管理的东西。

前面带冒号的符号是关键字。关键字就像符号一样,但关键字的值总是自身 - 类似于字符串或数字。它们与Ruby的符号(也以冒号前缀)相同。

表单前面的引用告诉评估者按原样保留数据结构:

user=> (list 1 2)
(1 2)
user=> '(1 2)
(1 2)
user=> (= (list 1 2) '(1 2))
true

虽然引用可以应用于不仅仅是列表,但它主要用于列表,因为clojure的求值程序通常会将列表作为类似函数的调用来执行。使用'是报价宏的缩写:

user=> (quote (1 2)) ; same as '(1 2)
(1 2)

Quoting基本上指定要返回的数据结构,而不是要执行的实际代码。所以你可以引用符号的符号。

user=> 'foo ; not defined earlier
foo

引用是递归的。因此,所有内部数据也被引用:

user=> '(foo bar)
(foo bar)

要在不引用的情况下获取(foo bar)的行为,您可以评估它:

user=> (eval '(foo bar)) ; Remember, foo and bar weren't defined yet.
CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:1)
user=> (def foo identity)
#'user/foo
user=> (def bar 1)
#'user/bar
user=> (eval '(foo bar))
1

引用还有很多,但这超出了这个范围。

需要

至于需要陈述,我假设你以下列形式找到了前者:

(ns my.namespace
    (:require [clojure.set :as set]))

nsmacro,它会将:require表达式转换为您描述的后一种形式:

(require '[clojure.set :as set])

还有一些命名空间工作。在REPL中询问ns的文档时描述了基础知识。

user=> (doc ns)
-------------------------
clojure.core/ns
([name docstring? attr-map? references*])
Macro
  Sets *ns* to the namespace named by name (unevaluated), creating it
  if needed.  references can be zero or more of: (:refer-clojure ...)
  (:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
  with the syntax of refer-clojure/require/use/import/load/gen-class
  respectively, except the arguments are unevaluated and need not be
  quoted. (:gen-class ...), when supplied, defaults to :name
  corresponding to the ns name, :main true, :impl-ns same as ns, and
  :init-impl-ns true. All options of gen-class are
  supported. The :gen-class directive is ignored when not
  compiling. If :gen-class is not supplied, when compiled only an
  nsname__init.class will be generated. If :refer-clojure is not used, a
  default (refer 'clojure) is used.  Use of ns is preferred to
  individual calls to in-ns/require/use/import:

REPL用法

通常,请勿在REPL中使用ns,只需使用requireuse函数即可。但是在文件中,使用ns宏来执行这些操作。

答案 1 :(得分:13)

区别在于require是用于导入代码的函数,而:require是关键字。

请记住将关键字用作函数时会发生什么:

=> (type :require)
clojure.lang.Keyword
=> (:require {:abc 1 :require 14})
14

它在地图中看起来很自在。因此,当您将[clojure.set :as set]传递给关键字时,它会尝试将其评估为向量,并且因为它不知道clojure.set是什么而失败。 Clojure docs说:

  

关键字为一个参数(一个映射)的invoke()实现IFn   可选的第二个参数(默认值)。例如(:mykey   my-hash-map:none)表示与(get my-hash-map:mykey:none)相同。

您可能对the ns macro感到困惑:

(ns foo.bar
  (:refer-clojure :exclude [ancestors printf])
  (:require (clojure.contrib sql sql.tests))    ;; here's :require!
  (:use (my.lib this that))
  (:import (java.util Date Timer Random)
           (java.sql Connection Statement)))

答案 2 :(得分:2)

ns

当您输入时:

(ns some-great-ns 
  :require my-form) 

您使用 :require 引用,在其中声明您希望使用给定命名空间中的内容。相当于写:

(in-ns 'some-great-ns)
(require 'my-form)

请注意,在 ns 形式中(与 in-ns 函数调用不同),您不必用 ' 引用您的符号。您永远不必在 ns 内引用符号。

require 函数

如前所述,可以在某个给定的命名空间中运行:(require 'some-great-ns),以便您可以使用它。要使用它,您必须使用完全限定名称,除非您在需要命名空间后立即使用:refer function: (refer 'some-great-ns)

您可以将这两个功能合二为一:(use 'some-great-ns)。现在你不需要写:(some-great-ns/my-form)。简单地说:my-form

当然,您也可以在宏引用和函数中使用 :as:exclude:only:rename 关键字。

宏和函数的区别:

  1. 如上所述,在函数中使用符号,在宏中不需要
  2. 您可以在 (:require) 引用中要求多个库,如下所示:
(ns my-great-namespace.core
  (:require [some-other-ns.a.b :as ab]
            [some-other-other-ns.c.d :as cd]))

function 处应该写 2 行:

(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b :as 'ab)
(require 'some-other-other=ns.c.d :as 'cd)
  1. require 引用还允许您引用名称,例如:
(ns my-great-namespace.core
  (:require [some-other-ns.a.b :refer [some-func]]))

您应该在函数中的哪个位置:

(in-ns my-great-namespace.core)
(require 'some-other-ns.a.b)
(refer 'some-other-ns.a.b :only ['some-func])