Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure : loading dependencies at the REPL

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.

like image 853
jayunit100 Avatar asked Mar 21 '12 18:03

jayunit100


1 Answers

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.

like image 106
Jeff Avatar answered Oct 05 '22 14:10

Jeff