Skip to content

Instantly share code, notes, and snippets.

@usametov
Last active August 14, 2020 01:36
Show Gist options
  • Save usametov/e344f860ed5c61b899f84b2f62ffeaaf to your computer and use it in GitHub Desktop.
Save usametov/e344f860ed5c61b899f84b2f62ffeaaf to your computer and use it in GitHub Desktop.

Looking up documentation

The REPL can also be used for looking up API documentation, using the clojure.repl lib. Evaluate the following expression at the REPL:

user=> (require '[clojure.repl :refer :all])

This expression makes all the names defined in the clojure.repl namespace available in the REPL. doc

You can print the API documentation of a given Var by evaluating (doc MY-VAR-NAME):

user=> (doc nil?)

clojure.core/nil? ([x])

Returns true if x is nil, false otherwise. nil

user=> (doc clojure.string/upper-case)

clojure.string/upper-case ([s]) Converts string to all upper-case. nil

source

You can also view the source code that was used to define a Var using source:

user=> (source some?) (defn some? "Returns true if x is not nil, false otherwise." {:tag Boolean :added "1.6" :static true} [x] (not (nil? x))) nil

dir

You can use dir to list the names of all the Vars defined a given namespace. Let’s do this with the clojure.string namespace:

user=> (dir clojure.string)

blank? capitalize ends-with? escape includes? index-of join last-index-of lower-case re-quote-replacement replace replace-first reverse split split-lines starts-with? trim trim-newline triml trimr upper-case nil

As another example, let’s use dir to see what’s available in clojure.repl itself:

user=> (dir clojure.repl)

apropos demunge dir dir-fn doc find-doc pst root-cause set-break-handler! source source-fn stack-element-str thread-stopper nil

We recognize the doc, source and dir operations we’ve used so far. apropos

If you don’t exactly remember the name of some Var, you can search for it using apropos:

user=> (apropos "index") (clojure.core/indexed? clojure.core/keep-indexed clojure.core/map-indexed clojure.string/index-of clojure.string/last-index-of)

apropos only searches Var names; you can search docstrings (the text that is printed by doc) using find-doc:

find-doc

user=> (find-doc "indexed")

clojure.core/contains? ([coll key]) Returns true if key is present in the given collection, otherwise returns false. Note that for numerically indexed collections like vectors and Java arrays, this tests if the numeric key is within the range of indexes. 'contains?' operates constant or logarithmic time; it will not perform a linear search for a value. See also 'some'.

clojure.core/indexed? ([coll]) Return true if coll implements Indexed, indicating efficient lookup by index

clojure.core/keep-indexed ([f] [f coll]) Returns a lazy sequence of the non-nil results of (f index item). Note, this means false return values will be included. f must be free of side-effects. Returns a stateful transducer when no collection is provided.

clojure.core/map-indexed ([f] [f coll]) Returns a lazy sequence consisting of the result of applying f to 0 and the first item of coll, followed by applying f to 1 and the second item in coll, etc, until coll is exhausted. Thus function f should accept 2 arguments, index and item. Returns a stateful transducer when no collection is provided. nil

Documentation is available only for libs that have been required.

For instance, if you have not required the clojure.set namespace, you won’t be able to search documentation for clojure.set/union. This is illustrated by this example REPL session:

clj Clojure 1.10.0 user=> (doc clojure.set/union) nil ;; no doc found user=> (apropos "union") () user=> (require '[clojure.set]) ;; now we're requiring clojure.set nil user=> (doc clojure.set/union)

clojure.set/union ([] [s1] [s1 s2] [s1 s2 & sets]) Return a set that is the union of the input sets nil user=> (apropos "union") (clojure.set/union) user=>

for a result which is a sequence of maps (like the above), use clojure.pprint/print-table to print it as a table:

user=> (pp/print-table (mapv number-summary [6 12 28]))

| :n | :proper-divisors | :even? | :prime? | :perfect-number? | |----+------------------+--------+---------+------------------| | 6 | #{1 2 3} | true | false | true | | 12 | #{1 2 3 4 6} | true | false | false | | 28 | #{1 2 4 7 14} | true | false | true | nil

When a structure is too deeply nested, you can truncate the output by setting the print-level Var:

user=> (set! *print-level* 3) 3 user=> {:a {:b [{:c {:d {:e 42}}}]}} ;; a deeply nested data structure {:a {:b [#]}}

You can undo this setting by evaluating (set! print-level nil).

Likewise, when a data structure contains long collections, you can limit the number of displayed item by setting the print-length Var:

user=> (set! print-length 3) 3 user=> (repeat 100 (vec (range 100))) ;; a data structure containing looooong collections. ([0 1 2 ...] [0 1 2 ...] [0 1 2 ...] ...)

Like the above, evaluate (set! print-length nil) to undo this setting.

print-level and print-length affect both ordinary REPL printing and pretty-pretting.

Accessing recent results: *1, *2, *3

In the REPL, the last evaluated result can be retrieved by evaluating *1; the one before that is saved in *2, and the one before that in *3:

user=> (mapv number-summary [6 12 28]) [{:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false, :perfect-number? true} {:n 12, :proper-divisors #{1 2 3 4 6}, :even? true, :prime? false, :perfect-number? false} {:n 28, :proper-divisors #{1 2 4 7 14}, :even? true, :prime? false, :perfect-number? true}] user=> (pp/pprint *1) ;; using *1 instead of re-typing the pevious expression (or its result) [{:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false, :perfect-number? true} {:n 12, :proper-divisors #{1 2 3 4 6}, :even? true, :prime? false, :perfect-number? false} {:n 28, :proper-divisors #{1 2 4 7 14}, :even? true, :prime? false, :perfect-number? true}] nil user=> *1 ;; now *1 has changed to become nil (because pp/pprint returns nil) nil user=> *3 ;; ... which now means that our initial result is in *3: [{:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false, :perfect-number? true} {:n 12, :proper-divisors #{1 2 3 4 6}, :even? true, :prime? false, :perfect-number? false} {:n 28, :proper-divisors #{1 2 4 7 14}, :even? true, :prime? false, :perfect-number? true}] user=>

TIP: saving a result by def-ining it

If you want to keep a result around for longer than 3 evaluations, you can simply evaluate (def *1):

user=> (mapv number-summary [6 12 28]) [{:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false ; ... user=> (def my-summarized-numbers *1) ;; saving the result #'user/my-summarized-numbers user=> my-summarized-numbers [{:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false ; ... user=> (count my-summarized-numbers) 3 user=> (first my-summarized-numbers) {:n 6, :proper-divisors #{1 2 3}, :even? true, :prime? false, ; ... user=> (pp/print-table my-summarized-numbers)

| :n | :proper-divisors | :even? | :prime? | :perfect-number? | |----+------------------+--------+---------+------------------| | 6 | #{1 2 3} | true | false | true | | 12 | #{1 2 3 4 6} | true | false | false | | 28 | #{1 2 4 7 14} | true | false | true | nil user=>

Investigating Exceptions

Some expressions won’t return a result when you evaluate them, but throw an Exception instead. Throwing an Exception is your program saying to you: "something went wrong when evaluating the expression, and I don’t know how to deal with it, so I gave up."

For instance, an Exception will be thrown if you divide a number by zero:

user=> (/ 1 0) Execution error (ArithmeticException) at user/eval163 (REPL:1). Divide by zero

By default, the REPL prints a two-line summary of the Exception. The first line reports the error phase (execution, compilation, macroexpansion, etc) and its location. The second line reports the cause.

This can be enough in many cases, but there is more information available.

First, you can visualize the stacktrace of the Exception - that is, the chain of function calls which led to the faulty instruction. The stacktrace can be printed using clojure.repl/pst:

user=> (pst *e) ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:163) clojure.lang.Numbers.divide (Numbers.java:3833) user/eval15 (NO_SOURCE_FILE:3) user/eval15 (NO_SOURCE_FILE:3) clojure.lang.Compiler.eval (Compiler.java:7062) clojure.lang.Compiler.eval (Compiler.java:7025) clojure.core/eval (core.clj:3206) clojure.core/eval (core.clj:3202) clojure.main/repl/read-eval-print--8572/fn--8575 (main.clj:243) clojure.main/repl/read-eval-print--8572 (main.clj:243) clojure.main/repl/fn--8581 (main.clj:261) clojure.main/repl (main.clj:261) nil

TIP: the last thrown Exception can be obtained by evaluating *e.

Finally, just evaluating the Exception at the REPL can provide a useful visualization:

user=> *e #error { :cause "Divide by zero" :via [{:type java.lang.ArithmeticException :message "Divide by zero" :at [clojure.lang.Numbers divide "Numbers.java" 163]}] :trace [[clojure.lang.Numbers divide "Numbers.java" 163] [clojure.lang.Numbers divide "Numbers.java" 3833] [user$eval15 invokeStatic "NO_SOURCE_FILE" 3] [user$eval15 invoke "NO_SOURCE_FILE" 3] [clojure.lang.Compiler eval "Compiler.java" 7062] [clojure.lang.Compiler eval "Compiler.java" 7025] [clojure.core$eval invokeStatic "core.clj" 3206] [clojure.core$eval invoke "core.clj" 3202] [clojure.main$repl$read_eval_print__8572$fn__8575 invoke "main.clj" 243] [clojure.main$repl$read_eval_print__8572 invoke "main.clj" 243] [clojure.main$repl$fn__8581 invoke "main.clj" 261] [clojure.main$repl invokeStatic "main.clj" 261] [clojure.main$repl_opt invokeStatic "main.clj" 325] [clojure.main$main invokeStatic "main.clj" 424] [clojure.main$main doInvoke "main.clj" 387] [clojure.lang.RestFn invoke "RestFn.java" 397] [clojure.lang.AFn applyToHelper "AFn.java" 152] [clojure.lang.RestFn applyTo "RestFn.java" 132] [clojure.lang.Var applyTo "Var.java" 702] [clojure.main main "main.java" 37]]}

In this simplistic example, displaying all this information may be more than what is needed to diagnose the issue; but this visualization becomes more helpful for 'real-world' Exceptions, which tend to have the following charateristics in Clojure programs:

Exceptions convey data: in Clojure programs, it’s common to attach additional data to an Exception (not just a human-readable error message): this is done by creating the Exception via clojure.core/ex-info.

Exceptions are chained: an Exception can be annotated with an optional cause, which is another (lower-level) Exception.

Here’s an example program which demonstrates these sort of Exceptions.

(defn divide-verbose "Divides two numbers xandy`, but throws more informative Exceptions when it goes wrong. Returns a (double-precision) floating-point number." [x y] (try (double (/ x y)) (catch Throwable cause (throw (ex-info (str "Failed to divide " (pr-str x) " by " (pr-str y)) {:numerator x :denominator y} cause)))))

(defn average "Computes the average of a collection of numbers." [numbers] (try (let [sum (apply + numbers) cardinality (count numbers)] (divide-verbose sum cardinality)) (catch Throwable cause (throw (ex-info "Failed to compute the average of numbers" {:numbers numbers} cause))))) `

We don’t know it yet, but our average function fails when applied to an empty collection of numbers. However, visualizing the Exception makes it easy to diagnose. In the below REPL session, we can see that we calling our function with an empty vector of numbers led to dividing zero by zero:

user=> (average []) Execution error (ArithmeticException) at user/divide-verbose (REPL:6). Divide by zero user=> *e ;; notice the :datakey inside the chain of Exceptions represented in:via#error { :cause "Divide by zero" :via [{:type clojure.lang.ExceptionInfo :message "Failed to compute the average of numbers" :data {:numbers []} :at [user$average invokeStatic "NO_SOURCE_FILE" 10]} {:type clojure.lang.ExceptionInfo :message "Failed to divide 0 by 0" :data {:numerator 0, :denominator 0} :at [user$divide_verbose invokeStatic "NO_SOURCE_FILE" 9]} {:type java.lang.ArithmeticException :message "Divide by zero" :at [clojure.lang.Numbers divide "Numbers.java" 188]}] :trace [[clojure.lang.Numbers divide "Numbers.java" 188] [user$divide_verbose invokeStatic "NO_SOURCE_FILE" 6] [user$divide_verbose invoke "NO_SOURCE_FILE" 1] [user$average invokeStatic "NO_SOURCE_FILE" 7] [user$average invoke "NO_SOURCE_FILE" 1] [user$eval173 invokeStatic "NO_SOURCE_FILE" 1] [user$eval173 invoke "NO_SOURCE_FILE" 1] [clojure.lang.Compiler eval "Compiler.java" 7176] [clojure.lang.Compiler eval "Compiler.java" 7131] [clojure.core$eval invokeStatic "core.clj" 3214] [clojure.core$eval invoke "core.clj" 3210] [clojure.main$repl$read_eval_print__9068$fn__9071 invoke "main.clj" 414] [clojure.main$repl$read_eval_print__9068 invoke "main.clj" 414] [clojure.main$repl$fn__9077 invoke "main.clj" 435] [clojure.main$repl invokeStatic "main.clj" 435] [clojure.main$repl_opt invokeStatic "main.clj" 499] [clojure.main$main invokeStatic "main.clj" 598] [clojure.main$main doInvoke "main.clj" 561] [clojure.lang.RestFn invoke "RestFn.java" 397] [clojure.lang.AFn applyToHelper "AFn.java" 152] [clojure.lang.RestFn applyTo "RestFn.java" 132] [clojure.lang.Var applyTo "Var.java" 705] [clojure.main main "main.java" 37]]}

Graphical and web-based visualizations

Finally, the REPL being a full-featured programming environment, it is not limited to text-based visualizations. Here are some handy "graphical" visualization tools bundled Clojure:

clojure.java.javadoc lets you view the Javadoc of a class or object. Here is how to view the Javadoc for a Java regex Pattern:

user=> (require '[clojure.java.javadoc :as jdoc]) nil user=> (jdoc/javadoc #"a+") ;; opens the Javadoc page for java.util.Pattern in a Web browser true user=> (jdoc/javadoc java.util.regex.Pattern) ;; equivalent to the above true

clojure.inspector lets you open GUI-based visualizations of data

Dealing with mysterious values (advanced)

Sometimes, the printed representation of a value in the REPL is not very informative; sometimes, it can even be misleading as to the nature of that value.[1] This often happens with values which are obtained via Java interop.

As an example, we’ll create an InputStream object using the clojure.java.io lib. If you don’t know what an InputStream is, all the better - the point of this section is to teach you how to find your footing in uncharted territory:

user=> (require '[clojure.java.io :as io]) nil user=> (def v (io/input-stream "https://www.clojure.org")) ;; NOTE won't work if you're not connected to the Internet #'user/v user=> v #object[java.io.BufferedInputStream 0x4ee37ca3 "java.io.BufferedInputStream@4ee37ca3"]

The above code sample defined an InputStream named v.

Now imagine you don’t know where v comes from, and let’s try to interact with it at the REPL so as to gain more understanding of it. Viewing the type hierarchy using type and ancestors

The printed representation of v tells us one thing about it: its runtime type, in this case java.io.BufferedInputStream. The type of a value can help us know what operations we may call on it. We can evaluate (type v) to obtain the concrete type of v, and (ancestors (type v)) to obtain its entire type hierarchy:

user=> (type v) ;; what is the type of our obscure value? java.io.BufferedInputStream user=> (ancestors (type v)) #{java.io.InputStream java.lang.AutoCloseable java.io.Closeable java.lang.Object java.io.FilterInputStream}

Using Javadoc

As we saw in the previous section, we can use the clojure.java.javadoc lib to view online documentation about a Java type:

user=> (require '[clojure.java.javadoc :as jdoc]) nil user=> (jdoc/javadoc java.io.InputStream) ;; should open a web page about java.io.InputStream true

Inspecting Java types with clojure.reflect

Javadoc is helpful, but sometimes Javadoc won’t even be available. In such cases, we can use the REPL itself to inspect types, via Java reflection.

We can use the clojure.reflect/reflect function to obtain information about a Java type as a plain Clojure data structure:

user=> (require '[clojure.reflect :as reflect]) nil user=> (reflect/reflect java.io.InputStream) {:bases #{java.lang.Object java.io.Closeable}, :flags #{:public :abstract}, :members #{#clojure.reflect.Method{:name close, :return-type void, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [java.io.IOException], :flags #{:public}} #clojure.reflect.Method{:name mark, :return-type void, :declaring-class java.io.InputStream, :parameter-types [int], :exception-types [], :flags #{:public :synchronized}} #clojure.reflect.Method{:name available, :return-type int, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [java.io.IOException], :flags #{:public}} #clojure.reflect.Method{:name read, :return-type int, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [java.io.IOException], :flags #{:public :abstract}} #clojure.reflect.Method{:name markSupported, :return-type boolean, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Field{:name MAX_SKIP_BUFFER_SIZE, :type int, :declaring-class java.io.InputStream, :flags #{:private :static :final}} #clojure.reflect.Constructor{:name java.io.InputStream, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Method{:name read, :return-type int, :declaring-class java.io.InputStream, :parameter-types [byte<>], :exception-types [java.io.IOException], :flags #{:public}} #clojure.reflect.Method{:name skip, :return-type long, :declaring-class java.io.InputStream, :parameter-types [long], :exception-types [java.io.IOException], :flags #{:public}} #clojure.reflect.Method{:name reset, :return-type void, :declaring-class java.io.InputStream, :parameter-types [], :exception-types [java.io.IOException], :flags #{:public :synchronized}} #clojure.reflect.Method{:name read, :return-type int, :declaring-class java.io.InputStream, :parameter-types [byte<> int int], :exception-types [java.io.IOException], :flags #{:public}}}}

Now, that is a very hairy data structure. Fortunately, we have learned how to deal with hairy data structures in the first section of this chapter: pretty-printing to the rescue! Let’s use pretty-printing to display the methods exposed by java.io.InputStream in a table:

user=> (->> (reflect/reflect java.io.InputStream) :members (sort-by :name) (pp/print-table [:name :flags :parameter-types :return-type]) )

| :name | :flags | :parameter-types | :return-type | |----------------------+----------------------------+------------------+--------------| | MAX_SKIP_BUFFER_SIZE | #{:private :static :final} | | | | available | #{:public} | [] | int | | close | #{:public} | [] | void | | java.io.InputStream | #{:public} | [] | | | mark | #{:public :synchronized} | [int] | void | | markSupported | #{:public} | [] | boolean | | read | #{:public :abstract} | [] | int | | read | #{:public} | [byte<>] | int | | read | #{:public} | [byte<> int int] | int | | reset | #{:public :synchronized} | [] | void | | skip | #{:public} | [long] | long | nil

For example, this tells us that we can call a .read method on v with no arguments, which will return an int:

user=> (.read v) 60 user=> (.read v) 33 user=> (.read v) 68

Without any prior knowledge, we have managed to learn that v is an InputStream, and read bytes from it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment