7c0h

Latest Post

Adventures in making

There is a recurring joke about how the longer you work as a programmer, the stronger the urge to quit the industry and go live in a farm. I'm not at that point yet but I do have a soft spot for making physical things with my own two hands that, no matter how crooked, I can hold proudly and say "I did this". It is no surprise then that I'm a fan of the Maker Movement, the idea of figuring out how to do stuff on your own rather than purchasing it. I've been using my free time this year to build quite a lot of stuff, and yet 3D printing is the only one I wrote about.

Today's post is an attempt at remediating that. I don't expect you to like them all, but if you're looking for a new hobby then here's a list of hobbies a nerd can enjoy.

Drawing

I have a problem with drawing: I've been doing it for long enough that I'm objectively good at it (no just "good enough", but "good") and yet without a project at hand I just... don't. I had some success with small comics I drew during particularly depressing meetings and I've been chasing that high ever since.

A simple comic I drew about safety trainings.

A friend and I are currently looking into writing a comic together, so hopefully I'll have more to tell in the coming months. I would have liked to join a comic contest as our North star, but those are hard to come by when you're an adult.

I don't want to write much more about the topic because I already did it once.

Painting

I started taking painting classes about a year ago after realizing that you can learn from videos how to add purple shadows to portraits but you can't ask why you are adding purple shadows to pink skin. My drawing skills have helped with the first steps, but becoming the next Rembrandt will still take a while.

Painting of a crocodile.

My main shock about painting was the stark difference between me just "going with it" versus having a specific goal: I can get good results whenever I paint for fun (or, more accurately, when following the tasks my teacher gives me) but getting a precise result is still a lost cause - the least I care, the better the result. And while I understand why this happens, I don't yet have a working solution.

My current main challenge with painting is the cleanup: I still haven't processed the trauma of that one night when paint splashed everywhere and I spent the next hour on my knees chasing tiny paint spots on the floor of my rented apartment. I know I can't get better without practice, but sometimes the apprehension is just too much.

Sewing

I have a general rule: if I face the same problem three times, and the problem is salient enough that I remember all three occasions at once, then I need to figure out how to solve it for good. October found me in this specific situation when I realized that I had

  • One nice shirt that's too big for me,
  • One summer shirt that's too big for me, and
  • One summer hoodie design that I've been wanting to have for almost twenty years.

My first idea was to learn sewing by hand. I wouldn't recommend it as a long-time strategy, but hand-making my own sweatshirt taught me a lot about why a sewing machine does what it does and when is hand stitching the faster, simpler alternative.

The next natural step was to get a sewing machine. The one in my local library is eternally on loan, but luckily second hand sewing machines in my area are surprisingly cheap. The difficult part was finding a good first project: I checked every beginner book in every library I know, and yet all of them start with projects like a skirt, a top or a dress, none of which would be very useful for me. In the end my hand-sewn sweatshirt had to be redone when the material failed to shrink as promised and having to redo all of that gave me some good practice.

Having now resized one shirt, one t-shirt, one sweatshirt and having made a summer hoodie from scratch I believe I'm ready to tackle the nice shirt. I've been toying with some stupid ideas after that, but whether they end up being fashion or cosplay I cannot say.

Music

I never really stopped playing music, but my piano skills have not evolved much in the last ten years. Fate put recently an old guitar into my hands for repair, and this gave me a good chance to give the habit a bit of a refresh - I learned how to play guitar when I was 12, and I am glad to report that I still keep the muscle memory.

The weird part was singing - I stopped playing guitar because I was really bad at singing, and yet I am now at the point of my life in which I get to understand two things:

  • Singing is not that difficult once you understand that your voice and the music have to follow each other
  • As long as your singing isn't atrocious, no one really cares

I tried to write an app for showing a song and its corresponding tab, but the problem is harder than I thought: the whole song doesn't fit in a phone screen, you cannot click the screen to move on (both your hands are busy), and coordinating the text and music automatically is a tough problem. I considered real-time speech detection, but I'm not sure there really is interest for that as guitar players are notoriously uninterested in formalizing their art. I'm currently sticking to the tried-and-tested hand-written booklet until I find a better solution.

Penmanship

On the summer of 2002 I stopped writing cursive - I was taking my first Math courses and realized that formulas only make sense in block letters. My block handwriting declined steadily until 2014 when I decided to do something about it, but cursive remained forgotten until last year.

