The Right Lang for the Job

Exploring the abbyss of non-mainstream programming languages

Under Construction!

The blog is going through a rewrite of its generator code

Created: · Updated:

In short:

Longer version: this blog was previously generated with a custom Python-based generator. I wrote it as an excercise - at the time, I was fascinated with functional programming, and I used the blog generator to play with functional features in Python, leveraging mainly funcy library. The code turned out weird, but it worked, and I used it for years. However, the whole workflow was optimized for publishing loooong and complex blog posts, and for every post, setup time was substantial.

I realized that I need to eliminate as many barriers to publishing posts as possible if I ever want to start publishing shorter content more often. I didn't care for a long time, but recently, finding time to finish those looong and complex posts became challenging. So, I decided to take a stab at integrating the generator into my normal workflow. In other words, I decided to rewrite the whole thing in Emacs Lisp.

I researched a bunch of existing solutions, the ones that appeared maintained:

But none of them worked for me out of the box. Plus, my previous custom code did a bunch of smart things that I'm not yet sure how to replicate in Org, and of course none of the packages provide them.

I took inspiration from weblorg, mostly, and in under 100 loc I managed to write something that works just enough to post this information. It will take another weekend or two to bring back all the features I want, and then some more time to convert old posts, but it's going to happen.

Just FYI, if you wondered where the content disappeared to: it will be back!

1. Works

  • basic templating with templatel
  • using Org keywords as metadata (#+TITLE:, #+SUBTITLE:, #+DATE:, etc.)
  • index page with all posts rendered, ordered by date
  • post pages, one post per page

2. To be done

Make <TAB> select the other Dired window

Replicating the best feature of Sunrise Commander in plain Dired

Created:

Some time ago, Sunrise Commander - a two-pane file manager for Emacs, built on top of Dired by Drew Adams - stopped working for me. The loss of familiar bindings was a little painful, but I mostly solved it with Hydra - the functionality is mostly still there in Dired.

One thing I missed was swithcing to the other Dired window easily. With just two Dired windows occupying a whole frame just M-x other-window worked well, but with more windows, I had to fall back to M-x windmove-* and that was less convenient1.

I decided to fix it at some point, and here's the result:

 1: (defun my-select-other-dired-window ()
 2:   "Select the other dired window, if there is only one."
 3:   (interactive)
 4:   (let* ((selected-window (selected-window))
 5:          (other-windows (cdr (window-list))))
 6:     (cl-loop for w in other-windows
 7:              if (with-current-buffer (window-buffer w)
 8:                   (derived-mode-p 'dired-mode)) 
 9:              collect w into result
10:              finally
11:              (when (= 1 (length result))
12:                (select-window (car result))))))
13: 
14: (keymap-set dired-mode-map "<tab>" #'my-select-other-dired-window)

Now I can select the other Dired buffer by pressing <TAB> in one of them, no matter how many other windows are there.

Footnotes:

1

Testing footnotes

How badly can a simple Kotlin function be written?

An abomination lurking deep in a production codebase...

Created:

This is a real gem, found in the middle of a fairly large codebase for more than two years:

 1: object SomeFileUtils {
 2:     fun getFilePath(context: Context, fileName: String, @RawRes resId: Int): String =
 3:         File(context.filesDir, fileName)
 4:             .let { file ->
 5:                 if (file.canRead() && file.length() > 0) {
 6:                     file.absolutePath
 7:                 } else {
 8:                     context
 9:                         .resources
10:                         .openRawResource(resId)
11:                         .use { inputStream ->
12:                             file
13:                                 .outputStream()
14:                                 .use { outputStream ->
15:                                     inputStream.copyTo(outputStream)
16:                                 }
17:                             file.absolutePath
18:                         }
19:                 }
20:             }
21: }

It's so wrong that it's almost beautiful… Almost.

The person who originally wrote this code must have been a little strange in the head, which isn't that unusual in this industry. The reviewer of this code who agreed to merge it - if they even existed - should probably reflect on how serious they are about doing their job, but well - it happens to the best of us. A single fuckup like this is nothing too unusual.

The real tragedy starts after that, though. Code is read much more often than it is written, so it had to be read by other programmers in the time it existed. Especially since many different programmers worked on a project, most of them only for a short time. Now, not one of these people took it upon themselves to eradicate this monstrosity, even though it would be 5 minutes of work. Here's how could it have looked like:

 1: import com.companyname.utils.using
 2: fun createFileForResource(context: Context, fileName: String, @RawRes resId: Int): File {
 3:     val file = File(context.filesDir, fileName)
 4:     if (file.canRead().not() || file.length() == 0L) {
 5:         val resource = context.resources.openRawResource(resId)
 6:         using(resource, file.outputStream()) { input, output ->
 7:             input.copyTo(output)
 8:         }
 9:     }
10:     return file
11: }

I'll start by appealing to authority (because I'm just as lazy as those that will, no doubt, persecute me with Uncle Bob quotes):

Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function. – John Carmack

I happen to agree, and it seemed like it was the case in this particular instance, so that's the first change to the code.

Switching to the block body was obvious, since that's the only way to declare vals in direct function scope, and it looked like giving a name to a subexpression could be helpful.

Flipping the condition not only highlighted the exact conditions (explicitly) in which the function does something special, but also allowed the use of just an if statement and dropping the use of else block. It's now clear that, no matter what happens, this function will return a File object - there's only one return statement, and it's explicit, making it really hard to miss.

Simultaneously using more than one Closeable instance is a pretty common thing to do, and deserves its own helper function. You can find using implementation on Github - it's boring, schematic code, so writing it yourself wouldn't be too fun. It's a single file liberally licensed utility that you can just copy and paste to your utils module.

Finally, since stringly-typed code is an abomination that should be eradicated from the face of the Earth, we return the File object itself, rather then just the path to it. The calling code knows better in what form it wants the file to be: if it's for printing, file path is OK, but if you wanted to read it, you'd have to create a new File object based on the returned path. It's always better to leave the representation decisions to the caller, and it pays off to return the most universal representation you have access to.