Clojure: загрузка зависимостей в REPL - PullRequest
36 голосов
/ 21 марта 2012

Я недавно узнал (благодаря технике), что на 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, поэтому по крайней мере файлы jar будут доступны на диске в подкаталоге зависимостей.

Ответы [ 2 ]

50 голосов
/ 21 марта 2012

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

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

REPL или циклы Read-Eval-Print являются ядромкак создаются LISP:

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

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

Reader Forms

Сначала некоторые, clojure reader формы.Это будет очень кратко, я призываю вас прочитать или посмотреть ( часть 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

Есть еще много чего,но это выходит за рамки.

Требование

Что касается операторов require, я предполагаю, что вы нашли первое в форме:

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

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

13 голосов
/ 21 марта 2012

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

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

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

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

Ключевые слова реализуют IFn для invoke () одного аргумента (карты) с необязательный второй аргумент (значение по умолчанию). Например (: mykey my-hash-map: none) означает то же самое, что и (получить 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)))
...