I recently started using Org mode for configuring Emacs and I really like it. However, I did run into an issue where, for some reason, org-babel-load-file didn't seem to update the tangled version of the file.

I thought perhaps it was an issue with caching at first, because if I manually tangled the file, everything would be just as I expected. However, if I started another instance of Emacs, it would re-evaluate the org-babel-load-file function, and I would end up with the wrong version of the file again.

I couldn't quite understand what was going on, but found the answer in a stack overflow post that I have sadly lost the link to: In Org mode, the official code for Emacs Lisp is emacs-lisp, but for code blocks, it also accepts elisp. From what I understand, elisp is set up as an alias for emacs-lisp. This seems to apply to manual tangling too, but not to org-babel-load-file.

And sure enough, I'd started using elisp instead of emacs-lisp partway through my config and those blocks wouldn't get tangled by org-babel-load-file. Going through and updating the blocks to use emacs-lisp instead of elisp was enough to get everything working again.

While the fix turned out to be simple, it was hard to find. So I'll leave this here, hoping it saves someone some time and effort in the future.

Remember: use #+begin_src emacs-lisp and not #+begin_src elisp.


70,000 words

A year of writing

Today marks a personal achievement of mine. A year ago, on May 18th 2019, I published the first post in a year-long chain of weekly content.

The decision to put something out every week was motivated in part by the words of Scott Hanselman, who says the most important thing is regularity. It doesn't matter if it's not stellar every time, as long as you're regular. As long as people can expect new content from you at regular intervals, you'll build a following.

Another important factor was this: if you force yourself to create and release content every week for a year, you will get better at it. Not only will you get better at writing and expressing yourself, but you will also learn to accept that things are never perfect and that you can (and must) release something nonetheless. You can't hold on to something until it is perfect, because it never will be. Learning to accept this is a key part of being a creator.

The ups and downs

As with everything else, committing to a weekly writing schedule has its share upsides and downsides. While finding a topic to write about every week is surprisingly easy, doing the research and putting words on the page is surprisingly hard and time-consuming.

The Good

The most obvious positive is that I've gotten a lot of writing practice. I've also gained some exposure within certain programming communities, and I've learned a lot about a wide variety of topics.

The most valuable thing, however, may be that I now have a body of work available on the internet. It may sound silly, but the satisfaction I get from looking back at how far I've come and how much I've produced is not to be understated.

The Bad

Time. Writing takes time. Writing takes a lot of time. Writing takes even more time when you are a bit of perfectionist and want to make sure everything is just right ™.

I estimate that I've spent, on average, about a work day on each post. That makes over four hundred hours in the past year and is equivalent to a little over 10 weeks worth of work, assuming a work week of about 40 hours.

This is a pretty serious cost. I can't remember the last time I had full weekend off. Sometimes you just want to relax, but you can't because you know you have to write that next post. It hasn't always been fun. I haven't always wanted to do this, but I've pushed through.

I've also set myself an increasingly high bar and specialized in a few key areas. I've created a system where it feels as if every post has to be of a certain length and about a certain topic. This has kept me from writing shorter posts and from exploring other topics, such as CSS and the .NET ecosystem, for instance.

Tangible outcomes

What do you get out of pouring all these hours and all this effort into a blog like this? Personal satisfaction and a feeling of accomplishment is one thing, but have I gotten anything tangible in return for my efforts?

Let's see:

Newsletters and reading lists
I've had posts featured on a number of weekly newsletters, including This Week in Rust, Haskell Weekly, and NixOS Weekly. As an avid reader of all of these, it's been a joy to see my name on the lists. I've also often seen links from other reading lists, such as Read Rust.
Rust release notes
And speaking of Rust, my post on advanced slice patterns got a special mention in the release notes for 1.42. Big, proud crab moment 🦀
Freelancer agreements
Off the back of the post mentioned above, I was recently contacted by (and subsequently signed a contract with) an external company to write content for their blog. By definition, I'm now a professional writer.
Being able to look up my own knowledge
More than a few times, I've found myself trying to recall how to use a specific git command or just how awk's syntax works. At times like these, knowing I've written about it previously makes it very easy to quickly find what I've forgotten. This also matches how I've often heard other content creators say that they end up finding their own content when looking up how to do something.
Conference talks
I'm speaking at NDC Oslo this year. I don't know whether the blog played any part in me landing the spot or not, but I like to think that it might have. At the very least, I don't think it hurt.

Stats

In addition to the outcomes mentioned above, I thought it'd be interesting to present some statistics on what I've published so far (before this post).

Total number of published words
71,554
Total number of posts
53
Average word count
1,350
Longest post
Let's Read Haskell Programming from First Principles, part IV: Basic data types (2,866 words)
Shortest post
Rebasing off a repo root (277 words)
Number of unique tags
34
Most used tags
  1. Haskell (15 posts)
  2. Git (10 posts)
  3. Rust (7 posts)
Time spent
400+ hours

Traffic generators

I use Netlify's analytics to get a rough overview over how many page views I get and to see what content gets the most traffic. Based entirely on anecdotal experience from checking my numbers every now and then, I can tell you that Rust is far and away what generates the most views on my blog. At the time of writing, the four most visited blog posts are all Rust posts.

