A personal journey through programming languages

A little while ago there was a post about Programmer Migration Patterns through programming languages. I didn’t agree with it, but it got me thinking, about my 30+ years of programming, the languages I’ve been through, and when I can take away from that.

a graph of masukomi’s journey through programming languages

When I was a wee thing my mother brought home a used Texas Instruments TI99/4A that had been velcroed into a suitcase. I had a lot of fun with that computer. With the help of the Learn To Program Basic books from the library, I made circles and squares appear and move on the screen and did other silly things.

Fast forward a few years and I was a young teen sitting in the computer lab of my mom’s college writing Choose Your Own Adventure games in Basic on an Apple //e.

Another fifteen years or so and I’m doing freelance web design with HTML & JavaScript; pure Vanilla JS. Front-end interaction wasn’t enough though, so I learned Perl, which lead quickly to learning Bash and SQL, which have served me well ever since.

I loved Perl. It’s an awesome language to work in when you have the discipline to write clean, maintainable code.

Despite my love of Perl, Sun spent millions getting everyone excited about Java, and it worked. All the large companies were hiring Java devs. All the cool new stuff was being made in Java. So, I learned it…

I think Java’s. It’s an incredibly capable language, and it taught me a lot. It’s also completely soul-less. There’s no joy in it. While my days were spent poking Java for big companies, I dabbled in Python and picked up enough PHP to help on a contract. I hated PHP. I feel it’s a messy junk-drawer of a language.

Perl came back into my life for a while when my employer bought a company with a Perl codebase, and that was a nice change.

Around that time (2006ish) I discovered Rails, which thanks to Ruby, was a much nicer way to build web sites than anything before it. I think Matz created something great when he built Ruby. Its guiding principle of “optimizing for developer happiness” has had wonderful consequences. I’ll be forever grateful to Dave Thomas for writing the book that helped bring this language to the US.

Ruby lead me on a brief trip through Smalltalk, which I undervalued at the time (Glamorous Toolkit didn’t exist yet either). Looking for something that wasn’t just another C-like thing I turned to Common Lisp. CL was great, for the time it was written. We have learned a lot since then, and certain things like “the internet”, and “package managers”, and interacting with “other applications” have proved to be really useful ideas. Common Lisp has managed to incorporated essentially none of them, although Quicklisp is starting to change that.

I wanted to love it but it honestly felt like it hadn’t been updated for 30 years, and trying to anything that involve things invented since then was ridiculously difficult. Combine that with things like looping being having its own excessively complicated mini language, and other common APIs also having way too many ways to invoke them and I gave up. I don’t want to have to fight with my language to get basic shit done.

It was around this time that i started to be concerned about distribution. If I wrote something in Common Lisp, how would I give it to you to run? You probably don’t have it installed. What about something I ran in Ruby? Ruby’s even worse. You need to know about installing different versions of it. You need to know what a “Gem” is. You need to know what “Bundler” is and how to use it. If it’s a rails app there’s another layer of setup and invocation knowledge that’s required.

Developers immersed in a language’s ecosystem forget just how much built up knowledge it takes just to “launch” an app. I want to be able to create things I can just give to you and have you use, regardless of your favorite language or if you’re even a developer.

It was clear that lisp was something special that still had a lot to teach me, and I wanted to find a way to work with it. That led me to Scheme, which is awesome but has tons of flavors to choose from. Fortunately, I had a couple more criteria to help whittle down the list.

  • It had to produce a binary that other people could run with no knowledge of the language or ecosystem it was created in.

  • It had to be something that made it easy to work with the internet, databases, and filesystems. Yes, I know. Most developers take it as a given that you can do that.

I ended up with the Chicken Scheme because it has a great collection of modern libraries and is very enthusiastically kept in the present by its users. It also transpiles to C and then uses a native compiler to compile that to an executable. So, I’ve got tools for the modern world, and a binary you can “just run”.

Unfortunately, Schemes are “batteries not included” languages. Their Standard Libraries are very bare bones, and the significant number of things you have to write for yourself is a bit jarring when coming from languages like Ruby and Perl. The number of “eggs” (libraries) helped a lot, but I still ended up writing my own set of list functions, and comparators, and other “core” stuff like that.

I feel like getting stuff done in a scheme is a lot more work than in a “batteries included” language, but I’m not “fighting” with the language. Scheme is happy to help me make the tools to get stuff done. It just doesn’t have them pre-built.

Around the same time I started looking at Crystal. It’s essentially a statically typed, compiled version of Ruby. I’m a huge fan of Statically typed languages and explicitly defining what everything is. The compiler errors help me develop faster, and the explicit annotations are wonderful for improving the maintainability of the codebase. To me, though Crystal is just a better Ruby. I’ve gotten a lot of value out of it, but it hasn’t taught me anything new, and I’ve been hanging out in Ruby land for a very long time now.

After building my Chicken Scheme and Crystal executables I rediscovered the headaches of dynamically linked libraries (dylibs). Both languages gave me an executable that worked great, if you happened to have the right libraries installed on your system. They didn’t even work on all of my computers never-mind a non-developer’s. On top of that, it turns out that macOS (my preferred OS) really REALLY doesn’t want you to generating statically linked binaries.