It was quite shocking to realize that I no longer remembered how to write some uppercase letters, particularly one that's part of my own name. I therefore made a conscious effort to start writing cursive more often, I looked into different types of scripts, and nowadays my cursive is nicer than it ever was - I'm still not as good as some teenage girls I know, but I'm getting there.

Make-up

I have no interest in wearing make-up on my day-to-day - in fact I wish less people would do it. But during Halloween, Karneval or similar? If you think of it as "applied painting" it turns out to be a lot of fun.

Picture of me in skull makeup

I believe my skull makeup is on point (I really need to find a new tube of cracked-skin white base) and my Picasso was also not that bad, but where to go from there is harder. I tried checking in social media, but the most interesting ones are meant for the camera and fall apart the second you look away. Latex prosthetic seem promising, so I'll probably be trying that for next February.

The way forward

I have a couple ideas I want to try next year. Sculpting in wax seems fun (messy, though) and woodworking (even messier) has definitely made it through my "three problems" threshold. And if I had a backyard you can bet I'd have a project boat underway - did you know you can just download instructions to make one?

I won't pretend that I'm particularly good at most of these hobbies, but I am fine and that's what matters. Mastery is a life-long process, but making your own t-shirt takes a couple days (about a week if you want welt pockets).

So go ahead and give any of them a try. Write a comic on post-its, draw a bad portrait, play some chords on a guitar and make a wax tiki lamp. Programming is a lot of fun, but remember that specialization is for insects.

Older Posts

Therac-25, LLMs and the Zen of Python

If you are a professional software developer you probably heard at some point the cautionary tale of the Therac-25, a radiation therapy machine that gave massive overdoses of radiation to at least six patients in the 80s and killed at least three.

There are several layers of problems explaining how all of this came to be, but today I'd like to focus on a specific thread. The (in)famous race condition in the Therac-25 was caused by two simultaneous bugs, but these bugs were not new: the software for the Therac-25 was ported from a previous version of the machine (the Therac-20) and those bugs were already present there. The key difference being that, while the Therac-20 had hardware interlocks preventing those faults, the Therac-25 engineers decided to implement them exclusively in software.

I'm digging this story up because today I read yet again someone suggesting that using ChatGPT to rewrite code from one programming language to another is a great idea. I disagree: a programming language is an opinionated way on how to better achieve a certain task and switching between world views without understanding how and why they do things the way they do is a recipe for inefficient code at best and weird bugs at worse. Simple example: if you're a Python programmer who came from C then you probably wrote loops like this:

# C programmer version
for i in range(len(mylist)):
    item = mylist[i]
    f(item)

# Pythonic code
for item in mylist:
    f(item)

This is a very simple example that highlights a fundamental misunderstanding of the language. The first code will run just fine, but it's generally not the proper way of writing Python code. For a more "real-life" example you can read the introduction to Learn Rust With Entirely Too Many Linked Lists where the author points out that writing linked lists in Rust is, from a Rust point of view, overall a bad idea and something to avoid entirely.

Real-case LLM

