The Blog is the program

Posted  

I decided to do a career change into writing fiction and RPGs, this will be a slow transition and my blog content will eventually shift to be more about creative writing and game design than development.

I enjoy development. I just don’t want to work as a developer anymore. It is an analogous situation to that friend of yours who enjoys cooking but doesn’t want to work in a restaurant.

Programming can still help me with my writing career, and that is how we segue into the topic for this blog post: This blog is a program. I don’t mean I use a program to write this blog, I mean the actual blog is the damn program. Let’s unpack it because I know you’re all inclining your heads.

This is a static website, it is generated using Pollen. Pollen is at the same time a publishing system and a programming language. It is built with Racket, and in Racket the phrase let’s build a new language can be used to answer almost all questions. Unlike most static generators that pick data files and process them to output HTML, the source files on a Pollen-based website are actually source-code that is executed by the Pollen language. Since Pollen is built with Racket, I can use any language or feature from the Racket platform inside my blog posts.

I’ve been working on writing some old-school TTRPG games and trying to immerse myself back into those communities. Yesterday, I posted about how much I like damage charts. That post contains a lot of graphs for damage chart profiles, such as

Example chart

Example chart

These graphs are generated at build time. I created a new Racket module to implement RPG stuff that I plan to reuse in my posts, and added a damage-graph function to it:

(define (html-damage-graph attrs elems)
  (check-required-attributes 'damage-chart '(label damage) attrs)
  (define label (attr-val 'label attrs))
  (define damage-values (attr-val 'damage attrs))
  (define dice-values (range 1 (+ 1 (length damage-values))))
  (define limit-natural (attr-val 'limit-natural attrs))
  (define (zip a b)
    (apply map list (list a b)))
  (make-directory* "img")
  (define path (build-path "img" (~a label (equal-hash-code damage-values) "." 'png)))
  (parameterize ([plot-x-label  "Dice Roll"]
                 [plot-y-label  "Damage"]
                 [plot-title (string-titlecase (~a #:separator " " label "damage"))])
    (plot-file
     (list
      (if limit-natural (vrule
                            limit-natural
                            #:style 'dot
                            #:color 2
                            #:label "Damage For Maximum Natural Dice Roll Value")
                        '())
      (if limit-natural (hrule
                            (list-ref damage-values (- limit-natural 1))
                            #:style 'dot
                            #:color 2)
                        '())
      (lines
       (zip dice-values damage-values)
       #:label (string-titlecase (~a #:separator " " label "damage"))))
     path))

This is plain old Racket using the plot module to draw the graph and save it to a file. Learning how to do it was not hard, a friendly user in the Racket Discourse pointed me in the right direction.

Since my blog is wired in a way that exposes functions named html-* to Pollen, I can call this function in a Pollen-based post as:

A magical spear that excels when held by someone worthy of its magic.
It can be built with a simple damage chart:

◊damage-graph[
	#:label "Spear of the Worthy"
	#:damage '(4 4 6 6 8 8 20)
	#:limit-natural 6
]

That is remarkably similar to the standard spear damage chart from Troika! ...

Each post in the blog is its own source file. When Pollen executes yesterday’s post source file, it finds the calls to ◊damage-graph and executes the code in (html-damage-graph ...) implementation in my little library for each of them.

The Blog is the program. That means it can access all the features and languages Racket has to offer, such as the dice-parser language which knows how to parse strings like 2d6 into results of rolling two six-sided dices.

So while most of the static generators I know are more popular than Pollen, they’re often less flexible. Usually, they only support Markdown and extending the markup is usually quite hard. One is often forced to change the static generator source itself to be able to add or remove markup language features. With tools such as Pollen, you’re supposed to build your own stuff. Pollen is a language, and it is waiting for you to use it.

Sometimes I wonder why I don’t see more people using Pollen, or why I don’t see static generators written in other languages copying the same features and mindset. I guess it is because creating languages is often very time consuming in other languages. Racket is language-oriented development. It is a language for building languages, and because of that, it makes such endeavours easier.

If this sounds interesting to you, I recommend you check out the Beautiful Racket book—also built with Pollen, by the creator of Pollen—and Language-oriented Programming in Racket: A Cultural Anthropology.

Did you enjoyed reading this content? Want to support me?

You can buy me a coffee at ko-fi.

Comments? Questions? Feedback?

You can reach out to me on Twitter, or Mastodon, Secure Scuttlebutt, or through WebMentions.

Mentions