Header logo.
small hallucinations
homeyearstagsaboutrss

So I wrote a new static-site generator

I used to generate this blog from Markdown files using my own static site generator written in Python. The resulting HTML files are then hosted on Cloudflare Pages.

At some point late last year, the Python codebase stopped running smoothly because of dependency issues. And I don't remember tweaking my system or Python version, or adding or removing these libraries.

At first, the Markdown parser I used went missing. I had to install it again in a new virtual environment. Then the feed generator complained that an argument was missing in a function call. (These kinds of minor problems will just crop up.)

So I decided to rewrite the SSG behind this blog in Go.

The whole process was pleasant. GitHub Copilot was very helpful. It suggested which dependencies I could use to parse Markdown and to generate RSS feeds (goldmark and gorilla/feeds). It suggested code completion that was often helpful. A few suggested solutions used deprecated function calls but were educational nonetheless.

When you program, old habits from other languages are carried over. Here are a few things I learned while being nudged by Copilot:

  1. Go doesn't support string interpolation with variable names. And there is a whole discussion about it.
  2. Go doesn't support optional args in the function signature. Instead, you can use a variadic function or use an options struct.
  3. I tried to import a struct from the main package in a child package. And that was a dumb idea. Because “importing the main package directly can lead to circular dependencies, which are not allowed in Go.” Instead, I could “move the BlogSetting type to a separate package, which can then be imported by both the main and template packages.”

Using large language models in, you guessed it, language learning

I remember in late 2022, Vibhakar Sarswat told an audience of dozens of developers at a Flutter Stockholm event that he began building his mobile app with the help of GitHub Copilot after finishing a tutorial, and he described GitHub Copilot as “a good teacher.” I chuckled in disbelief.

My early impression of GitHub Copilot was that it was more clumsy than helpful, but it quickly improved and made me more productive. Now in addition to Copilot, I use ChatGPT every day and other gen AI tools quite often too.

Apart from generating code, asking technical questions, making quick queries of syntax and features, I'm using these tools for learning natural languages.

And I find ChatGPT helpful in this very particular way:

ChatGPT converts Chinese characters to Japanese kanjis for me, even when I mistake them for a wrong cognate.

For example, I was trying to read a book about a Japese artist when it mentioend her moving to Berlin. The original sentence was:

ベルリンに拠点を移した

At the time I did not recognize “拠” and thought mistakenly it must be related to 处 (chù).

I tried asking ChatGPT this question:

Explain this sentence to me: ベルリンに 点を移した。

Notice I used the wrong character here. The character shown in bold is only used in China.

The sentence “ベルリンに 拠点 を移した” translates to “moved the base to Berlin.”

Here’s a breakdown of the sentence:

• ベルリン (Berurin): Berlin, the capital city of Germany.

• に: A particle indicating the direction or destination of the action, meaning “to” here.

• 拠点 (kyoten): Base or base of operations; it refers to a main location where someone works or operates.

• を移した (wo utsushita): The past form of the verb 移す (utsusu), meaning “to move” or “to transfer.”

So, this sentence indicates that someone or an organization has relocated their main base or operational headquarters to Berlin.

Note how ChatGPT corrected the sentence and converted it to the correct Japanese kanji 拠 (kyo).

Other non-AI dictionary apps would not be able to translate this sentence, break it down, and explain it word by word because 处点 is not a word in either language.

Some dictionary apps might be able to convert correctly if I had written 据点 (jù diăn), the correct equivalent of the same word in Chinese, but if I had known that, I wouldn’t have needed to look it up.

Copy and paste on Ubuntu

On a Mac, if you press Command-C while not selecting anything, nothing happens. I didn't realize this was a behavior that had been designed and implemented by someone until I did the same on Ubuntu.

On Ubuntu, what happens when you press Ctrl-C while not selecting anything is two things:

  • "\n" is copied to your clipboard; and
  • whatever that has been in your clipboard is overwritten.

I'm saying as if this is an Ubuntu problem. But I don't really know. It is most likely handled by Gnome alongside (in my case) X11 according to Reddit.

And you would think the clipboard is a solved problem since it was invented in 1973.

“Asia/Beijing” doesn’t exist

