Clojure:在REPL上加载依赖项

jay*_*100 36 clojure read-eval-print-loop

我最近学到了(感谢技术),在REPL ---

这失败了:

user=> (:require [clojure.set :as set])
java.lang.ClassNotFoundException: clojure.set (NO_SOURCE_FILE:24)
Run Code Online (Sandbox Code Playgroud)

虽然这成功了:

user=> (require '[clojure.set :as cs]) 
nil
Run Code Online (Sandbox Code Playgroud)

在加载clojure.set类时.

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

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

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

Jef*_*eff 50

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

Clojure(或LISP)如何一般工作

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

  • 所述读取器转换字符到数据结构(被称为阅读器形式)的一个流.
  • 评估需要的读者形式收集和评估它们.
  • 打印机发出的评估结果.

因此,当您在REPL中输入文本时,它会通过每个步骤来处理您的输入并将输出返回到您的终端.

读者表格

首先是一些clojure读者形式.这将非常简短,我鼓励您阅读或观看(第1 部分,第2部分).

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

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

引用的形式的前告诉评价者离开的数据结构,是:

user=> (list 1 2)
(1 2)
user=> '(1 2)
(1 2)
user=> (= (list 1 2) '(1 2))
true
Run Code Online (Sandbox Code Playgroud)

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

user=> (quote (1 2)) ; same as '(1 2)
(1 2)
Run Code Online (Sandbox Code Playgroud)

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

user=> 'foo ; not defined earlier
foo
Run Code Online (Sandbox Code Playgroud)

引用是递归的.所以里面的所有数据也被引用:

user=> '(foo bar)
(foo bar)
Run Code Online (Sandbox Code Playgroud)

要获得(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
Run Code Online (Sandbox Code Playgroud)

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

要求

至于require语句,我假设您以以下形式找到前者:

(ns my.namespace
    (:require [clojure.set :as set]))
Run Code Online (Sandbox Code Playgroud)

ns是一个,它将:require表达式转换为您描述的后一种形式:

(require '[clojure.set :as set])
Run Code Online (Sandbox Code Playgroud)

随着一些命名空间的工作.在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:
Run Code Online (Sandbox Code Playgroud)

REPL用法

一般情况下,不要ns在REPL中使用,只需使用requireuse函数.但是在文件中,使用ns宏来做那些事情.


Mat*_*ick 13

不同之处在于它require是用于导入代码的函数,而是:require一个关键字.

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

=> (type :require)
clojure.lang.Keyword
=> (:require {:abc 1 :require 14})
14
Run Code Online (Sandbox Code Playgroud)

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

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

您可能ns感到困惑:

(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)))
Run Code Online (Sandbox Code Playgroud)