Clojure:在 REPL 加载依赖项 [英] Clojure : loading dependencies at the REPL
问题描述
我最近了解到(感谢技术),在 REPL ---
这失败了:
user=>(:require [clojure.set :as set])java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)
虽然成功了:
user=>(需要 '[clojure.set :as cs])零
在加载 clojure.set 类时.
上下文:前一行是从命名空间的源文件中复制的.
我的主要问题是:通过交换 : 和 ' 字符,我们所做的更改是什么,现在允许后一个命令成功?
我的第二个问题是,总的来说 - 在 REPL 中做事的指导方针是什么 --- 与在普通 clojure 源文件中做事相比? 假设我们可以在这里加载我们的 repl来自 LEININGEN 项目的根目录,因此至少 jars 将在磁盘上的依赖项子目录中可用.
我会从高层到您的特定问题:
Clojure(或 LISP)通常如何工作
REPL 或 Read-Eval-Print Loops 是 LISP 设计的核心:
- 阅读器将字符流转换为数据结构(称为阅读器表单).
- 评估者收集读者表格并对其进行评估.
- 打印机发出求值器的结果.
因此,当您将文本输入到 REPL 中时,它会通过每个步骤来处理您的输入并将输出返回到您的终端.
读者表格
首先是一些clojure阅读器表单.这将非常简短,我鼓励您阅读或观看 (第 1 部分,第 2 部分.>
符号 在 clojure 中是可以表示特定值(如变量)的形式.符号本身可以作为数据传递.它们类似于 c 中的指针,只是没有内存管理的东西.
前面有冒号的符号是关键字.关键字类似于符号,但关键字的值始终是其自身——类似于字符串或数字.它们与 Ruby 的符号相同(也以冒号为前缀).
表单前的引号告诉评估者保持数据结构不变:
user=>(清单 1 2)(1 2)用户=>'(1 2)(1 2)用户=>(= (列表 1 2) '(1 2))真的
虽然引用不仅仅适用于列表,但它主要用于列表,因为 clojure 的评估器通常会将列表作为类似函数的调用来执行.使用 '
是引用宏的简写:
user=>(引用 (1 2)) ;与 '(1 2) 相同(1 2)
引用基本上指定了要返回的数据结构,而不是要执行的实际代码.所以你可以引用引用符号的符号.
user=>'富;之前没有定义富
引用是递归的.所以里面的所有数据也都引用了:
user=>'(富栏)(富吧)
要获得 (foo bar)
的行为而无需引用,您可以对其进行评估:
user=>(eval '(foo bar)) ;请记住, foo 和 bar 还没有定义.CompilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context, compiling:(NO_SOURCE_PATH:1)用户=>(定义 foo 身份)#'用户/foo用户=>(定义栏 1)#'用户/栏用户=>(eval '(foo bar))1
要引用的还有很多,但这超出了范围.
需要
至于 require 语句,我假设您以以下形式找到前者:
(ns my.namespace(:require [clojure.set :as set]))
ns
是一个 宏,它将把 :require 表达式转换成你描述的后一种形式:>
(需要 '[clojure.set :as set])
以及一些命名空间工作.在 REPL 中要求 ns 的文档时描述了基础知识.
user=>(文档 ns)-------------------------clojure.core/ns([name docstring?attr-map?references*])宏将 *ns* 设置为按名称命名的命名空间(未评估),创建它如果需要的话.引用可以是零个或多个:(:refer-clojure ...)(:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)使用引用 clojure/require/use/import/load/gen-class 的语法分别,除了参数是未评估的,不需要引.(:gen-class ...),当提供时,默认为 :name对应于 ns 名称,:main true,:impl-ns 与 ns 相同,以及:init-impl-ns 真.gen-class 的所有选项都是支持的.:gen-class 指令在没有时被忽略编译.如果 :gen-class 未提供,则编译时仅提供一个将生成 nsname__init.class.如果 :refer-clojure 没有被使用,一个使用默认值(请参阅clojure").优先使用 ns对 in-ns/require/use/import 的单独调用:
REPL 用法
一般情况下,REPL 中不要使用 ns
,而只使用 require
和 use
函数.但是在文件中,使用 ns
宏来做这些事情.
I recently learned (thanks to technomancy) that, at the REPL ---
This fails:
user=> (:require [clojure.set :as set])
java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)
Whereas this succeeds :
user=> (require '[clojure.set :as cs])
nil
at loading the clojure.set class.
Context: The former line was copied from a namespaced source file.
My primary question is : What is the change we have made, by swapping the : and ' characters, which now allows for success of the latter command ?
My 2nd question is , in general - what are the guidelines for doing things at the REPL --- as compared with doing things in normal clojure source files ? Assume here that we can load our repl from the root of a LEININGEN project, so at least the jars will be available on disk in the dependencies sub directory.
I'll go from high-level down to your particular problem:
How Clojure (or LISPs) Generally Work
REPLs, or Read-Eval-Print Loops are the core of how LISPs are designed:
- The reader converts a stream of characters into data structures (called Reader Forms).
- The evaluator takes collection of reader forms and evaluates them.
- The printer emits the results of the evaluator.
So when you enter text into a REPL, it goes through each of these steps to process your input and return the output to your terminal.
Reader Forms
First some, clojure reader forms. This will be extremely brief, I encourage you to read or watch (part 1, part 2) about it.
A symbol in clojure is form that can represent a particular value (like a variable). Symbols themselves can be pass around as data. They are similar to pointers in c, just without the memory management stuff.
A symbol with a colon in front of it is a keyword. Keywords are like symbols with the exception that a keyword's value are always themselves - similar to strings or numbers. They're identical to Ruby's symbols (which are also prefixed with colons).
A quote in front of a form tells the evaluator to leave the data structure as-is:
user=> (list 1 2)
(1 2)
user=> '(1 2)
(1 2)
user=> (= (list 1 2) '(1 2))
true
Although quoting can apply to more than just lists, it's primarily used for lists because clojure's evaluator will normally execute lists as a function-like invocation. Using the '
is shorthand to the quote macro:
user=> (quote (1 2)) ; same as '(1 2)
(1 2)
Quoting basically specifies data structure to return and not actual code to execute. So you can quote symbols which refers to the symbol.
user=> 'foo ; not defined earlier
foo
And quoting is recursive. So all the data inside are quoted too:
user=> '(foo bar)
(foo bar)
To get the behavior of (foo bar)
without quoting, you can eval it:
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
There's a lot more to quoting, but that's out of this scope.
Requiring
As for require statements, I'm assuming you found the former in the form of:
(ns my.namespace
(:require [clojure.set :as set]))
ns
is a macro that will transform the :require expression into the latter form you described:
(require '[clojure.set :as set])
Along with some namespacing work. The basics are described when asking for the docs of ns in the REPL.
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 usage
In general, don't use ns
in the REPL, and just use the require
and use
functions. But in files, use the ns
macro to do those stuff.
这篇关于Clojure:在 REPL 加载依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!