官术网_书友最值得收藏!

Using a REPL

One of the main advantages of Clojure (and Lisp) is interactive development, the REPL is the base of what can be achieved with interactive programming, it allows you to connect to a running VM running Clojure and execute or modify code on the fly.

There is a story about how NASA was able to debug and correct a bug on a $100 million piece of hardware that was 100 million miles away (http://www.flownet.com/gat/jpl-lisp.html).

We have that same power with Clojure and Leiningen and invoking it is very simple, you just need a single command:

lein repl

This is what you'll get after running the preceding command:

Let's go into a bit more detail, as we can see we are running with the following programs:

  • Java 8
  • Clojure 1.6.0

We can also get some nice suggestions on how to see documentation, source, Javadoc, and previous errors.

The nREPL protocol

One particular thing that is important to note is the nREPL protocol; Someday it might grant us the power to go into a machine running 100 million miles away.

When you fire up your REPL, the first thing you see is:

nREPL server started on port 55995 on host 127.0.0.1 - nrepl://127.0.0.1:55995
REPL-y 0.3.5, nREPL 0.2.6

What it is saying is that there's a Clojure process running an nREPL server on port 55995. We have connected to it using a very simple client that allows us to interact with the Clojure process.

The really interesting bit is that you can connect to a remote host just as easily; let's try attaching an REPL to the same process by simply typing the following command:

lein repl :connect localhost:55995

Most IDEs have a good integration with Clojure and most of them use this exact mechanism, as clients that work a little more intelligently.

Hello world

Now that we are inside the REPL, (any of the two) let's try writing our first expression, go on and type:

"Hello world"

You should get back a value from the REPL saying Hello world, this is not really a program, and it is the Hello world value printed back by the print phase of the REPL.

Let's now try to write our first Lisp form:

(println "Hello world")

This first expression looks different from what we are used to, it is called an S-expression and it is the standard Lisp way.

There are a couple of things to remember with S-expressions:

  • They are lists (hence, the name, Lisp)
  • The first element of the list is the action that we want to execute, the rest are the parameters of that action (one two three).

So we are asking for the string Hello world to be printed, but if we look a bit closer at the output, as shown in the following screenshot, there is a nil that we weren't expecting:

The reason for this is that the println function returns the value nil (Clojure's equivalent for null) after printing Hello world.

Note

In Clojure, everything has a value and the REPL will always print it back for you.

REPL utilities and conventions

As we saw, the Leiningen nREPL client prints help text; but how does that work? Let's explore some of the other utilities that we have.

Try each of them to get a feeling of what it does with the help of the following table:

Let's check how these functions work:

user=> (javadoc java.util.List)
;; Should open the javadoc for java.util.List

user=> (doc doc)
-------------------------
clojure.repl/doc
([name])
Macro
  Prints documentation for a var or special form given its name
nil

user=> (source doc)
(defmacro doc
"Prints documentation for a var or special form given its name"
  {:added "1.0"}
  [name]
  (if-let [special-name ('{& fn catch try finally try} name)]
    (#'print-doc (#'special-doc special-name))
    (cond
      (special-doc-map name) `(#'print-doc (#'special-doc '~name))
      (find-ns name) `(#'print-doc (#'namespace-doc (find-ns '~name)))
      (resolve name) `(#'print-doc (meta (var ~name))))))
nil

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

What you are seeing here is metadata pertaining to the doc function; Clojure has the ability to store metadata about every function or var you use. Most of the Clojure core functions include a doc string and the source of the function and this is something that will become very handy in your day to day work.

Besides these functions, we also get easy access to the latest three values and the latest exceptions that happened in the REPL, let's check this out:

user=> 2
2
user=> 3
3
user=> 4
4
user=> (* *1 *2 *3) ;; We are multiplying over here the last three values
24 ;;We get 24!
user=> (/ 1 0) ;; Let's try dividing by zero
ArithmeticException Divide by zero clojure.lang.Numbers.divide (Numbers.java:156)
user=> *e
#<ArithmeticException java.lang.ArithmeticException: Divide by zero>

user=> (.getMessage *e)
"Divide by zero"

Note

*e gives you access to the actual plain old Java exception object, so you can analyze and introspect it at runtime.

You can imagine the possibilities of being able to execute and introspect code with this, but what about the tools that we are already used to? How can we use this with an IDE?

Let's check now how to create a new Clojure project, we'll use Leiningen from the command line to understand what is happening.

主站蜘蛛池模板: 庄浪县| 玉屏| 洛扎县| 准格尔旗| 南丰县| 湘西| 郧西县| 阜平县| 东安县| 纳雍县| 元谋县| 定日县| 江安县| 岳西县| 奈曼旗| 大理市| 韶山市| 明溪县| 托克逊县| 剑河县| 南溪县| 芜湖县| 会泽县| 阜宁县| 东乌| 武威市| 贵阳市| 龙海市| 赤峰市| 黔西县| 天全县| 文水县| 岳阳县| 彰化县| 北安市| 南宁市| 晋中市| 砚山县| 阜城县| 宜丰县| 乐昌市|