The post on Magit Forge also performed very well and got some pretty good circulation (in relative terms) on Twitter.

Now what?

Now that I've reached my goal and completed the year of blogging, what's next for the blog? Do I just drop it? Do I keep it up? Do I put even more time into it?

I have no intentions of letting the blog go any time soon. However, I do intend to change up the format a bit. I'd like experiment more with shorter posts and different kinds of content.

I also expect not to write much in the next month, as I'd like to focus much more on the talk I'll be giving at NDC Oslo in June. I'd say a little time off is well deserved.

Was it worth it?

Chris Coyier has said that the best way to create a name for yourself online is to write. Write, write, and then write some more. Content is valuable, and you can always offer something new and unique.

Words like these motivate me, and when I look back upon what I've written so far, I'm even more motivated to keep going. I've got so much more to learn and so much more to say.

So yes, it was worth it.

Now excuse me while I go play some video games.


The text `''.split(' ')` and the title of the article

What should a function splitting an empty string return? What about splitting a string on a separator that does not exist within the string? This was the topic of some debate within my team recently, when one of my teammates had assumed that splitting an empty string on a separator would return an empty list. It does not1.

Splitting on a substring that doesn't appear in the string

When you split a string an arbitrary number of times, it's reasonable to expect to find yourself with a list of all the pieces. If you were to join all these pieces back together using the same separator that you used to split the original string, you should arrive back at where you started. In other words, if sep is an arbitrary separator, then the following bit of pseudocode should be true: originalString == originalString.split(sep).join(sep).

The algorithmic approach

Given a string and a separator, we can describe a string splitting algorithm in two, simple steps:

  1. Take everything up until the separator and append it to the list of substrings. If you don't find the separator, add the whole string to the list.
  2. Take what's left of the string after the separator, and go back to step one, using the remaining part of the string as input.

When there is no more string to split, you've completed the substring list and can return that to the caller.

Adding the whole string to the list if you don't find the separator is a crucial part of step one. Without it, you would never get the final piece of a string. If you split "I fell asleep" on spaces, you'd expect to get ["I", "fell", "asleep"] in return. But if you discard a string if it doesn't have the separator, you'd end up with only ~["I", "fell"]~; a very different outcome.

If the separator doesn't appear in the string at all, we simply add the whole string as the first element of the list and consider our work done. This way, the function is internally consistent. As you go through the string, you will always eventually reach a substring that doesn't contain the separator. That substring is also part of the result.

The user experience approach

There's another angle to come at this from: as the end user. What would you, the developer using the function, expect it to return? This is a subjective thing, so I can only really speak for myself, but if I know that if a function always returns data of the same shape (such as a list), I find it much easier to work with.

What if the string doesn't contain the separator at all? Well, then it can't be split. Imagine we're cooking together. I give you some carrots and ask you to cut the tops off. What do you do if one of the carrots has already had the top cut off? You'd probably realize it doesn't need anything done to it, and put it in the done-pile with the others.

Similarly, a string that has no separator (no top) need not be split, and you can just return it as-is.

Splitting an empty string

Splitting an empty string isn't any different than what we've already explored. In fact, it's just a more specialized version of the above problem. It can also come up if the string you're splitting ends with the separator. For instance "juice," split on commas, would be ["juice", ""]. An empty string is still a string.

The empty string as separator

What if the separator is an empty string? This varies from language to language. For instance, in JavaScript, an empty string as separator means 'split the string into a list of characters', while Python throws an exception. Rust's split splits the string into a list of one-character strings (like JavaScript does), but inserts an empty string at the start and at the end (playground link).

What does it even mean to use an empty string as a separator? There's no clear answer here, so it's at your discretion as the function author. For instance, a Haskell-implementation I quickly threw together while writing this post2 returns an infinite list of empty strings. I'm inclined to say that that's reasonable, but it's probably not very useful.

Summary

There's any number of ways to slice a string. Most languages seem to follow the idea that splitting a string gives you back a list of substrings, even if the string is empty or the separator doesn't appear in the string at all. The disagreement is about what to do with an empty separator, but at least they all agree on it being a special case.

If you're feeling the urge come over you after all this, then how about taking a stab at implementing a string splitting algorithm yourself? It's quite the fun, little exercise if you're looking for something to occupy your mind for a bit.

Footnotes

This particular case was in JavaScript, where splitting using an empty string as the separator will actually return an empty list given an empty string (~''.split('')~ returns []), but this is a special case. Most languages (and their standard functions), that I'm aware of, require you to opt in to remove empty strings from the output, though there are outliers.

The implementation is included here for the particularly interested reader:

     split :: String -> String -> [String]
     split sep input =
       go "" (Just input)
       where go before remaining =
               case remaining of
                 Nothing -> [before]
                 Just(s) ->
                   if take (length sep) s == sep
                   then before : (go "" $ Just $ drop (length sep) s)
                   else case s of
                     "" -> go before Nothing
                     (c:cs) -> go (before ++ [c]) $ Just cs

FirstPreviousNextLast