I decided to test my theory with Google's Gemini - I've seen students using it in their actual coding (probably because it's free) making it a fair choice. I asked the following:

Convert the following code from Python to Elixir:
def change_eight(my_arr):
    my_arr[8] = None

And the answer was:

def change_eight(my_arr) do
  if length(my_arr) >= 9 do
    List.replace_at(my_arr, 8, nil)
  else
    my_arr
  end
end
# This Elixir code ensures that the index 8 exists in the list before
# attempting to replace the value with nil. If the index is out of bounds,
# the original list is returned unchanged.

In my original code passing a list with less than 9 elements would throw an IndexError exception, which is consistent with The Zen of Python principle "Errors should never pass silently". But if my code relies on the exception to detect that a precondition is not being met, this rewrite has now ensured that the error will not be detected because the exception won't be triggered.

I added more context to the prompt, passing this code instead:

def change_eight(my_arr):
    my_arr[8] = None

if __name__ == '__main__':
    mylist = list(range(10))
    try:
        change_eight(mylist)
    except IndexError:
        print("Precondition failed")

    mylist = list(range(20))
    try:
        change_eight(mylist)
    except IndexError:
        print("Second precondition failed")

which lead to this rewrite:

def change_eight(my_arr) do
  if length(my_arr) >= 9 do
    List.replace_at(my_arr, 8, nil)
  else
    raise RuntimeError, "Precondition failed"
  end
end

if __MODULE__ == __ENV__["__MODULE__"] do
  mylist = Enum.to_list(0..9)
  try do
    change_eight(mylist)
  rescue RuntimeError -> error
    IO.puts(error.message)
  end

  mylist = Enum.to_list(0..19)
  try do
    change_eight(mylist)
  rescue RuntimeError -> error
    IO.puts(error.message)
  end
end
# This Elixir code is similar to the previous response, but it includes two
# test cases to demonstrate how the RuntimeError is raised in different scenarios.

The new version of the function is marginally better: Gemini now throws exceptions where expected, but it has also hard-coded the first error message - if the code fails on the second call the user will be shown an error corresponding to the first. I later asked for a C version instead of Elixir, at which point the error messages where gone entirely.

This is how critical bugs are born: when your software depends on (reasonable!) assumptions about the world and you send it through a pipeline (be it human or automated) that has no idea about what those preconditions are. And if you send your code through a system that's been designed to understand code exclusively at a syntactic level then you don't get to be surprised when the resulting code is not semantically equivalent to your original one.

Final words

Software development is not just about writing code. Software development is about understanding the environment in which your code runs and the decisions that lead to it - some of them reasonable ("this condition can never happen, the hardware will catch it"), some of them arbitrary ("let's write it in Perl"). The Therac-25 incident was made possible because someone decided to use code on an unfamiliar environment without considering the repercussions, the same way that Gemini did not consider "The Zen of Python" nor my error reporting strategy while rewriting my code.

There is more to software development than "data comes in, data comes out". Thinking about systems in terms of the context in which they run (software, hardware and social) is the best way to avoid finding yourself one day unpleasantly surprised.

Or, perhaps more relevant, unpleasantly shocked.

Further reading

If you haven't already, consider giving the classical paper "Four dark corners of Software Engineering" a try.

Calculator texts

Some weeks ago HN user wonger_ posted this list of words to Hacker News. This is a list of the words you can spell using only an upside-down calculator - the word "boobies" (5318008) is perhaps the most well-known, but it's far from the only one.

This comment by user chriscbr went a step further and annotated all words according to their Part-of-Speech while this one by user jprete raised the bar: can you do a long work of fiction only using calculator words?

I spent some time trying to make the longest possible text, and this post is a long, complicated way to say "probably not".

Randall Munroe of XKCD fame published some years ago this article on how to write texts using only a subset of letters - he was interested in phone keypads, but there's no reason why his code can't be adapted to our task. The basic idea is that of a trigram model: you train a model to predict "given these two words, this is an ordered list of the words that have the highest probability of coming next", you restrain your words to those that fit your constraint (in our case, letters that can be mapped to a calculator), train the model with some data, and you're done.

After downloading Randall's code, updating it for Python3 and making it slightly more efficient I trained it with as much Wikipedia text as I had patience for. The exercise gave some interesting word combinations, although nothing resembling a coherent long work of fiction:

  • Be less (would make a nice parody of "Be best")
  • Oh Ohio hills
  • High heel shoes
  • I'll go see his leg
  • His shoe size is big
  • He is obese she is his size
  • He is high, so is his boss, so is she

The next step was to use a more capable language model, and for that we move onto LLM territory. The idea is straightforward: during generation an LLM will look at the input words and make an ordered list of the most probable next words in the sequence. Usually we want to use one of the top words, but nothing stops us from using the most likely words that only uses a specific set of characters. We need to do this by hand because LLMs are incapable of backtracking - if they have generated "Hegel oozes ego" and then realize that they've written themselves into a corner, there's no way for them to backtrack and try something different. As a result sooner or later they all choose a word that doesn't fit the instructions, which is where we come into play.

Writing the prediction code was straight-forward, but punctuation was an issue: we do want to keep some punctuation for the text to look natural, but at the same time the phrase "Oil... Oil... Oil... Oil..." is more likely than something like "Hillbillies besiege his soil". And this meant that neither GPT-2 nor LLaMa 2 could generate anything long, although some short phrases ended up being interesting enough:

  • Bob is eligible.
  • He is 2 eggs high. See his gills.
  • He is 90. She... she is his lobolo.
  • Hello Bill, Bob is his big ol' goose.

At the end, the best approach for me was to go back to the beginning, use the annotated list of words and build phrases by hand. This led me to the short-story:

Bob sees Zoe boil his sole beige goose egg, sighs. She giggles.

HN user araes used ChatGPT and, after some sentences that didn't stick to the prompt (color me surprised) eventually generated the text:

Ellie sells loose shoes; Bill shills big goose oil.

Not one to be outdone, HN user bcherny used Claude 3.5 to obtain:

Ellie sees Bill. Bill sells bibles. Ellie lies, "Bill, sell bibles else." Bill sells bibles. Ellie gobbles bibles. Bill obsesses. Ellie sighs.

Which I now counteract with my own:

Leslie obliges. She'll go see Giles' bill "Oh, Ohio hills". She seizes Giles' high heel shoes, giggles. "Bozo, go sell blisses".

Or, if you want something even longer and feel like reading some really weird prose:

Giles sells big oil. Loose soil geologies is his hobbies, his Lego. He sees Shell be so big he is ill. He begs his boss Zoe, his gig is hell.

"Hell is high heel shoes. Hell is Boise, Ohio. Hell is hillbillies", his boss hisses. "Go be Bob's hell".

Giles obliges.

Bob oozes ego. Hegel is his Bible. Bob is so high he giggles. "Oh ho ho! Hello bozo".

Giles sighs. "Hello Bob. Zoe lobs blisses."

"Blisses? She lobs ills."

Can we do better? Probably. Could we generate "a long word of fiction"? I'm going to say "probably not": having 35 verbs and lacking both the letter "a" and the word "the" restricts the set of possibilities a lot. But given enough patience I bet you could easily write a page of more-or-less coherent text.

Let me close this post with some of my favorite generations that didn't make it into the longer stories because they are a bit inappropriate:

Repairing a shoe with a 3D printer

I got my 3D printing license last year, courtesy of the Stadtbibliothek Köln offering free printing to any card-carrying member who sits through their two-hours course "please don't burn down your fingers, the printer, or the library". The printer has been vital for replacing items that are too important to live without but too cheap to sell as spare parts, which brings me to today's topic: shoes.

If you haven't spent the last week obsessing over shoe spare parts you may not know that the heel cap is the part of the heel of a shoe that makes contact with the ground. A shoe without heel cap is useless because you can't tiptoe everywhere but there is no store around me that will sell a replacement for this specific model. A friend of mine lost one of those recently and you can imagine where this story goes from there.

The first step in making a replacement heel cap is to take measures of the part you want to replace. Since I didn't have my caliper at hand I took a picture of the heel where the replacement should go along with a reference of known size, a trick I picked from this scene of the 1999 movie "The bone collector". Here's the picture in all its grainy glory:

Picture of two shoes, one with missing heel cap and one with a 20 cent euro coin on top.

Next we need to trace the contour. A 20 cent Euro coin is 22.25mm wide so I imported the image in Inkscape, created a circle of exactly that size, and then resized the image until the coin in the picture matched the circle. I then traced the main forms on screen trying to take perspective deformation into account and ended up with a decent design.

Sketch of the contours of the main pieces that will be 3D printed.

While probably not the best tool for this specific use case, I used Blender to generate 3D shapes for these curves using extrusion, a process where you take (say) a circle, push it in a straight line, and end up with a 3D cylinder. I extruded every part individually, estimating 2mm for the bottom, 5mm for the guides, and 15mm for the main internal support. I finally saved the project as .stl for MakerBot Desktop, and we are ready to go.

3D rendering of the final piece.

One visit to the library and 30 minutes later, the first draft was ready for a test fit. I asked my friend for her shoe, took out my caliper for the necessary adjustments... and the draft piece fit perfectly on the first try. So all that remained was a second visit to make the piece tougher and deeper (just in case), and we are done.

Picture of the final piece.

This is not the first custom piece I print, but it's the one that made me realize how truly useful this technology has been to me. And you don't even need to know how to 3D model when sites like Thingyverse offer a wide array of ready-made designs for free. If you have the chance, you should definitely give it a try.

Building a Web App with nothing - Update

In my last post I mentioned a small webapp that I had built in an hour. This app was okay for what it was, but using it revealed that it needed a bit more love before it could be useful. And that's fine! After all, that was the whole point of making a quick mockup: to put it into the hand of users and see what their usage reveals about what's working and what isn't.

I have now written an updated version that you can see following this link. This new version has a couple small but important improvements:

  • It adds a second tab where you can see all of your data. Useful for knowing which expenses you've already added and/or to make sure that your records are saved properly.
  • It adds a button for clearing all data. I use this button not really to clear data, but rather to query the current date and time.
  • It now shows messages showing that data was inserted properly. As it turns out, not even the developer trusts a program that stores data silently and gives no acknowledgement whatsoever.

I still have not fixed the CSV export error. That one will have to wait for a future version.