I was writing a Python script last week and the ORM threw a tantrum, asking for an “offset-aware” datetime.

ChatGPT (GPT-4o) suggested I use the zoneinfo package and simply specify the name of the timezone.

Naturally, I assumed the timezone name for Beijing would be “Asia/Beijing” and asked GPT-4o for confirmation.

What is the timezone name for Beijing: Asia/Beijing

To be sure, I asked again. (I missed a question mark in my first question, which might have been caused an issue.)

But this didn't work.

Because the timezone for most part of China (+08:00) is actually called “Asia/Shanghai”. Shanghai is the most populous city in mainland China after all.

When I asked again the next day, GPT-4o gave me the correct answer: “Asia/Shanghai”.

So, either hallucinations are random, or the first time ChatGPT thought I was asserting rather than asking.

Recent readings

How to Work with SQL Databases in Go

This post is a good point of reference that clarifies how you can use transactions and contexts while working with SQL databases in Go. The author of this post, Jia Hao, also shares his learnings about algorithms and Elixir on his website.

Web Development with Go

This course is pretty good. Go has a rather straightforward syntax. When you want to build something using Go, there are a lot of options to choose from at every step. Which framework should you use? Do you need an ORM? How shoud you manage schema changes and run migrations? In addition, the internet will always tell you to “just use the standard library” because it already does everything well.

In this course, the author will teach you his preferred way to do things. What's equally, if not more, valuable is he explains background knowledge, technicalities, compromises that he mas made while making technical choices.

If you find the price tag intimidating, scroll down and you'll find a PDF version that is sold at a lower price.

Fixing addon bugs for Anki 23

I've been using Anki for years. Anki has recently changed its version numbers to year.month. From version 23.10, FSRS algorithm is implemented for scheduling reviews. Without thinking too much, I upgraded to the latest version.

Not surprisingly, this upgrade broke a few addons on my computer:

  1. Syntax Highlighting for Code
  2. Mini Format Pack
  3. Zoom 2.1; and
  4. Customize Keyboard Shortcuts

Fixing them was easier than I had thought.


When you start Anki, you are greeted with this alert if there's a bug in an addon.

By clicking “copy debug info”, you'll have some clue around how to move forward.

This is the first clue for fixing Syntax Highlighting for Code:

Anki 23.12.1 (1a1d4d54)  (ao)
Python 3.9.15 Qt 6.5.3 PyQt 6.5.3
Platform: macOS-13.0.1-arm64-arm-64bit
When loading Syntax Highlighting for Code:
Traceback (most recent call last):
# ...
  ".../Application Support/Anki2/addons21/1463041493/consts.py", line 22, in 
    addon_path = os.path.dirname(__file__).decode(sys_encoding)
AttributeError: 'str' object has no attribute 'decode'

By following this path, we can fix this bug by simply changing line 22 into something like this:

1anki21 = version.startswith("2.1.") or version.startswith("23")

The same change fixed Mini Format Pack too.


The reason why Zoom 2.1 stopped working is it imports Qt5 when Anki is using Qt6 now.

Long story short, go to the addon folder and change the import section in __init__.py to this:

1from PyQt6.QtCore import Qt
2from PyQt6.QtGui import QKeySequence, QAction
3from PyQt6.QtWidgets import QMenu

While debugging, you'll need to check the actual value of variables. The way I did it is: dump a value to JSON, create a new error that includes this JSON and raise this error.

The downside is you have to quit Anki and start it again over and over. Luckily it didn't take me too much time to fix the first three addons.

I only scratched the surface of Customize Keyboard Shortcuts as the logic inside it is a bit complicated. If you want to fix it, here's something you can start with:

The constant Qt.Key_Enter is no longer valid. You'll need to use Qt.Key.Key_Enter.

Something clever in the syntax of Elixir

I find this quite clever in the syntax of Elixir.

There are two sets of boolean operators in Elixir and, or, not versus &&, ||, !.

The difference is that and, or, not only take literal boolean values. In contrast, &&, ||, ! can take values of all types. And all values except false and nil will evaluate to true.

When you think about it, it makes a lot of sense. Because literal boolean values -- true and false -- are words. It's only natural that they go with words: and, or, not.