SXML is Good
I've been interested in SXML for a while. I just finished writing my own image gallery generator using SXML, and I have to admit… Guile doesn't exactly have amazing SXML documentation, so I decided to write a bit about it.
SXML, or "Symbolic XML" is a lispy, and dare I say, more intuitive way of writing XML documents. For example, instead of writing the following snippit in XML:
<bird> <type>Chickadee</type> <name>Joe</name> </bird>
You could simply write the following SXML expression:
(bird (type "Chickadee") (name "Joe"))
If you take a look at the above snippits, you'll realize that SXML is very much like XML. You just don't need explicit closing tags for SXML. This comes naturally to Lisp and it would be nice if XML was like this to begin with, but that's okay.
One problem with SXML though, is the documentation. Every implementation is a little different, and since it's not exactly a hot topic, not a lot of people are writing about it.
I'm going to use the
(sxml simple) module in Guile, which comes
with plenty of tools to help us get comfortable with SXML.
We'll start off by writing a "Hello world" XML document to a file.
(use-modules (sxml simple) (let ((port (open-output-file "test.xml"))) (sxml->xml '(p "Hello SXML!") port) (close-port port)))
In this example we define
port, which is a file named
(sxml->xml) lets us specify the port we write to, so we
tell SXML to write to our file.
test.xml should now caontain the line
<p>Hello world!</p>. You can
also transform XML to SXML with
Because SXML is really just XML, all you need to know are the oddities and differences of SXML. For example, attributes in SXML are different:
(bird (@ (type "Chickadee") (name "Joe"))) ;; <bird type="Chickadee" name="Joe">
honestly, I think it would have been better if attributes were Guile tags instead, for example:
;; Incorrect code. This will not run (bird #:type "Chickadee" #:name "Joe")
I don't know how Guile tags work on a low level, though, so I can't complain. Abstractions
Because Guile's SXML module is part of Guile, you can use the power of Guile to make your life easier through abstractions!
For example, You can use Scheme variables in SXML if you use a
(define name "Jimbo") (sxml->xml `(p "Hey there, " ,name)) ;; <p>Hey there, Jimbo</p>
Variables can come in handy when you want your user to be able to customize a lot of things!
(define me '((name "Spongebob") (title "My Work Blog") (message "Welcome to my website!") (best-friend "Patrick")))
You could then use association lists for every sublist defined in
I tend to use custom functions as shortcuts for myself, so I don't need to write out loooong strings of SXML all the time:
(define (a link string) `((a (@ (href ,link)) ,string))) ;; Now instead of writing *this* all the time: (a (@ (href "https://muto.ca")) "My site!") ;;I can just slap down: ,(a "https://muto.ca" "My site!")
I know I'm taking this a bit far, but I like abstraction, so let's continue.
You can create a post template for all your files, and create new files based on it easily:
(define (post title date body) (let ((port (open-output-port "post.html"))) (display (string-append "<!DOCTYPE html>\n" "<html>\n" "<head>\n" "<title>" title "</title>\n" "</head>\n" "<body>\n" "<h1>" title "</h1>\n" "<h2>" date "</h2>\n") port) (sxml->xml ,body port) (display (string-append "</body>\n" "</html>") port)))
We can use this function like so:
(post "My Test Post" "May 14, 2020" '((p "Hello and thanks for checking out this blog post!")))
I enjoy writing in SXML, although I wish it had a standard
design. Every implementation is a little different. For example,
Guile's SXML doesn't include support for
<!-- comments -->, so I had
to modify the existing module just so I could have comments!
I still need to get used to SXML. I really enjoy it and I feel that, with enough time and the right abstractions, I could have a blast with this little module!
Anyway, since this post may leave you wanting more, Here's a list of resources that could help you find your way in the mystical world of SXML: