lisp based static site generator
what is lisp?⌗
Lisp is a language that is known for its simplicity in terms of syntax, so much so that the language can write itself midst evaluation.
Lisp’s syntax is rather simple, all lisp programs are essentially composed of two building blocks: symbol, and list.
A symbol is anything but a list. So anything that has an actual value and does not depend on something else to have value. Integers, Floats, Strings, Characters, and so on are symbols.
A list is defined using '()
, anything inside the parenthesis is considred as an item of the list. So, if you wanted to create a list containing number 1 to 10, you’d do the following: '(1 2 3 4 5 6 7 8 9 10)
.
On the other hand a list without a quote in the start is considred an operation call. Meaning, if you want to printf, you’d do the following: (printf "Hello World\n")
Lists can be contain names of variables, or the values of variables. By putting a backquote behind a list, instead of normal quote, you can embed specific operations or variables.
`(1 2 3 4 ,(+ 4 1) 6 7 8 9 10)
would produce the same result as '(1 2 3 4 5 6 7 8 9 10)
This means that you can generate code via generating lists and evaluating them. The process of generating code is called a macro. Macros can modify parameters and send back dynamic operations.
Lisp’s power is in its extremely simplistic syntax. It is can generate itself code and execute it, virtually no other language can do what Lisp does.
the case for static site generators⌗
I prefer using static site generators as they allow for very fast web site development.
A static site generator typically organizes the website via having different formats to generate actual HTML code. Some extend that ability and generate sitemaps that important for Search Engine Rankings.
You can think of static site generators as sort of libraries that ease the website development process.
Specifically across many websites I stick to Hugo. For me, it is the most reliable, easiest and most performant website generator there is.
However, Hugo has some issues. Not necessarily with behaivor, nor with performance or robustness, no, but a bigger issue with building sites fast.
First, consider how ugly the template engine is. Hugo uses go’s internal templating engine, which is rather unelegant compared to others.
To call functions, print variables, or control execution flow you have to wrap operations around braces. E.g.
<!-- Call Function Print -->
{{ print "Hello World 1" }}
<!-- Create a variable -->
{{ $display := true }}
<!-- Control Execution -->
{{ if $display }} {{ print "It is true. So I am displayed" }} {{ end }}
<!-- Call function print with variable -->
{{ print $display }}
<!-- Piping into operation -->
{{ print "henlo" | toUppercase }} <!-- HENLO -->
Every. Single. Time. You will wrap your operations in braces (specifically two) and you will be miserable happy.
Second, the template engine is weak. Want to define functions? Want to influence behaivour? Perhaps, you want to include files? Well, you are outta luck.
Go’s templating engine cannot be modified within the template engine, no, you have to explicity define functions in Go to use them in the template engine.
The sacrifice for power is made because of safety and performance. However, hugo does not need safety nor does it heavily need performance.
Hugo’s safety is a by-product of using Go’s internal templating engine. Go’s internal libraries emphasize security heavily, and the templating engine is geared towards generation of Server-Side Content. Which is rather wise for its purpose but inefficient for static generated content.
Hugo normally generates websites in milliseconds but that is unnecessary. Even if a website took a minute or two to generate for production, it wouldn’t be a bad thing.
alternative template engine⌗
Lisp is pliable and dynamic. With Lisp, we could skip the hassle of writing html, css, or javascript files all-together and instead use the holy Lisp instead.
Having a full Lisp machine to generate HTML, CSS and Javascript would allow for very easy code minification, bundling assets with pages, global variable access.
Which might sound overkill, until you factor in that you have access to the OS, the network, and anything in between. Anything that Lisp has access to you have access to.
But power is not all I want. I also seek Elegance. Take a look at the following html snippets:
(!doctype html)
(html
(head
(title "Hello World"))
(body
(h1 "I love Lisp!")))
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hello World</title>
</head>
<body>
<h1>I love Lisp!</h1>
</body>
</html>
Lisp’s version is less verbose while providing the same content and it can be even less verbose since Lisp’s syntax depends on S-Expression rather than multilines.
(!doctype html)
; One line document
(html (head (title "Hello World")) (body (h1 "I love Lisp!")))
The same applies for Javascript and CSS. Lisp’s syntax can be easily transformed into those languages, while also providing more elegance, clarity and power.
Preprocessors are not used in Lisp web environments because they are not needed.
The ability to create functions can be used to make components reusable without relying on a MVC.
; racket syntax
(define (component markup css js)
`(component
(,component)
(style [(type "text/css")] ,(s-expr->css ,css))
(script [(type "text/javascript") ,(s-expr->js ,js)])))
(define (head title)
(component `(h1 title) (h1 :color #fff) nil))
a practical application⌗
There is no need for an executable since Lisp machines are widely available and can use packages. Creating an extra static site generator would mean wasting all those valuable packages, or at best, adding more code to an already complete system.
The following quote explains this perfectly:
Any sufficiently complicated static blog generator contains an ad-hoc, informally-specified, bug-ridden, slow implementation of half of make.
A Makefile is more than sufficient. To prevent code repeatition one could import a sort-of global Lisp file. All macros, functions and variables are available right there.
A simple Lisp file with variables that writes to files, or heck even standard output does the job.
All of the libraries you need are available, so why not use a Lisp machine?