Thursday, 8 May 2008

Renaming files in Common Lisp.

So I ran across this while going through my morning catchup routine and thought it could do with some clearing up.

The part of the post I'm actually interested is this.


When Haskell can compete on those types of problems, it'll be easier to induce people to learn it. (Same with CL, my fav language....)

Now, bonus points for proclaiming CL as being his favorite language, but minus 10 billion for continuing the meme that CL is somehow unsuited for writing[1] these kinds of simple software (for whatever definition of simple you have).

One of the examples given is "I have a bunch of files, and I want to rename them all according to some pattern." and as it so happens translate-pathname[2] makes this wonderfully simple.


(defun rename-files (from to)
(dolist (file (directory from))
(rename-file file (translate-pathname file from to))))

And thats it, 3 lines of code, or for our simple testing purposes

(defun show-rename-files (from to)
(dolist (file (directory from))
(format t "Renaming ~A to ~A~%" file
(translate-pathname file from to))))

(show-rename-files "/usr/share/pixmaps/*.xpm" "/usr/share/pixmaps/backup-*.xpm")

The funny thing is that this isn't secret knowledge but is pulled straight from the Hyperspec (see the examples).

----
[1]: Please note, I said 'writing' them, not 'creating a 2k binary' of them, please people CL /= Unix.
[2]: Granted, the behavior of translate-pathname isn't specified in detail by the spec but that doesn't mean we cannot use it.

This post brought to you by Lispworks 5.1, clisp 2.41 and SBCL 1.0.12

Sunday, 10 February 2008

Case Conversion Considered Useful

One of the stranger[1] features of Common Lisp is that the CL reader, by default, will convert symbols to uppercase[2] which can be a little surprising to newcomers, and inevitably leads misconceptions regarding case (in)sensitivity.

One of the advantages of this automatic case conversion is that it allows us to use case as syntactic markers.

Here's a simple example (ignore the non idiomatic use of CL).

(defun filter (test list)
(let ((result ()))
(dolist (elt list result)
(when (funcall test elt)
(push elt result)))))

Did you notice the return form?
This happens to be one of those constructs that is quite easy to overlook when reading through code, it doesn't happen often but it does happen[3].

However, if we change it to this.

(defun filter (test list)
(let ((result ()))
(dolist (elt list RESULT)
(when (funcall test elt)
(push elt result)))))
the result form 'leaps' out of the page which makes it very difficult to miss.

It's the code equivalent of wearing a silly hat.

---

1: Stranger, as in, 'This isn't like C/Java/Python/Ruby'.
2: This is only the default and can be changed using readtable-case
3: Well it happens to me, ok.

Tuesday, 5 February 2008

The 'pre Arc' Arc

Seeing that arc is the word on everyones lips I thought I'd take a quick peek.

In Summary.
- data in the functional position is interesting.
- [] function syntax is neat (and almost as nice as the #L syntax in iterate)
- foo:bar for function composition reads quite nicely (once you get out of the CL mindset)

The rest is kind of 'meh' and then i realized, wait a second, a Lisp1 with more Lisp than Scheme and unhygienic macros,  I've seen that before and I had, it is called rep.  This is the Lisp that powers sawfish which (and someone correct me if I'm wrong) was the default window manager for GNOME for quite some time.

