This article is part of a series describing a port of the samples from Practical Common Lisp (PCL) to Clojure. You will probably want to read the intro first.
This article covers Chapter 5, Functions.
One of the cool things about Common Lisp is the variety of ways to pass arguments to functions. You can pass arguments
Clojure provides many of the same things, but the syntax is different. Here is a function that takes two required arguments, and two optional arguments.
Here are some examples of calling foo
:
You can also pass values by name, using a map literal. The function bar
expects named arguments a
and b
. The :or
clause specifies a default for b
:
Examples of calling bar
:
Another nicety is defining an optional parameter's default value in terms of another parameter. Here is a Clojure approach that defaults height
to width
when creating a rectangle:
Usage:
Common Lisp also supports discovering whether a user specified a parameter. This comes in handy if you want to detect that the user re-specified the default for some parameter. I didn't find a built-in way to do this in Clojure. Here is one approach:
The :as :all
collects the entire arguments list into all
. Then, I use contains?
to detect whether the user specified a value for c
.
Usage:
If you wanted to make this feel more like Common Lisp, you could write a macro.
Common Lisp also supports a return-from
macro to "return" from the middle of a function. This encourages an imperative style of programming, which Clojure discourages.
However, you can solve the same problems in a different way. Here is the return-from
example, rewritten in a functional style so that no return-from
is needed:
Usage:
To demonstrate funcall
and apply
, PCL uses a function to plot a histogram. Clojure provides an equivalent apply
. The PCL version of plot
also uses CL's loop
. Instead, I will use doseq
and dotimes
for looping:
The _
is idiomatic for "I don't plan to use this value". In this case I want to do something n times, but the individual iterations do not need to know their ordinal.
With plot
in place, we can plot any function of one argument, over any range. (The usefulness of this is sharply limited by the horizontal resolution of your output device). Some examples:
user=> (plot #(Math/pow % 2) 1 5 1)
*
****
*********
****************
user=> (plot identity 2 10 2)
**
****
******
********
No big surprises here. Function invocation is flexible and powerful in Clojure.
The sample code is available at http://github.com/stuarthalloway/practical-cl-clojure.