As far as I know it’s not possible to make a 100% statically linked binary for macOS, but you can statically link everything except the system libraries. There are some tools for finding those dylibs in your executable and statically bundling them into it. At the time, I could not find them, and the languages didn’t offer static linking as an option. Crystal may have, but I recall still having some issues testing it. At the time of writing Crystal and Chicken scheme both offer it and I know for a fact that the Chicken Scheme works well, but the documentation is kinda crap.

I wanted a language that really “got” this distribution problem, understood the modern world, and now my latest criteria… had a good solution for concurrency or parallelism. [side note: Crystal actually had concurrency from early on, and now offers parallelism too.]

I tried Go. Partially because it really understands this distribution problem, and “all the cool kids are doing it” which is useful when job hunting. Unfortunately the more I used it the more I disliked it. I kept telling myself that it was created by some geeks with serious cred, and that if I stuck with it, I’d surely learn something good.

What I learned was that I have fundamental philosophical differences with the people who created it. I’m crossing my fingers that I don’t need to get a job programming in it.

Conclusions

So, what have I learned from all this?

  • Learning new languages just makes you better at whatever language they pay you to write.

  • Being a “good” language isn’t the same as a “good” language for you.

  • People don’t value learning a lisp nearly enough.

  • People aren’t thinking enough about the importance of easy distribution.

  • What’s Next?

Learning languages makes you better.

Fundamentally there are only a handful of programming constructs. Learning a new language teaches you to distinguish between basic programming features, and the syntactic sugar your language adds to them to make life “better” for the developer.

As you learn more languages you pick up more core tools. You gain new ways to use the ones you have. After the first couple, every new language you learn reinforces your core understanding of what “programming” is, and that learning the next language is just a matter of learning the syntax for the concepts you already mastered.

“Different Strokes for Different Folks”

Different people think about things different ways. It’s why working through problems with other people is so useful. I loved how Perl let me solve my problems in 20 different ways. I hated how Python forced me to use 1, or maybe 2 solutions for a thing. It was never a “bad” solution, but it forced me solve problems from an approach that I just didn’t want. At the same time, that simplicity and lack of complication is joyful for some people. I totally understand why people love Python.

It’s ok to hate a language others love. It’s ok to love a language others hate. Try and find one that works well with the way you think, and helps you to express your thoughts with the least interference.

Lisps

Even after a decade of programming it was still a major eye-opener to learn a Lisp. I suspect that learning something even more functional, like Haskell, would be a great learning experience even now. I think it’s ridiculous how people get all bent out of shape about “all those parentheses”. There are tools for this, people. It’s a solved problem.

And macros, oh my. Lisp macros are a beautiful thing. Being able to easily change and improve any aspect of a language… it’s an amazing thing. It’s also a dangerous tool that, when taken too far can make it difficult for others to contribute to your project.

My advice is to learn a Scheme. Racket is probably a great place to start. There are lots of books, the documentation is great, and it’s designed for teaching and learning. I went with Chicken Scheme because of its ecosystem (Racket’s is even better) and because it compiles to C code (or straight on to an executable), which means I can build it anywhere easily. The 5.0 release made creating statically linked binaries easy too.

Distribution

Code distribution is a serious problem that’s not getting nearly enough attention. The required knowledge to get a Ruby programming running is ridiculous, and it’s the same with most modern languages. The Go community is one of the few that really gets this problem. They’ll give you a binary for your system that requires zero knowledge of Go or Go libraries to run. On top of this, their binaries can be statically linked. This radically changes who can use your creation and how hard it’ll be for them. Wanna know how to run a Go app? Just execute it. It’ll either start working, or give you usage instructions.

It is frustrating to me that no-one seems to care about this.

What’s Next

I’ve burnt out twice in the past decade. The first time was bad. The second time was worse. I’m trying to avoid a third time. The effects of this color all my decisions now.

The most significant effect (relative to this post) is that I don’t have enough fucks to give for languages that I don’t enjoy. I spend 8+ hours a day thinking about code. Most of it is frustratingly terrible code that I’m wading through. I don’t need any additional frustration coming at me from the language I’m working in.

I’ve been learning Rust for a work project, and I’m gaining serious respect for it. It’s a really great solution to the problem its creators had. But I don’t have the problems its creators have. I’m fine with garbage collection. In fact, I like garbage collection.

After 30+ years of writing software. I like not having to deal. I like not having to specify every little detail for the compiler to know what I mean. Crystal does a great job of letting me not deal. I even specify all the types that it could figure out implicitly because it makes it all so easy, and my doing so makes it trivial for the people who come after me to figure out what I intended. But, like I said, I’m getting tired of Ruby.

My advice to you? Don’t waste your limited time in a language you don’t enjoy. There are too many good ones out there. Search them out. Some of them are new, and rough, but worth helping grow. Some are old and filled with amazing tools. Learn Smalltalk. Learn Lisp.

I’m not sure what language I’ll end up playing with next. I do know that I’ll be trying to find a way to spend it with one that makes me happy.