The last time Hackerfall tried to access this page, it returned a not found error. A cached version of the page is below, or clickhereto continue anyway

guile-lips: Scheme as a generic macro language - rbryan

This blog thing is finally picking up! I would consider this to be one of my more polished projects. There are still some warts on it that I don't particularly like, but I've done my best to make it as clean and modular as possible. This project was inspired by another similar project that you can find here so if you prefer common lisp to scheme go check it out. The idea is pretty much the same, but (I think) my version is cleaner and more modular. My version is also written in guile rather than common lisp.

By Example

The idea here is simple. You write whatever sort of text file as you normally would, except scheme expressions following a ~ will be evaluated and printed. If you want to write a '~', just write "~~". It's that simple. Lets jump straight into an example.

One of the most useful uses for a macro system is to add features to a language to fit it to your uses better. As an example, lets fix one of the things that annoys me most about Java, import statements. They always end up being verbose and repetitive.

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.awt.Color;

Look at that. Yes, the repetition is kind of mesmerizing; it does look fairly tidy once you're finished typing it all out. I write enough as it is though. I don't want to have to type out java.util.* half a dozen times every time I make a new file. Let's clean this up a bit. I think it should look more like this:

~(java-import '((java
			(util
				HashMap
				HashSet
				Map
				Random
				Set
				UUID
				(concurrent
					ExecutorService
					Executors
					Callable
					Future))
			(awt
				Color))))

Much better! My eye's were starting to get sore looking at all those jagged semi-colons! So, How do we make this happen? It's as simple as writing a function that takes a tree of symbols and prints appropriate import statements:

(define (java-import specification)
		(letrec ((recurse  ;;future Russell thinks he should have used a named loop here
			   (lambda (spec-string spec-list)
			     (for-each (lambda (spec)
					 (if (list? spec)
					   (recurse (string-append
						      spec-string
						      (symbol->string (car spec)) ".")
						    (cdr spec))
					   (begin
					     (display (string-append
							"import "
							spec-string
							(symbol->string spec) ";"))
					     (newline))))
				       spec-list))))
		  (recurse "" specification)))

How easy was that!!? You can use this in a few ways. You can make a macro file and include it in your project loading it as you would any scheme code. You can put it in your lipsrc.scm file in $HOME/.config/lips/lipsrc.scm or you can do what I did and make a module and put it in $HOME/.config/lips/modules. lips will look here by default when using a standard configuration.

git clone https://github.com/rbryan/guile-lips
cd guile-lips
./install.sh   #If you don't read this first you're a fool.
lips < test.txt

Copyright 2016 Russell Bryan

Continue reading on rbryan.github.io