Clojure:在 REPL 加载依赖项 [英] Clojure : loading dependencies at the REPL

查看:14
本文介绍了Clojure:在 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,而只使用 requireuse 函数.但是在文件中,使用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