Recur not at tail position - recursion

How can I use something similiar to recurnot at tail position?
Take a look at my code:
(defn -main [& args]
(println "Hi! Type a file name...")
(defn readFile[])
(let [fileName(read-line)]
(let [rdr (reader fileName)]
(if-not (.exists rdr)
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))
(defn list '())
(doseq [line (line-seq rdr)]
(if-not (= "" line)
(concat list '(line)))
(list))))
(defn fileLinesList (readFile))
...
...)
I know I can't use recur here... But I neither know how can I make it in clojure.
I'm a newbie in Clojure and I'm coming from a OOP context. So...
Is there a way to use recursion in this case?
What would be an alternative?

First of all you should not nest your functions definitions in another defn (-main in this case). defn or def always defines symbol bindings at the top level of namespace and they don't nest. If you want to define a locally scoped function you need to use let and fn, e.g.
(let [my-fn (fn [a b] (+ a b))]
(my-fn 1 2))
In your particular case I think it would be easier to split your code into multiple functions. This way it will be more readable.
Prompting for a file name is one piece of your logic.
(defn get-existing-filename []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
filename
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Then you can use it to read a file removing empty lines:
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
For a file with following content:
AAA
BBB
CCC
DDD
it will return
("AAA" "BBB" "CCC" "DDD")
If you really want it as a single function, the following will work:
(defn read-file []
(let [filename (read-line)]
(if (.exists (java.io.File. filename))
(with-open [input (clojure.java.io/reader (get-existing-filename))]
(->> (line-seq input)
(remove empty?)
(doall)))
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur)))))
Finally, this function can be called from -main.
I have also noticed another issue in your sample code:
((println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
if and if-not require a single expression for their then and else branches. If you want to have multiple expressions you need to nest them in do:
(do
(println "Sorry, this file doesn't exists. Type a valid file name...")
(recur))
If you need if or if-not without the else branch then you can use when or when-not macros. Then you don't need to wrap multiple expressions because when/when-not will wrap them for your inside of do.
(when true
(println 1)
(println 2))
is equivalent to
(if true
(do
(println 1)
(println 2)))

Related

using a struct as property list to macro

I have a struct with :name and :value that I'd like to use as arguments to a macro. But I'm not sure how to tell lisp that.
I can write out the call like
(sxql:yield (sxql:set= :name "a" :value 1))
"SET name = ?, value = ?"
("a" 1)
But I'd like to use an already existing structure
(defstruct my-struct name value)
(setq x (make-my-struct :name "a" :value 1))
; #S(MY-STRUCT :NAME "a" :VALUE 1)
using answers from Common LISP: convert (unknown) struct object to plist?
I've made
(defun struct-plist (x)
"make struct X into a property list. ugly kludge"
(let* ((slots (sb-mop:class-slots (class-of x)))
(names (mapcar 'sb-mop:slot-definition-name slots)))
(alexandria:flatten
(mapcar (lambda (n) (list (intern (string n) "KEYWORD")
(slot-value x n)))
names))))
(setq p (struct-plist x)) ; (:NAME "a" :VALUE 1)
My naive attempts are
(sxql:set= p) ; error in FORMAT: No more argument SET ~{~A = ~A~^, ~}
(funcall 'sxql:set= p) ; SXQL:SET= is a macro, not a function.
(macroexpand (sxql:set= p)) ; error in FORMAT ...
I imagine this is an easy/fundamental lisp programming question. But I'm not sure how to ask it (or search for answers). I'm also hoping there is an better struct<->plist story than what I've stumbled across so far.
EDIT: In case this is really an xy-problem. I've used flydata:defmodel to create the struct and I want to insert to a database using the same model.
This is definitely an xy problem: unfortunately I don't understand y (flydata?) well enough to answer the y part.
Here's why what you are trying to do can't work however. Consider this code in a file being compiled:
(defstruct mine name value)
...
(sxql:set= <anything derived from mine>)
Compiling this file must satisfy two constraints:
It does not fully create the structure type mine (see defstruct);
It must macroexpand sxql:set=.
What these constraints mean is that sxql:set= can't know about the structure at the time it is expanded. So any trick which relies on information about the structure must make that information available at compile time.
As I said, I don't understand the y part well enough to understand what you are trying to do, but a hacky approach to this is:
write a wrapper for defstruct which stashes information at compile time (strictly: at macro-expansion time);
write a wrapper for sxql:set= which uses that information to expand into something which makes sense.
Here is a mindless wrapper for defstruct. Note that this is mindless: it can only understand the most simple defstruct forms, and even then it may be wrong. It exists only as an example.
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *structure-information* '()))
(defmacro define-mindless-structure (name &body slots)
(assert (and (symbolp name)
(every #'symbolp slots))
(name slots)
"I am too mindless")
(let ((found (or (assoc name *structure-information*)
(car (push (list name) *structure-information*)))))
(setf (cdr found) (mapcar (lambda (slot)
(list slot (intern (symbol-name slot)
(find-package "KEYWORD"))
(intern (concatenate 'string
(symbol-name name)
"-"
(symbol-name slot)))))
slots)))
`(defstruct ,name ,#slots))
So now
(define-mindless-structure mine
name value)
Will expand into (defstruct mine name value) and, at macroexpansion time will stash some information about this structure in *structure-information*.
Now I stop really understanding what you need to do because I don't know what sxql:set= is meant to do, but it might be something like this:
(defmacro mindless-set= ((s o))
(let ((info (assoc s *structure-information*))
(ov (make-symbol "O")))
(unless info
(error "no information for ~A" s))
`(let ((,ov ,o))
(sxql:set= ,#(loop for (slot initarg accessor) in (cdr info)
;; the compiler will whine about slot annoyingly
collect initarg
collect `(,accessor ,ov))))))
So with this macro, assuming a suitable define-mindless-structure for mine form has been seen by the time the macro is expanded, then
(mindless-set= (mine it))
Will expand into
(let ((#:o it))
(set= :name (mine-name #:o) :value (mine-value #:o)))
But, as I said, I am not sure what the expansion you actually want is.
Finally, before even thinking about using anything like the above, it would be worth looking around to see if there are portability libraries which provide compile/macroexpansion-time functionality like this: there very well may be such, as I don't keep up with things.

Simple Calculator (Addition function input) in Clojure

I'm trying to write a simple calculator with addition, subtraction, etc.
My problem is with getting the user input. How do I turn the string of numerical values into a vector? And also what is a better way to write the program?
(ns scalc.core)
(defn add
[numbers]
(println (apply + numbers)))
(defn numchoose
[]
(println "What numbers?: ")
(let [numbers (read-line)] numbers))
(defn opchoose
[]
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(if (= operation "add")
(do
(println "You chose to add.")
(let [numvect (numchoose)]
(add [numvect]))))))
(defn -main
[& args]
(opchoose)
(numchoose))
And this is the error:
~/clj/scalc 1/7 % lein trampoline run -m scalc.core
What operation would you like to do?:
add
You chose to add.
What numbers?:
5 7
Exception in thread "main" java.lang.ClassCastException: Cannot cast java.lang.String to java.lang.Number
at java.lang.Class.cast(Class.java:3005)
at clojure.core$cast.invoke(core.clj:318)
at clojure.core$_PLUS_.invoke(core.clj:927)
at clojure.lang.AFn.applyToHelper(AFn.java:161)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at clojure.core$apply.invoke(core.clj:601)
at scalc.core$add.invoke(core.clj:5)
at scalc.core$opchoose.invoke(core.clj:21)
at scalc.core$_main.doInvoke(core.clj:27)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.Var.invoke(Var.java:411)
at user$eval15.invoke(NO_SOURCE_FILE:1)
at clojure.lang.Compiler.eval(Compiler.java:6511)
at clojure.lang.Compiler.eval(Compiler.java:6501)
at clojure.lang.Compiler.eval(Compiler.java:6477)
at clojure.core$eval.invoke(core.clj:2797)
at clojure.main$eval_opt.invoke(main.clj:297)
at clojure.main$initialize.invoke(main.clj:316)
at clojure.main$null_opt.invoke(main.clj:349)
at clojure.main$main.doInvoke(main.clj:427)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at clojure.lang.Var.invoke(Var.java:419)
at clojure.lang.AFn.applyToHelper(AFn.java:163)
at clojure.lang.Var.applyTo(Var.java:532)
at clojure.main.main(main.java:37)
EDIT: the solved program now looks like this:
(ns scalc.core)
(defn add [numbers]
(reduce + numbers))
(defn numchoose []
(let [nums (re-seq #"\d+" (read-line))]
(map #(Integer/parseInt %) nums)))
(defn delegate []
(println "What operation would you like to do?: ")
(let [operation (read-line)]
(when (= operation "add")
(println "You chose to add.")
(println "What numbers? ")
(add (numchoose)))))
(defn -main
[& args]
(delegate))
For getting the numbers, you can use re-seq:
(re-seq #"\d+" "123 456 789") => ("123" "456" 789")
You still only have strings rather than numbers though. You can use read-string to get the numbers (read-string is convenient, but not safe in all cases. Here we make sure there are really only numbers in these strings so it's fine).
(read-string "5") => 5
Instead of (apply + numbers) you could use reduce: (reduce + numbers), also your add function really shouldn't print anything (you should try to separate functional functions from side-effecty functions whenever possible).
This (let [numbers (read-line)] numbers) is equal to (read-line). Don't overcomplicate things!
Instead of
(if (= operation "add")
(do ... ))
you can write
(when (= operation "add")
...)
when is just a macro that's useful when you don't need the else case in your ifs (it wraps everything after the condition in a do, and evaluates to nil when the condition evaluates to false).

Can you get the "code as data" of a loaded function in Clojure?

To put it another, way, "Okay, so code is data..."
That thread addresses how to read from a source file, but I'm wondering how to get the s-expression of an already-loaded function into a data structure that I can read and manipulate.
In other words, if I say,
(defn example [a b] (+ a b))
can't I get that list at runtime? Isn't this the whole point of "code as data"?
This is really a general Lisp question, but I'm looking for an answer in Clojure.
You can use the clojure.repl/source macro to get the source of a symbol:
user> (source max)
(defn max
"Returns the greatest of the nums."
{:added "1.0"
:inline-arities >1?
:inline (nary-inline 'max)}
([x] x)
([x y] (. clojure.lang.Numbers (max x y)))
([x y & more]
(reduce1 max (max x y) more)))
nil
But this is only part of the answer. AFAICT source looks up the source filename and line number that define the given symbol, and then prints the source code from the file. Therefore, source will not work on symbols that you do not have the source for, i.e. AOT-compiled clojure code.
Coming back to your original question, you can think of source as reading the meta data associated with the given symbol and simply printing that. I.e. it's cheating. It's not in any way returning "code as data" to you, where with code I mean a compiled clojure function.
In my mind "code as data" refers to the feature of lisps where source code is effectively a lisp data structure, and therefore it can be read by the lisp reader. That is, I can create a data structure that is valid lisp code, and eval that.
For example:
user=> (eval '(+ 1 1))
2
Here '(+ 1 1) is a literal list which gets read by the clojure reader and then evaluated as clojure code.
Update: Yehonathan Sharvit was asking in one of the comments if it's possible to modify the code for a function. The following snippet reads in the source for a function, modifies the resulting data structure, and finally evaluates the data structure resulting in a new function, my-nth, being defined:
(eval
(let [src (read-string (str (source-fn 'clojure.core/nth) "\n"))]
`(~(first src) my-nth ~#(nnext src))))
The syntax-quote line replaces nth with my-nth in the defn form.
You can get the source in recent versions of clojure with the source function.
user=> (source nth)
(defn nth
"Returns the value at the index. get returns nil if index out of
bounds, nth throws an exception unless not-found is supplied. nth
also works for strings, Java arrays, regex Matchers and Lists, and,
in O(n) time, for sequences."
{:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~#nf)))
:inline-arities #{2 3}
:added "1.0"}
([coll index] (. clojure.lang.RT (nth coll index)))
([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
nil
to get the string as a value you can wrap this in with-out-str:
user=> (with-out-str (source nth))
"(defn nth\n \"Returns the value at the index. get returns nil if index out of\n bounds, nth throws an exception unless not-found is supplied. nth\n also works for strings, Java arrays, regex Matchers and Lists, and,\n in O(n) time, for sequences.\"\n {:inline (fn [c i & nf] `(. clojure.lang.RT (nth ~c ~i ~#nf)))\n :inline-arities #{2 3}\n :added \"1.0\"}\n ([coll index] (. clojure.lang.RT (nth coll index)))\n ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))\n"
user=>
That was my message; nice to meet you ;-) BTW, the references given in that thread for answers were excellent reading; so if you're interested, you might want to take the time to read them. Back to your question though source seems to work for code that was loaded through a file, but it doesn't work in all cases. I think, specifically, it doesn't work for functions defined in the repl.
user=> (def foo (fn [] (+ 2 2)))
#'user/foo
user=> (source foo)
Source not found
nil
user=> (defn foo2 [] (+ 2 2))
#'user/foo2
user=> (source foo2)
Source not found
nil
Digging a little bit...
user=> (source source)
(defmacro source
"Prints the source code for the given symbol, if it can find it.
This requires that the symbol resolve to a Var defined in a
namespace for which the .clj is in the classpath.
Example: (source filter)"
[n]
`(println (or (source-fn '~n) (str "Source not found"))))
nil
user=> (source clojure.repl/source-fn)
(defn source-fn
"Returns a string of the source code for the given symbol, if it can
find it. This requires that the symbol resolve to a Var defined in
a namespace for which the .clj is in the classpath. Returns nil if
it can't find the source. For most REPL usage, 'source' is more
convenient.
Example: (source-fn 'filter)"
[x]
(when-let [v (resolve x)]
(when-let [filepath (:file (meta v))]
(when-let [strm (.getResourceAsStream (RT/baseLoader) filepath)]
(with-open [rdr (LineNumberReader. (InputStreamReader. strm))]
(dotimes [_ (dec (:line (meta v)))] (.readLine rdr))
(let [text (StringBuilder.)
pbr (proxy [PushbackReader] [rdr]
(read [] (let [i (proxy-super read)]
(.append text (char i))
i)))]
(read (PushbackReader. pbr))
(str text)))))))
nil
So yeah, it looks like it tries to load the source file off the classpath to try to spit it out for you. One thing I've learned when working with Clojure is that 9 times out of 10 it is useful to look at the source.

How can I use the Clojure REPL together with Qt Jambi?

I have not found a solution to use the Clojure REPL with Qt on the web.
Basically the problem is that the REPL hangs as soon as you call QApplication/exec in order to get the UI to display. You cannot C-c C-c back into the REPL, and closing the active Qt window seems to kill the whole Clojure process.
Now simply calling QApplication/processEvents from within an agent is not possible, unless the agent runs in exactly the same thread in which you created your Qt widgets. It took me two days to figure this out and I have seen others have the same issue/problem but without a solution.
So here is mine, in code:
(add-classpath "file:///usr/share/java/qtjambi.jar")
(ns qt4-demo
(:import (com.trolltech.qt.gui QApplication QPushButton QFont QFont$Weight)
(com.trolltech.qt.core QCoreApplication)
(java.util Timer TimerTask)
(java.util.concurrent ScheduledThreadPoolExecutor TimeUnit))
(:require swank.core))
(defn init []
(QApplication/initialize (make-array String 0)))
(def *gui-thread* (new java.util.concurrent.ScheduledThreadPoolExecutor 1))
(def *gui-update-task* nil)
(def *app* (ref nil))
(defn update-gui []
(println "Updating GUI")
(QApplication/processEvents))
(defn exec []
(.remove *gui-thread* update-gui)
(def *gui-update-task* (.scheduleAtFixedRate *gui-thread* update-gui 0 150 (. TimeUnit MILLISECONDS))))
(defn stop []
(.remove *gui-thread* update-gui)
(.cancel *gui-update-task*))
(defmacro qt4 [& rest]
`(do
(try (init) (catch RuntimeException e# (println e#)))
~#rest
))
(defmacro with-gui-thread [& body]
`(.get (.schedule *gui-thread* (fn [] (do ~#body)) (long 0) (. TimeUnit MILLISECONDS))))
(defn hello-world []
(with-gui-thread
(qt4
(let [app (QCoreApplication/instance)
button (new QPushButton "Go Clojure Go")]
(dosync (ref-set *app* app))
(doto button
(.resize 250 100)
(.setFont (new QFont "Deja Vu Sans" 18 (.. QFont$Weight Bold value)))
(.setWindowTitle "Go Clojure Go")
(.show)))))
(exec))
Basically it uses the ScheduledThreadPoolExecutor class in order to execute all Qt-code. You can use the with-gui-thread macro to make it easier to call functions from within the thread.
This makes it possible to change the Qt UI on-the-fly, without recompiling.
If you want to mess with Qt widgets from the REPL, QApplication/invokeLater or QApplication/invokeAndWait are probably what you want. You can use them in conjunction with agents. Given this:
(ns qt4-demo
(:import (com.trolltech.qt.gui QApplication QPushButton)
(com.trolltech.qt.core QCoreApplication)))
(def *app* (ref nil))
(def *button* (ref nil))
(def *runner* (agent nil))
(defn init [] (QApplication/initialize (make-array String 0)))
(defn exec [] (QApplication/exec))
(defn hello-world [a]
(init)
(let [app (QCoreApplication/instance)
button (doto (QPushButton. "Go Clojure Go") (.show))]
(dosync (ref-set *app* app)
(ref-set *button* button)))
(exec))
Then from a REPL:
qt4-demo=> (send-off *runner* hello-world)
#<Agent#38fff7: nil>
;; This fails because we are not in the Qt main thread
qt4-demo=> (.setText #*button* "foo")
QObject used from outside its own thread, object=QPushButton(0x8d0f55f0) , objectThread=Thread[pool-2-thread-1,5,main], currentThread=Thread[main,5,main] (NO_SOURCE_FILE:0)
;; This should work though
qt4-demo=> (QApplication/invokeLater #(.setText #*button* "foo"))
nil
qt4-demo=> (QApplication/invokeAndWait #(.setText #*button* "bar"))
nil
I've written about how to do this with SLIME on my blog (German) as well as on the Clojure mailing-list. The trick is to define appropriate functions on the Emacs side and tell SLIME to use those when making requests. Importantly, this frees you from having to do special incantations when invoking Qt code.
Quoting myself:
Given that we're talking Lisp here,
anyway, the solution seemed to be
obvious: Hack SLIME! So that's what I
did. The code below, when dropped
into your .emacs (at a point at which
SLIME is already fully loaded),
registers three new Emacs-Lisp
functions for interactive use. You
can bind them to whatever keys you
like, or you may even just set the
slime-send-through-qapplication
variable to t after your application
has started and not worry about key
bindings at all. Either should make
your REPL submissions and C-M-x-style
interactive evaluations indirect
through QCoreApplication/invokeAndWait.
Have fun!
(defvar slime-send-through-qapplication nil)
(defvar slime-repl-send-string-fn (symbol-function 'slime-repl-send-
string))
(defvar slime-interactive-eval-fn (symbol-function 'slime-interactive-
eval))
(defun qt-appify-form (form)
(concatenate 'string ;'
"(let [return-ref (ref nil)] "
" (com.trolltech.qt.core.QCoreApplication/invokeAndWait "
" (fn [] "
" (let [return-value (do "
form
" )] "
" (dosync (ref-set return-ref return-value))))) "
" (deref return-ref))"))
(defun slime-interactive-eval (string)
(let ((string (if slime-send-through-qapplication
(qt-appify-form string)
string)))
(funcall slime-interactive-eval-fn string)))
(defun slime-repl-send-string (string &optional command-string)
(let ((string (if slime-send-through-qapplication
(qt-appify-form string)
string)))
(funcall slime-repl-send-string-fn string command-string)))
(defun slime-eval-defun-for-qt ()
(interactive)
(let ((slime-send-through-qapplication t))
(slime-eval-defun)))
(defun slime-repl-closing-return-for-qt ()
(interactive)
(let ((slime-send-through-qapplication t))
(slime-repl-closing-return)))
(defun slime-repl-return-for-qt (&optional end-of-input)
(interactive)
(let ((slime-send-through-qapplication t))
(slime-repl-return end-of-input)))

"Unable to resolve symbol" error

When I paste this code into a REPL, it works fine:
(use 'clojure.contrib.seq-utils)
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
Now, I have this code with :gen-class :implements [my.ServiceInterface] and a function prefixed with - to implement the interface. I unit-test with Maven/Groovy/TestNG. Everything works fine with several other interfaces/Clojure implementations, but in this particular case, I get this error:
java.lang.RuntimeException:
java.lang.Exception: Unable to resolve symbol: random-letter in this context (NO_SOURCE_FILE:32)
I can't figure out why. The only thing I can tell is different in this function from all the other functions, is that this is the only place where I use quoting, i.e. '((random-digit)) and '(random-letter). EDIT: also, this is the only place where I use eval.
I tried defining the functions as "non-private" (defn instead of defn-). I also tried a (declare random-digit random-letter) at the top. Neither of these solves the problem.
On a side note, if you have a suggestion for a better way to implement the random-password function, I am all ears. But I'd still like to know why I am getting this error and how to get this to work.
Many thanks in advance for your help. Clojure is awesome.
Edit: here is the complete code.
(ns fred.hp2010.service.ClojurePoolerService
(:gen-class :implements [fred.hp2010.service.PoolerService])
(:use [clojure.contrib.seq-utils :only (flatten)]))
(def dao (fred.hp2010.persistence.Repository/getDao))
(declare find-by is-taken random-password)
(defn -addPooler [this pooler] (. dao insert "POOLER" pooler))
(defn -getPoolers [this] (. dao list "poolers"))
(defn -isEmailTaken [this email] (is-taken {"email" email}))
(defn -isUsernameTaken [this username] (is-taken {"username" username}))
(defn -login [this email password] (. dao findSingle "POOLER" {"email" email "password" password}))
(defn -changePassword [this email new-password]
(let [updated-pooler (assoc (into {} (find-by {"email" email})) "password" new-password)]
(. dao update "POOLER" "POOLER_ID" updated-pooler)))
(defn -resetPassword [this email]
(let [new-password (random-password)]
(-changePassword this email new-password)
new-password))
(defn- find-by [params] (. dao findSingle "POOLER" params))
(defn- is-taken [params] (not (nil? (find-by params))))
(defn- random-letter [] (char (+ (rand-int 26) 97)))
(defn- random-digit [] (rand-int 10))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(let [password (interpose '((random-digit)) (repeat 3 (repeat 2 '(random-letter))))]
(apply str (flatten (map (fn [coll] (map eval coll)) password)))))
I don't know why you're having problems compiling this with :gen-class but I wouldn't be surprised if eval had something to do with it. eval is usually a bad idea. One thing to try (completely untested) is to use ` (backquote) instead of ' (quote) so that your symbols are namespace-qualified. Don't know if that'd help or not.
Probably better to get rid of eval though. If you turn your random-character functions into infinite lazy seqs via repeatedly you can do it this way:
(defn- random-letter [] (repeatedly #(char (+ (rand-int 26) 97))))
(defn- random-digit [] (repeatedly #(rand-int 10)))
(defn- random-password
"Returns an 8-character password consisting of letters and digits as follows: aa1aa1aa"
[]
(apply str
(mapcat (fn [[n f]] (take n (f)))
[[2 random-letter]
[1 random-digit]
[2 random-letter]
[1 random-digit]
[2 random-letter]])))
In the top handful of lines, I'm having a bit of trouble following the syntax. In particular, why all the quotes in line 7? Delayed evaluation of all those expressions is probably not helping you. I would guess that the quoted '(random-letter) is spoiling your fun.
You can probably write simpler code while eschewing eval. I'm going to go try it in the REPL, I hope to be back soon with an improved version.
EDIT:
OK, this works:
(apply str (interpose (random-digit) (repeat 3 (apply str (repeat 2 (random-letter))))))
...and it doesn't need anything from clojure.contrib :)
The str function will mung any arguments together into a string. If the arguments are in a list, you smuggle str inside the list by using apply.
As you said, Clojure is cool!
EDIT:
Here's a function that generates a random string of alphas and numerics in accordance with a String specification:
(apply str (map (fn [c] (if (= c \a) (random-letter) (random-digit))) "aanaanaa")))
It deviates a little bit from your spec but I think it's pretty cool.

Resources