How to learn a new (programming) language

...by leveraging your existing knowledge to rebuild something you've already built. A Case study in Hare

(Cold open)

I watch a lot of “edutainment” on Youtube. And when a video on Portal 2 being turned into a webserver crossed my feed, I had to watch.

Combining that with this “webserver in 25 lines” from Computerphile I saw the previous year, I got inspired to write a webserver in Zsh.

Here’s the immediate result is what came out of that. Even though it’s single-threaded, it’s still reasonably scalable, since I’m poll()ing all open connections (using the Zsh builtin zselect).

And a toy webserver like this is a great project to take to a new language.

A new language

So you’re trying to learn a new programming language. The tutorials are short and contrived. The documentation is written with different knowledge in mind.

But if you have a moderately-complex problem that you’ve solved before, something like I had (building a webserver with a pool of connections), you might have the perfect project to go from unfamiliar to competant.

I’ve had interest in low(-ish) level programming for a while. But Rust feels too big, and C too old.

Enter Go Zig Hare. A language with a small featureset heavily inspired by C’s simplicity and by advances in language design over the last 50 years. And personally I’ve been following what Drew DeVault has been doing ever since Sway came along to bring Wayland to my i3 config.

Tutorials

It was over a year ago that I went through the tutorials, writing each little program (or something similar to it) by hand to get used to the tooling and the syntax.

But then I stalled. At that point I didn’t have any projects in the goldilocks zone where I could focus on the Hare-isms and not on figuring out what problems I had to solve.

But much later (as in just last week) I got reminded of my Zsh webserver, and realized that I had the perfect “reimplement at a low-level” project to work with.

And here is the result! About 250 lines of likely-clumsily-written Hare, implementing many of the same features from that Zsh webserver.

Some parts feel like they are designed right, some feel absolutely awful, but I’ve learned a lot, enjoyed the language design, and am looking forward to writing more in it.

The easy bits

A lot of the logic is very similar.

I had only interfaced with poll() via Zsh’s zselect builtin, so the exact interface was different. But reading the hare docs (and man poll) gave me enouh context to get it working.

Accepting new connections worked just as expected. getopt to read in an alternate port was straightforward.

The “I expected that to be hard, and it was” bits

Explicit error handling via tagged unions was compelling to me, and one of the main reasons why I was drawn to hare.

But figuring what errors were possible is tricky. net::error could be net::unknownproto, or also any other errors::error error. Handling memory correctly when errors could propogate out with ? is sometimes non-trivial.

And speaking of memory management…

The “I expected that to be hard, and it was worse” bits.

I have not dealt with manual memory management… practically ever. Dealing with it now is quite enlightening, and a good exercise.

I still haven’t figured out the best way to dynamically pull in more data if a request is larger than my buffer. Reading the source of io::drain gave me a good pattern to follow.

Honestly now I’ve gotten deep enough that it’s probably time to show off my awful code to some other people in the Hare community, which is where this blog post comes in. :)

There’s a lot of Bad Things I’m probably doing. But fuck it, I’m learning.

The “Small and new project, here be dragons” bits.

I found a construct where I got my program to crash because it ran “unreachable code”, and the output helpfully suggested “(compiler bug?)”

The setup was something like this:

for (...)
    match (...) {
    case errors::again =>
        continue;
    };
    <more code, this is what got tagged as unreachable>
};

I tried to write a minimal reproducer from scratch, but couldn’t get it. I need to pare down the original code instead.

Other thoughts

As I’ve been growing the Hare webserver, I’ve gradually grown the Zsh webserver ahead of it. At the time of writing, here is the source code. This has probably grown enough that I’ll move it into its own project.

You can now PUT data into it, GET it out later, and DELETE it. It should support ranges and modified-by checks, and strips query parameters. All targets are in a single targets associative array (with modified dates and filetypes in their own arrays as well) instead of as files on disk. I might add a cache that writes out all the target state with typeset -p to a file that gets sourced when the server closes.

There’s a small part of me that wonders if I could add enough features to have it actually serve a public website (like this one). Presumably I could pass TCP data through openssl too? Or maybe I’ll build up the Hare version to that level instead?

Hmm…

code  zsh  harelang  web