Monday, February 18, 2008

My first macro-writing macro

Any trained monkey can write a Common Lisp macro. They're pretty simple. But last night, I wrote a macro that further defines another macro. In about 40-ish lines, I can now access data from my database in a single form:

(with-users ((user-read-many) :name n :details d)
(format t "~a: ~a~%" n d))

That will print out the names and details of all users in the system I'm building. I'm doing this as part of a little web application that I'm building for my own education. The idea is that the above code can be used within HTML page generators to print out what I want from the database.

As far as that macro goes, I would never have succeeded without C-c RET, which triggers SLIME's MACROEXPAND-1. I could be hacking at this macro, and in a split second have the result of using it there, on my screen.

It was some of the slowest code I'd ever written in my life. No, I'm not talking about run-time efficiency, I'm talking about how slowly I was typing code in. Getting my head to operate in two layers of back-ticks is a mind-bending experience.

The SLIME scratch buffer also helped. I wouldn't have even known about the SLIME scratch buffer without the SLIME selector.

The SLIME selector is a simple mode-switcher, containing options to go to the most recent Lisp buffer, or the REPL, or bring up the scratch. This line in your .emacs will enable it:

(define-key global-map (kbd "<f12>") 'slime-selector)

Some people use C-c s instead, but I like the one-key access myself.

So yeah, two-line database access is pretty awesome.

Wednesday, February 13, 2008

Feeling Parenthetical

Any Lisper, regardless of dialect or skill level, recognises that the parentheses are little more than structure for the Lisp reader to handle. Your editor handles parentheses, you handle the code.

Brian Carper recently stumbled across a nice Emacs customisation that will dim parentheses: parenface. With it, parentheses will appear as dim grey instead of black. The things are still there and can be seen and manipulated as per normal, but now your code really stands out. Handy for Lisp programming.

Sunday, February 10, 2008

Terminals? In MY Emacs?

Graphical text editors are fine. I'm okay with them. But when I'm in a terminal, I want something that will edit text while inside that terminal. Previously, this has been vim's task, and to a great extent it still is. But I've been mucking around in Emacs, and recently discovered M-x term.

Not too much to say about it: it's a fully-featured terminal emulator inside Emacs. The main reason I've turned to it was because I realised the version control facilities inside Emacs insofar as git is concerned are inadequate. Branching is a mess, commits for a single set of files appear as multiple commits in the system... git is a lot easier to manage via the command line. I suppose it's not entirely Emacs's fault: its version control system seems closely tied to the anachronism that is CVS. Or maybe I just don't understand it and need more time. The fact of the matter is that it's not working for me at the moment, not in the way I'd like it.

So, git. Most easily interacted with via the terminal. And I like being able to edit text while in those terminals. Since I'm developing a system in Common Lisp inside Emacs, which happens to support a terminal mode, it makes sense for me to do that editing inside Emacs. However, at a normal terminal, I still like editing stuff in vim, because it's quick and easy for me. It's possible and very easy to set this up:

Edit your ~/.bashrc or equivalent to include something like this:

if [ "$INSIDE_EMACS" ]; then
export EDITOR=emacsclient
else
export EDITOR=vim
fi

Then add this to your ~/.emacs

(server-start)

Or just run M-x server-start which does the same thing.

Now if a program invokes the EDITOR, it'll bring up an Emacs buffer while in the terminal in Emacs, which you can save/discard using C-x #, while opting for vim when using the terminal anywhere else.

Wednesday, February 6, 2008

CL web adventures

I've been scratching up on Common Lisp, and what better way to do that than to try building a few web applications. Nothing complicated at the moment, just following a few tutorials online, but I did manage to pull together a simple guest-book in about a page of CL code.

Here's what I've done so far:

  1. Learned how to use Swank as a server for SLIME, so I know how to connect to a Lisp instance running remotely now.
  2. Installed Hunchentoot, CL-WHO, and cl-markdown.
  3. Compiled, installed and configured mod_lisp for Apache.
  4. Started learning to use git.

Getting Swank running once you have SLIME installed is pretty easy. What I've been doing so far is firing up my Common Lisp implementation in a terminal (SBCL for those interested), and then run the following commands:

(require 'swank)
(setf swank:*use-dedicated-output-stream* nil
swank:*communication-style* :fd-handler)
(swank:create-server :dont-close t)

I've memorised the above by heart, but you can just as easily put it in a script and run Lisp in the background.

Thanks to ASDF-Install, grabbing Hunchentoot, CL-WHO and cl-markdown was a breeze. It really is as simple as doing this:

(require 'asdf-install)
(asdf-install:install 'name-of-package-here)

ASDF-Install is awesome enough to download dependencies automatically. Initially, it may complain about GPG keys. You can ignore it, but I decided to look into it anyway, because the message that the debugger raised didn't look too hard.

First I needed to make my own public-private key pair:

gpg --gen-key

I just went with the default values for most of the prompts. Then I generated my public key:

gpg --armor --output pubkey.txt --export 'Tung Nguyen'

Then, whenever ASDF-Install complained about not having a key, I'd do something like:

gpg --recv-keys KEY_ID_FROM_CONDITION_MESSAGE
gpg --sign-key KEY_ID_FROM_CONDITION_MESSAGE

If I was happy that the key was from the author of whatever I was downloading at the time, I'd confirm the prompt at the second command. Unfortunately, the "retry" restart for ASDF-Install at that point won't let you go through, but it's a simple matter of aborting the process and retrying the same installation command to get through.

Next, I had mod_lisp to deal with. I've never installed an Apache module before, heck, I barely knew what one was before this. First I had to download the C source file. The instructions at the mod_lisp site mentioned a tool called "apxs", that would handle the compilation of the C source file into a shared object and then install it. It took me a while to figure out that on my Ubuntu setup, it's not included with the 'apache2' package, but in 'apache2-threaded-dev', which totally isn't obscure at all. Then when I ran this command from the mod_lisp site and restarted Apache, the thing still didn't work.

sudo apxs2 -c -i -a mod_lisp2.c

As it so happens, Apache on my Ubuntu Linux system has its modules arranged into directories for small files at /etc/apache2/mods-available, and enabled modules at /etc/apache2/mods-enabled, which just symlinks into some of the files in the previous directory. I copied one of the existing .load files, referring to the new mod_lisp.so file, and after a bit of trial and error, got the thing running.

From there, configuring mod_lisp was easy. I put this in /etc/apache2/httpd.conf:

# Hunchentoot stuff!
LispServer 127.0.0.1 3000 "hunchentoot"

<Location>
SetHandler lisp-handler
</Location>

I initially had a different path for Location, but the Hunchentoot demo is hard-wired to use that particular path, so I changed it back to "/hunchentoot" so I could try it out. Going back to Swank, I fired up Emacs, ran M-x slime-connect, went with the default values at the prompts, and entered the following:

(asdf:operate 'asdf-load-op 'hunchentoot-test)
(hunchentoot:start-server :port 3000 :mod-lisp-p t)

Pointing my browser to http://localhost/hunchentoot showed the demo, which was good enough for me.

From there, I tried out a couple of web applications from tutorials linked from the Hunchentoot website, before trying a simple guestbook application of my own, which brings me up to this point.

I'm learning git so that I can manage the development of a simple centralised issue tracking system, but I haven't gotten very far yet. I've found it easier to simply enter the git commands directly from shell prompts than from the Emacs hooks, so some reading into how to effectively use the version management facilities in the editor is probably in order. We'll see how things pan out.

Despite how old Lisp is as a language, it's quite surprising how active the Common Lisp community is, and how many useful libraries there are available. Even just scratching the surface like I have, there is no way that anybody can claim that Common Lisp is outdated.