This was actually the Lisp that I cut my teeth on way back in 2001 (Grief, I can't believe it was that long ago)  and looking back on it brings back some fond memories and reminds me how full featured it was (especially for something at version 0.17), to list some of them:

  • Profiler
  • Module system (modelled on Scheme48)
  • Tail call elimination
  • Byte Compiled
  • First class Continuations
  • Regexes
  • Threading
  • Good access to OS 
  • Ability to load .so files
  • Bindings to mysql
  • Built in documentation via the ,desc operator

and Most importantly it has apropos, god knows why arc is missing this.

Friday, 26 October 2007

Ruby Quiz 144 (in CL)

There was a small posting on c.l.l asking if anyone felt like completing Ruby quiz 144 in CL and after eric's solution I thought I'd give it a go.

This needs :cl-ppcre and :alexandria (and :lisp-unit to run the tests)
Apologies for formatting screwiness.



(defpackage :time-window (:use :cl :cl-ppcre :alexandria)
(:export #:in-window-p))

(in-package :time-window)

;(defparameter *window* "Sat-Mon; Mon Wed 0700-0900; Thu 0700-0900 1000-1200")

;; This works by converting a spec (like the one above) into it's seperate components (split by ;)
;; and converting each spec into distinct time and day parts (expanding day ranges as we go)
;; so the above would be converted into the following
;; ((nil ("Sat" "Sun" "Mon"))
;; (("0700-0900") ("Mon" "Wed"))
;; (("0700-0900" "1000-1200") ("Thu")))
;; We can then walk through each converted spec ensuring that the time specified falls into
;; the time/day specified.

(defun in-window-p (time window)
(some (lambda (spec)
(every (lambda (part)
(if (null part)
t ;; since (some (constantly t) ()) is nil
(some (curry 'time-within time) part)))
spec))
(mapcar (lambda (split)
(group-by-type (split " " (string-trim " " split))))
(split ";" window))))

(defun group-by-type (list)
(loop for spec in list
:when (time-range-p spec) :collect spec :into times
:when (single-day-p spec) :collect spec :into days
:when (day-range-p spec) :append (days-of spec) :into days
:finally (return (list times days))))

(defparameter *days*
'(("Mon" . 0) ("Tue" . 1) ("Wed" . 2) ("Thu" . 3) ("Fri" . 4) ("Sat" . 5) ("Sun" . 6)))

(defparameter *day-ring* (let ((list (copy-list *days*)))
(setf (cdr (last list)) list)
list))

(defun single-day-p (spec)
(assoc spec *days* :test 'string=))

(defun day<-spec (spec)
(cdr (single-day-p spec)))

(defun day-range-p (spec)
(and (= (count #\- spec) 1)
(every 'single-day-p (split "-" spec))))

(defun days-of (spec)
(assert (day-range-p spec))
(destructuring-bind (start stop) (split "-" spec)
(loop :for (day . nil) :in (member start *day-ring* :key 'first :test 'string=)
:collect day
:until (string= day stop))))

(defun single-time-p (spec)
(every 'digit-char-p spec))

(defun time-range-p (spec)
(and (= 1 (count #\- spec))
(every 'single-time-p (split "-" spec))))

(defun time-in-range (hour minute start stop)
(flet ((to-mins (x) (+ (* 60 (parse-integer (subseq x 0 2)))
(parse-integer (subseq x 2 4)))))
(<= (to-mins start) (+ (* 60 hour) minute) (1- (to-mins stop)))))

(defun time-within (time spec)
(multiple-value-bind (sec minute hour date month year day)
(decode-universal-time time 0)
(declare (ignore sec date month year))
(cond ((single-day-p spec) (= day (day<-spec spec)))
((time-range-p spec)
(apply #'time-in-range hour minute (split "-" spec))))))


;;; and a small test package
(defpackage :time-window.tests (:use :time-window :lisp-unit :cl))
(in-package :time-window.tests)

;; Tests
(define-test window-tests
(let ((window "Sat-Sun; Mon Wed 0700-0900; Thu 0700-0900 1000-1200"))
(flet ((in (sec hour date month year)
(in-window-p (encode-universal-time 0 sec hour date month year 0) window)))
(assert-false (in 0 8 25 9 2007))
(assert-true (in 0 8 26 9 2007))
(assert-false (in 0 11 26 9 2007))
(assert-false (in 59 6 27 9 2007))
(assert-true (in 0 7 27 9 2007))
(assert-true (in 59 8 27 9 2007))
(assert-false (in 0 9 27 9 2007))
(assert-true (in 0 11 27 9 2007))
(assert-true (in 0 11 29 9 2007))
(assert-true (in 0 0 29 9 2007))
(assert-true (in 59 23 29 9 2007))
)))

(define-test window-tests2
(let ((window "Fri-Mon"))
(flet ((in (date month year)
(in-window-p (encode-universal-time 0 0 0 date month year 0) window)))
(assert-false (in 27 9 2007))
(assert-true (in 28 9 2007))
(assert-true (in 29 9 2007))
(assert-true (in 30 9 2007))
(assert-true (in 1 10 2007))
(assert-false (in 2 10 2007)))))

(run-tests)





Nothing flashy but it does remind me how useful circular lists can be.
Comments, as always, are welcomed.

---
This post brought to you by Lispworks 5.0.2

Saturday, 21 July 2007

Closures + Lambda < CLOS

There's a blog post floating around the web[1] on how Closures + Lambda make up all the OO programming tools you'll ever want or need. Now this is a long running theme[2] in the Lisp family and really is a testament to how flexible Common Lisp (or any other language which shares these traits) is

.... however ....

the majority of these are for pedagogical purposes[3] and should never be seriously
compared with a fully fledged object system. It's seems to be in vogue to consider
CLOS elephantine[4] and complex but the truth is that at the surface CLOS is wonderfully simple.

We have classes with slots (read instance variables) created with defclass, we create
instances of these classes with make-instance. We access (and change) the slots of these
instances using slot-value and create methods for the classes using defmethod.

Simple, yes? Not complex or elephantine, Yes?

You can read more of course (all in the CLHS) and discover the object initialization protocol
and how the classes of objects can be changed on the fly, you can find out how to customize
the initialization of your classes and add accessors to your slots.
You can dig deeper and you'll discover the MOP and discover how to change the behaviour of
slot access and class definition.

But remember that you do not need to understand any of this in order to
define classes, create instances, access slots and define methods!

So next time you are rolling your own Object System because someone considers CLOS too slow,
or too large, stop and give CLOS a try, you may just like it.


--------------------
[1] : See here and here .
[2] : And is implemented in PAIP and On Lisp among others.
[3] : For a full OO implementation in CL see KR which is a prototype based object system with valuepropogation.
[4] : Or big, slow[5] or klunky
[5] : This myth has officially been debunked.

Wednesday, 18 July 2007

Lisp in Lisp

One of the fantastic things about Common Lisp is discovering how you can write parts of the language in the language itself and I'm not talking about implementing number parsing or some library function but rather a languages control structures. A nice and relatively simple one, is implementing handler-case[1] in portable Common Lisp[2].

(defvar *handlers* () "Alist of condition name to handler")
(defvar *old-handlers* () "Var to save the bindings of *handlers*")

(defclass root-error ()
((message :initarg :message :accessor message-of :initform "Unknown"))
(:documentation "Our base error class."))

(defun raise (class text)
"signals an error of class CLASS with message TEXT."
(let ((handler (get-handler class)))
(if handler
(invoke-handler handler (make-instance class :message text))
;; This is our 'we crash now'
(error text))))

;; We implement our handlers as functions.
(defun invoke-handler (handler class)
(funcall handler class))

(defun get-handler (class)
"Finds the first handler on *handlers* which is registered with a class
which CLASS is a subtype of."

(cdr (find-if (lambda (handler) (subtypep class handler))
*handlers* :key 'car)))

(defun add-handlers (&rest handlers)
"Takes a list of (class . handler) forms and creates a new
list which can be used as *handlers*"

(append handlers *handlers*))

;;; And all that is left now is to implement trycatch

(defmacro trycatch (form &body error-bindings)
(let* ((block (gensym "BLOCK"))
;; turns each handler into a list of (tmpvar classname handler-fn)
;; its important that we save the state of *handlers* to prevent
;; using the handler bindings we are a part of if we signal an error
;; from within a handler.
(binds (loop for (name args . body) in error-bindings
collect (list (gensym) name `(lambda ,args
(let ((*handlers* *old-handlers*))
(return-from ,block (progn ,@body))))))))
`(block ,block
(let* ((*old-handlers* *handlers*)
;; binds our tmpvar to the handler-function
,@(mapcar (lambda (bind) (list (first bind) (third bind)))
binds))
;; and add (classname . handler-fn) to *handlers*
(let ((*handlers* (add-handlers ,@(mapcar (lambda (bind)
`(cons ',(second bind) ,(first bind)))
binds))))
,form)))))

;and we now raise and catch errors
(trycatch (raise 'root-error "foo")
(root-error (c) (format t "WE GOT AN ERROR ~A" c)))

(defclass my-error (root-error) ())

(defun test-my-error () (raise 'my-error "Whoops!~%"))

(trycatch (test-my-error)
(my-error (c) (format t "Great it works!~%")))

(trycatch (test-my-error)
(root-error (c) (format t "And subtyping works too~%")))

(trycatch (trycatch (test-my-error)
(root-error (c) (raise 'my-error "new-error"))
(my-error (c) "INNER MY-ERROR HANDLER"))
(my-error (c) "OUTER MY-ERROR HANDLER"))

;; should return "OUTER MY-ERROR HANDLER"


Next step handler-bind[3].

-------------------------------


[1] : http://www.lispworks.com/documentation/HyperSpec/Body/m_hand_1.htm
Slow and renamed to trycatch for namespace reasons.
[2] : hmmmm, is this the start of recursive Greenspunning?
[3] : http://www.lispworks.com/documentation/HyperSpec/Body/m_handle.htm

-----
This post is courtesy of lispworks(5.0.2), emacs22 and lispdoc

Friday, 22 June 2007

Advertising is good

Lifted from Akita's interview

I think that Ruby have almost all the power I'm used to from Common Lisp. [sic]
- Ola Bini

Unsolicited advertising from a well respected individual, 'nuff said.