Подтвердить что ты не робот

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, поэтому, по крайней мере, банки будут доступны на диске в подкаталоге зависимостей.

4b9b3361

Ответ 1

Я перейду с высокого уровня до вашей конкретной проблемы:

Как Clojure (или LISP) обычно работают

REPLs, или Read-Eval-Print Loops - суть того, как проектируются LISP:

  • reader преобразует поток символов в структуры данных (называемые формами Reader).
  • Оценщик принимает коллекцию форм читателей и оценивает их.
  • Принтер испускает результаты оценки.

Поэтому, когда вы вводите текст в REPL, он проходит каждый из этих шагов для обработки вашего ввода и возврата вывода к вашему терминалу.

Формы чтения

Сначала создаются некоторые, Clojure. Это будет крайне кратким, я рекомендую вам read или смотреть (часть 1, часть 2).

A символ в Clojure - это форма, которая может представлять конкретное значение (например, переменную). Символы сами могут проходить как данные. Они похожи на указатели в c, только без материалов управления памятью.

Символ с двоеточием перед ним является ключевым словом . Ключевые слова похожи на символы, за исключением того, что значение ключевого слова всегда само по себе - подобно строкам или цифрам. Они идентичны символам Ruby (которые также имеют префикс двоеточий).

A quote перед формой сообщает, что оценщик оставляет структуру данных как-есть:

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

Хотя цитирование может применяться к нескольким спискам, оно в основном используется для списков, поскольку оценщик Clojure обычно выполняет списки как функцию-вызов. Использование ' является сокращением макроса цитаты:

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

Цитата в основном указывает структуру данных для возврата, а не фактический код для выполнения. Таким образом, вы можете цитировать символы, которые относятся к символу.

user=> 'foo ; not defined earlier
foo

И цитирование является рекурсивным. Таким образом, все данные внутри тоже цитируются:

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

Здесь гораздо больше ссылок, но из этой области.

Требуя

Что касается требовательных утверждений, я предполагаю, что вы нашли первое в виде:

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

ns является macro, который преобразует выражение require: в выражение, которое вы описали:

(require '[clojure.set :as set])

Наряду с некоторыми работами по расширению имен. Основы описаны при запросе документов ns в 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

В общем случае не используйте ns в REPL и просто используйте функции require и use. Но в файлах используйте макрос ns для выполнения этих действий.

Ответ 2

Разница в том, что require - это функция, используемая для импорта кода, тогда как :require - это ключевое слово.

Помните, что происходит, когда вы используете ключевое слово как функцию:

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

он выглядит на карте. Поэтому, когда вы передаете ключевое слово [clojure.set :as set], оно пытается оценить это для вектора и терпит неудачу, потому что оно не знает, что такое clojure.set. Clojure docs говорят:

Ключевые слова реализуют IFn для invoke() одного аргумента (карту) с необязательный второй аргумент (значение по умолчанию). Например (: 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 :require!
  (:use (my.lib this that))
  (:import (java.util Date Timer Random)
           (java.sql Connection Statement)))