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
,只使用require
和use
函数。 但在文件中,使用ns
macros来做这些事情。
区别在于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)相同。
你可能被ns
macros弄糊涂了:
(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)))