Today was my first day at the Recurse Center (RC). I had so much fun hearing about what awaits me over the next ~12 weeks and meeting a bunch of new people, all of whom seem to be just about as excited as I am!
A lot of the discussions I had with folks revolved around what everyone was planning to work on during the coming weeks. I've been curious about a handful of programming topics recently so I have some initial ideas for things to explore:
- I've been working my way through Robert Nystrom's Crafting Interpreters and I have a couple of ideas for different directions to take.
- Andy Pavlo is a great speaker and writer and I'd love to dive into some of the course material he has put together on database systems.
- Andreas Kling is my go-to for relaxing and satisfying YouTube viewing. It would be a blast to contribute to SerenityOS in some way.
There are also a bunch of reading groups and other interesting gatherings to attend. I'll probably attend lots of stuff early in the batch and see what sticks.
Most of all, regardless of what I am working on, I want to make a real effort to communicate about it. I'd love to improve my writing and communication skills significantly over the next few months.
In the rest of this post, I'll dive into some of the things I've been thinking about.
Joy in Programming
I love programming. I get a deep sense of satisfaction watching a program that I've written solve a problem. I also love the craft of programming. There are so many different ways to solve problems with code. Which way is the most expressive? Which techniques help reduce or eliminate bugs? I'm constantly thinking about these sorts of things whether solving an Advent of Code problem or starting a design document at work.
Often when I've started conversations about the practice of programming at work, I've received blank stares from many of my coworkers. This has been pretty discouraging. From everything I've read and everything I've seen so far, RC is the kind of place where these kinds of discussions are met with curiosity from the people around you. Whether they are experts in a particular programming topic or looking to learn more.
The summer after I graduated, I attended the Oregon Programming Languages Summer School (OPLSS). Everyone there was psyched to exchange ideas about programming languages across the full spectrum from theory to practice. That was the last time that I remember being surrounded by a group of people so enthusiastic about the subject at hand.
I really enjoy programming as a career. But necessarily, programming and the practice of programming is a means to an end in a business context. There is a lot of satisfaction to be had in generating business value, but sometimes the drive to produce impact can quell some natural curiosity. There isn't always time to peek under the hood and learn about the different systems and abstractions that we lean on to get our work done, every single day.
This is what I'm most excited about being at RC. To have the time and space to explore those nooks and crannies and muse about it with a bunch of likeminded people.
High-level Programming Language Runtimes
A lot of my background in the programming language space comes from my undergraduate curriculum. I loved the PL classes in school, but there was a heavy emphasis on static type systems and theory in general. Reading and working through Crafting Interpreters has been a blast. High-level dynamic languages were generally derided (example) during my school years. Writing a good amount of code in python and JavaScript over the years has me convinced that dynamic languages have their place, though I still generally prefer to have a powerful static type system.
I'm really fascinated by all of the efforts that have gone into creating absurdly fast language runtimes over the last couple of decades. I'm hoping to do some reading starting with the smalltalk and Self optimization papers of the 80s and 90s, working my way through LuaJIT in the mid 2000s and V8 and pypy in the late 2000s.
Ideally, I'd be able to incorporate some of those ideas into a Lox interpreter, but that might be overly ambitious. We'll see!
Somewhat unrelated, I've been running into an issue where a C++ implementation
of Lox that I've been working on runs ~100% slower than clox on a simple
for
loop example. Most of the time seems to be spent in std::vector
reads.
Not totally sure what's going on, but it'd be fun to dive deeper and do a write up.
Protohackers, Rust and Python
Since I wasn't sure how much time I would have amidst the orientation events, this week, I've lined up some bite-sized tasks to tackle.
I started working on protohackers at the end of August. I had started in Rust which has been a ton of fun. When I reached problem 6 (Speed Daemon), I stalled a little bit as there was more synchronization required than the previous problems. I made a couple of adjustments with the hope that I might be able to make more sustainable progress:
- Swapped from DigitalOcean to fly.io
- Solved the problem in Python instead of Rust
DO to fly
DigitalOcean is great. It's super simple to spin up a tiny VM, ssh into it, grab the latest copy of my code and run my program. I've been curious to get better familiarized with fly.io and similar hosting providers.
For the most part, deploying via a Dockerfile is pretty straightforward. I encountered two issues:
- I couldn't get UDP to work; protohackers problem 4 specifies a key-value store accessed over UDP. I tried following the fly.io guide on UDP but gave up before getting anything to work.
- The default concurrency settings caused my service to reject some connections when testing
my solution. The auto-generated
fly.toml
file I was using included this snippet:
[services.concurrency]
type = "connections"
hard_limit = 25
soft_limit = 20
For a while, I thought I had a logic error in my program that was causing me to
close connections before I found a log entry from the proxy layer that indicated the
concurrent connections were being limited. Eliminating the snippet from fly.toml
had
no effect, but replacing those lines with:
[services.concurrency]
type = "connections"
hard_limit = 150
soft_limit = 100
seemed to fix the issues.
Overall, fly.io is really pleasant to use. I don't have a lot of experience using Heroku which seems to be the main comparison for fly. But the ability to launch a service anywhere in the world from my command line is pretty neat. It's still quite a bit faster to iterate over ssh, but I'm hoping I can find ways to optimize my workflow.
Rust to Python
I'm pretty much head-over-heels for Rust. It's a really satisfying combination of ideas from ML and C++.
That being said, there is a lot to think about when dealing with concurrency
in Rust. Sometimes you need to wrap something in Arc<Mutex<...>>
to provide
mutable access to some shared state across threads. Tokio is
fantastic, but you still need to think about multi-threading and synchronization.
Python's asyncio
offers a programming model that is conceptually similar to
Rust's asynchronous runtimes but without the need for synchronization (since programs
that leverage asyncio
run the event loop in a single thread).
It took me a surprisingly long time to get an implementation working in python.
I don't have a ton of experience dealing with binary encodings in python, so that
took some adjustment. On the asyncio
side of things, it took me a while to figure out
that I might want to use readexactly
for the implementation of reading a single message from a client. In general,
I think I have an easier time discovering python documentation but an easier time
reading Rust documentation once I've found it.
I also made the embarassing mistake of forgetting to explicitly set flush=True
for my print
debugging. I spent far too long trying to figure out why my service
didn't seem to start.
All-in-all, it was really satisfying to watch the last test case pass and I'm excited to keep working on the rest of the problems that I haven't solved yet. Maybe I'll find a chance to do a writeup on solving the problems in python and Rust at some point during my batch.
Eloquent JavaScript
I love reading programming language books and I had seen lots of recommendations for Eloquent JavaScript by Marijn Haverbeke in the past.
I am currently reading Chapter 16 which discusses the implementation of a small platformer using the DOM for rendering the game world. The book is great. I really enjoy the general style (a practical mix of functional and imperative) that Marijn uses in his examples. He also uses a lot of closures, which I think are the most powerful and expressive feature of high-level garbage collected languages.
But so far, the most interesting bits from the book have been general software engineering concepts. Chapter 10 discussing modules opens with this gem:
A typical real program grows organically. New pieces of functionality are added as new needs come up. Structuring--and preserving structure--is additional work. It's work that will pay off only in the future, the next time someone works on the program. So it is tempting to neglect it and allow the parts of the program to become deeply entangled.
Marijn continues describing the issues of this sort of "entaglement". I thought it was a really empathetic way of describing how some programs accrue technical debt.
On the other side of the coin, there is a section in chapter 16 labeled "Encapsulation as a Burden". Marijn discusses the tradeoffs involved when choosing where to introduce abstraction in your programs. This is a subtle topic that takes a lot of experience to master (I certainly haven't mastered it yet). But Marijn makes convincing arguments and plants the seed in the reader's mind.
Overall, it's been a great read so far! I'm excited to finish it up and maybe do some JavaScript pairing.
Wrap Up
If you've made it this far, thanks for reading! Stay tuned to hear more about my experience at RC.