Alrought, here's what I've cooked up. I'm not sure whether this is any better than CatDancer's original -- I was doing it more as a highly applied introduction to Arc :)
I copied out the code, commented it (spending about 80% of the time between www.arcfn.com and arc.arc), and then made two main changes. One is that I rewrote 'json-unicode-digits to only pass over the 4 chars once, rather than three times -- I did this by dissecting 'firstn, and putting the error checking forms inside there.
The other major change is the macro. I noticed that three functions used a similar pattern (the last one was a slight stretch, but after all, a 'let is a lambda deep inside).
My code:
(def hexdigit (c)
(or (<= #\a c #\f) (<= #\A c #\F) digit.c))
(def json-backslash-char (c)
(case c
#\" #\"
#\\ #\\
#\/ #\/
#\b #\backspace
#\f #\page
#\n #\newline
#\r #\return
#\t #\tab
(err "invalid JSON backslash char" c) ) )
;; (The above two are verbatim from CatDancer's code
;; Now comes some modified stuff)
(def json-unicode-digits (j)
(let u ((afn (n xs)
(if (is n 0) nil
(or (no xs) (no:hexdigit (car xs)))
(err "need 4 hexadecimal digits after \u")
(cons (car xs) (self (-- n) (cdr xs)))))
4 j)
(coerce (int (coerce u 'string) 16) 'char) ) )
(mac def-jsniffer (name c next (o arg 'cdr.j))
`(def ,name (j)
(if (is car.j ,c)
(,next ,arg))))
;; ... because it sniffs ahead at the next char.
(def-jsniffer match-json-unicode-escape #\u
[list (nthcdr 4 _)
(json-unicode-digits _)])
(def-jsniffer match-json-backslash #\\
[do (if no._ (err "missing char after backslash"))
(or match-json-unicode-escape._
(list cdr._ (json-backslash-char car._)) ) ] )
(def-jsniffer match-json-string #\"
(fn ((j a))
(list j (coerce rev.a 'string)))
((afn (j a)
(if no.j (err "missing close quote"))
(if (is car.j #\") (list cdr.j a)
(iflet (j c)
(match-json-backslash j)
(self j (cons c a))
(self (cdr j) (cons (car j) a)) ) ) )
cdr.j nil))
Well, I hope that my experiment might have some useful ideas for you!
(mac def-jsniffer (name c next (o arg 'cdr.j))
`(def ,name (j)
So we have three answers to the question, what to do if you're passing some state through a lot of functions, to the extent that passing it around is taking up as much code as what you're actually doing?
1. Put all the functions inside a surrounding lexical scope, and keep the state in some lexical variables that all the functions can access.
2. Write some macros so that you're still passing the state through all your functions, but the variables don't appear in your code.
3. Use some kind of dynamic binding (like Scheme parameters) so the functions can access the state without it having to be passed in as part of the function's arguments.