Clojure:在REPL中加载依赖关系

我最近得知(感谢technomancy),在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类。

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

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

我的第二个问题是, 一般情况下,在REPL中做什么的指导方针与在普通clojure源文件中做的事情相比? 假设在这里我们可以从LEININGEN项目的根目录加载我们的repl,所以至less在依赖项子目录中的磁盘上可用。

我将从高层到下面的特定问题:

Clojure(或LISPs)如何工作

REPL或Read-Eval-Print Loops是LISPdevise的核心:

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

所以当你input一个文本到REPL时,它会经过这些步骤来处理你的input并把输出返回到你的terminal。

读者forms

首先一些,clojure读者forms。 这将是非常简短的,我鼓励你阅读或观看( 第1 部分 , 第2部分 )。

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

前面带有冒号的符号是一个关键字 。 除了关键字的值总是自己的外,关键字就像符号一样 – 类似于string或数字。 它们与Ruby的符号(它们也以冒号为前缀)相同。

表单前的引号告诉评估者保持原样保留数据结构:

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

虽然引用可以应用到不仅仅是列表,它主要用于列表,因为clojure的评估器通常将列表作为类似于函数的调用执行。 使用'是简短的引用macros:

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

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

 user=> 'foo ; not defined earlier foo 

引用是recursion的。 所以里面的所有数据也被引用:

 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 

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

要求

至于要求陈述,我假设你find了前者的forms:

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

ns是一个macros ,它将把:requireexpression式转换为你描述的后一种forms:

 (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函数。 但在文件中,使用nsmacros来做这些事情。

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

记住当你使用关键字作为函数时会发生什么:

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

它在地图上看起来自己。 所以,当你将[clojure.set :as set]传递给一个关键字的时候,它正试图评估一个向量,并且因为不知道clojure.set是什么而失败。 Clojure文档说:

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

你可能被nsmacros弄糊涂了:

 (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)))