Tag Archives: ns-unmap

Functions in Clojure

In our Hello World program in Clojure, we saw one function, the println function, which prints its argument to the standard output. The heart of any Clojure program is, of course, the ability to write your own functions, so we’ll have a look at how that is done here. We’ll start with functions that do relatively simple things, such as arithmetic.

Java programmers are used to the idea of defining a function (or method as they are usually called in OO programming) by defining a name, argument list, and return type for their function. For example, a function that calculates the square of an integer might have the signature

int square(int number)
{
  return number * number;
}

In Clojure, the process is roughly the same, although the syntax is quite different. One key point is that a Clojure function actually has no name; however for it to be useful (that is, for you to be able to call it), it is almost always bound to a symbol that effectively gives it a name. The syntax for a Clojure function equivalent to the Java one above is

(defn square
         "Squares its argument"
         [number]
         (* number number))

Like everything in Clojure, a function definition is a list of forms enclosed in parentheses. The form defn is a special form (a keyword of the language) which begins a function definition. The next form is the symbol (square) to which the function is to be bound. Following this is an optional string which documents the function. As with all coding, it’s a good idea to document your code even if you plan never to let anyone else see it. It’s amazing how fast your own memory of what a function does will fade.

The next form is a list of arguments for the function. Note that this list is enclosed in square brackets: [number]. A list in square brackets is a vector, of which we will say more in another post. A function can have any number of arguments, including zero (in which case this form is just empty brackets: []). As with all lists, elements within the list are separated by blanks, so if the function had two arguments, this would be written as [arg1 arg2].

The last element in the list is the code which is run when the function is called. In this case we want to square number, so the code is (* number number). The * here is just another symbol, rather than a specialized operator as in Java, and it calls a built-in function which multiplies its arguments. (Actually, * can take any number of arguments so you could multiply together 4 numbers by writing (* n1 n2 n3 n4).)

A statement returns the value of the last element in the list, so in this case the result of the multiplication is returned.

To run this code, create a new Netbeans project called Square, with a namespace of glenn.rowe.square. Add the code above after the ns statement, so the full program looks like this:

(ns glenn.rowe.square
	;(:import )
	;(:require )
)
(defn square
         "Squares its argument"
         [number]
         (* number number))

Start up a REPL but don’t use Netbeans’s menu to load anything just yet. At the prompt, try calling your function. You’ll get the following error:

user=> (square 4)
#<CompilerException java.lang.Exception: Unable to resolve symbol: square in this context (NO_SOURCE_FILE:2)>

The problem is that since the file hasn’t been loaded, the square function is unknown to the REPL.

Now try loading the file by right clicking on the project name and selecting ‘REPL->Load all Clojure files in Source Packages’. Now try calling the square function again. You’ll still get an error:

user=>
#'glenn.rowe.square/square
user=> (square 4)
#<CompilerException java.lang.Exception: Unable to resolve symbol: square in this context (NO_SOURCE_FILE:4)>

What’s going on? The clue lies in the response you got when you loaded the source file. The REPL’s response was the mysterious string “#’glenn.rowe.square/square”. This gives the fully qualified name of the function it found in your source file; that is, the symbol name ‘square’ preceded by the namespace you defined when you created the project, and which appears in the ns statement at the start of the file.

The problem is that the namespace currently being used by the REPL is not the namespace defined in the file. In fact, it’s a default namespace which is called ‘user’, and that’s where the ‘user’ in the prompt comes from: it tells you which namespace the REPL is currently operating in.

Try calling the function by giving it its full name:

user=> (glenn.rowe.square/square 4)
16

Aha! This time we have success. However, it gets to be a bit of a pain having to type in the namespace every time we want to call a function. Fortunately, there are a few ways around this. One way is to change the namespace in which the REPL is operating by entering the following line at the ‘user’ prompt:

user=> (ns glenn.rowe.square)
nil
glenn.rowe.square=>

The ‘ns’ command tells the REPL to change its namespace to “glenn.rowe.square”, and you can see that after entering this command, the prompt in the REPL has changed to reflect the new namespace. Now try calling the square function using just its name:

glenn.rowe.square=> (square 4)
16

This time it works without having to type in the namespace.

If you don’t want to change the REPL’s operating namespace, there is another way to get access to the square function without having to type its namespace name every time. Return to the ‘user’ namespace by entering (ns user). Next, enter a use statement as follows:

user=> (use 'glenn.rowe.square)
nil
user=> (square 4)
16

The use statement, followed by the name of a namespace (the quote before the name is required) tells the REPL to include all the symbols defined in that namespace within the current ‘user’ namespace. Thus ‘square’ now becomes directly accessible so we can call it as shown above.

There’s an obvious danger with doing this, of course. If we use several namespaces within our operating namespace, no two of these namespaces can contain the same symbol; if this happens we get a collision of names, and the REPL will generate an error. However, for testing purposes, it’s usually a safe thing to do.

A couple of other commands are useful to know about before we leave this introduction to functions.

When you’re writing code, you frequently need to change things and then test them by running the result. However, the REPL won’t know about any changes unless you tell it reload the file. For example, suppose we added a new function called cube which returns the cube of its single argument. The file now looks like this:

(ns glenn.rowe.square
	;(:import )
	;(:require )
)
(defn square
         "Squares its argument"
         [number]
         (* number number))

(defn cube
         "Cubes its argument"
         [number]
         (* number number number))

Entering the code will not update the REPL, so the cube function will still be undefined. In Netbeans using Enclojure, the easiest way is to type Alt+L. This will save the file and load all the changes into the REPL.

If you’re running the REPL from a command console, you can reload a namespace by typing

user=> (use :reload-all 'glenn.rowe.square)
nil

Note that this won’t work in the Enclojure REPL, so just use the Alt+L shortcut (it’s a lot faster than typing in that line anyway).

Finally, it’s useful to know how to delete a symbol from the REPL’s map. If you try deleting the code for square from the source file and then reloading it, you’ll find that you can still call square from the REPL. The reason is that loading or reloading a file will just add new symbols or modifications to existing symbols, but it won’t delete symbols that are no longer in the source code. To do that, there is a special command called ns-unmap. Suppose we want to delete the square symbol. Try the command

user=> (ns-unmap 'glenn.rowe.square 'square)

The ns-unmap function’s first argument is the namespace from which to remove the symbol, and the second argument is the symbol to be removed (both prefixed by a quote).

Now if you try calling square you’ll get an error. However, if you had previously given a (use ‘glenn.rowe.square) command, there will still be a square symbol in the REPL’s map. You can see this by typing square on its own (without parentheses) into the REPL. You’ll get a response that looks something like this:

user=> square
#<square$square__304 glenn.rowe.square$square__304@1677737>

If square were completely undefined, you’d get an error, but this cryptic string shows that there is still a reference to square stored in the REPL’s map.

This is because the square in the namespace glenn.rowe.square was copied into the ‘user’ namespace. When you unmapped square from glenn.rowe.square, you didn’t remove the copy from ‘user’. However, you are unable to call square since the copy in ‘user’ pointed to the original function symbol in glenn.rowe.square, and since the original is now gone, the function can’t be called.

To completely remove the square symbol you need to use a second call to ns-unmap:

user=> (ns-unmap 'user 'square)

Now all trace of square is gone.

Advertisements