Tuesday, March 19, 2019

Old Man Reviews Programming Languages, 2019 Edition

Go

I picked up Go early last year to put together some services for work. There's a lot that I like with the language. It's dead simple; I would bet someone coming from a background in bracey languages can be productive in a couple hours. 

I love being able to get up and running without being able to write any sort of build manifests or scripts or anything, that I can drop in references to third party projects to fill in the few gaps in its massively comprehensive standard library and the out-of-the-box tooling just rolls with it. It has its downsides though, and the ability to manage versions for consistency is a big one. 

Coming from C++ (in games) where exceptions were not an option, and having minor PTSD from reading Herb Sutter's series on So You Think You Can Write Exception Safe Code, and languages like C# where I really had to dig deep to find the effort to handle exceptions, I liked Go's error handling at first. I guess it's that it was explicit about being able to specify "this is a function that returns a value AND an error if something goes wrong"; it seemed more natural than having to see if something threw and how to catch it, or (worse) checked exceptions. I didn't even mind the verbosity.

I like that the go tools build code, run tests on code, format code, and fetch code. My gold standard now in the tabs-vs-spaces war is now "who cares, let the robots handle it".

The compiler error messages are teeerrrrriiibbbble. They are so bad. In some cases it would be an improvement if they just took out the message, leaving the file and line number, and let you dig in. The language itself is simple enough to figure out most errors at a glance.

Rust

I liked Go a lot. Then I tried Rust and now I like Go less.

I've been wanting to dig into Rust for a long time but I've been intimidated by what I've heard. "I'll spend all my time fighting the borrow checker!" Then I had the occasion to have to write a shared object plugin for a service that's live on the internet and suddenly the borrow checker seems like a much better dance partner than trying to harden C or C++.

Yeah, it's a pain, but it's got rules. I spent some time frontloading and internalizing the rules and found that when I started writing code it wasn't a huge battle. 

I spent more time fixing issues with missing semicolons than I did with the borrow checker. I guess a year in Go/TypeScript/Python will do that. 

(But, so far my spelunking into Rust hasn't really taken me deep into managing reference lifetimes.)

Error checking in rust is fantastic. I love that I both have the transparency of the error types you get in Go (ie with Result/Option), but that you can elide the boilerplate with the question-mark operator. Brilliant. Love it.

A lot went into the thought and implementation of the Rust language features and it's a very pleasant, expressive language as a result. Everything is an expression? Love it. Matching? Brilliant. I could go on all day.

Rust's tools put Go's to shame. Like the go tools, they do builds, tests, benchmarks, formatting, package fetching. Something I miss from leaving EA was the code build system. It worked really well for packaged components and I think the closest I've found to this since leaving is Cargo. Package downloads aren't particularly useful in production environments if you can't have consistent environments which is something Go is still figuring out.

(Maybe if you're working at Google and everything is on latest all the time and you have no need to step out of your internal ecosystem it's fine, but one CI system failure on a new and exciting public API change in Dr Janky's Awesome Left Padding Library is too many, because who knows how many behavior changes are included too. But I digress.)

In general the tools just feel polished, like someone has actually internalized that UX is important for more than just web pages. The error messages are top-notch. Providing a way for the compiler to explain in greater detail more of what went wrong? I love it. Microsoft, clang, gcc, take note. Having a link to the warning page on MSDN instead of having to go through Google to get more information would be great. 

The standard library is pretty slim compared to Go, requiring users to have to go out into the wild west to find the functionality they need. It would be nice if more was blessed and included in like a std::ext namespace or something (ie: std::ext::http, ...), but that's not a big deal.

One of the things that I really hated about C++ was it seemed every C++ developer only knew 30% of the language space, but every developer knew a different 30%. And with every iteration of C++ over the last 8 years it seems like both the language space people know is decreasing and the spread of core language concepts is widening. I get the feeling with Rust that there is less conceptual spread in core areas, so while I may only have had exposure to 30% of the language, I'm not finding myself reading things like this C++ value category reference  and wondering why I didn't go into veterinary medicine instead.

If I had a piece of advice to the Rust steering group it would be to not follow C++ down the road of committee driven, lowest common denominator bolt-ons. The language is great. Don't be afraid to extend with both syntax and library (ie: Option/Result/?-operator). Keep doing the great work you're doing!

All that said in praise of Rust over Go, I do want to say that Go is still great for building and deploying tools and services. 

JavaScript / ECMAScript 2015

I mean... it's better? I guess? But... just use TypeScript.

TypeScript

TypeScript is brilliant. Seriously, unambiguously brilliant. I started using it in December so when I noticed that create-react-app supported it. I remember when I first heard that Anders Hejlsberg was starting on TypeScript and was wondering where it would lead; he had done an amazing job with his previous language development and (more importantly) implementation efforts. 

The end result is something that lets you start bringing law and order to the wild west of existing JavaScript code bases, one file at a time. I just started dropping in files written in TypeScript in my existing web front end project and it worked. First time. No screwing around with tools, no nothing.

Brilliant!

Having the sanity of Real Types available when refactoring JavaScript code? Amazing!

Seeing a path forward from the morass of 0 / false / null / undefined? Great!

Being able to use ==/!= instead of ===/!== like a Sane Language(tm)? Where have you been all my life!

Automatic semi-colon insertion without the danger? Hook it to my veins!

A few things surprised me when I started getting into it, like 1) how many existing publicly hosted packages were actually written in TypeScript; 2) how many existing publicly hosted non-TS packages had type annotations available for them; and 3) how I could just drop a module declaration in my index.d.ts file for all modules that didn't fall into categories 1 and 2 and I could get back to work. 

I'd really like to start using in the state backbone of our React front end, ie: the Redux reducers, but where I've tried it I'm finding it's awkward and there's a lot of fighting going on. Maybe I'm doing it wrong. Maybe it's something that's just not meant to be. 

The error messages can be pretty appalling, bringing me back to the days of template metaprogramming with older versions of MSVC. But that's a price I'm willing to pay to reduce the number of times I'm debugging undefined references caused by accessing a misspelled property.