Update: Want to listen to a podcast about go:embed instead of reading a blog post? Check out Go Time episode 171.
I have previously written about how to use
//go:generate. It exists to “to automate the running of tools to generate source code before compilation.” Now there is a new feature in Go that eliminates many uses of source code generation. In addition to the remarkable new flag.Func feature, Go 1.16 also introduced a new
//go:embed directive that allows you to include the contents of arbitrary files and directories in your Go application. To demonstrate some of the capabilities of
//go:embed, I have made an example repo which I will explain in this post.
The basic idea of embedding is that by adding a special comment to your code, Go will know to include a file or files. The comment should look like
//go:embed FILENAME(S) and be followed by a variable of the type you want to embed:
byte for an individual file or
embed.FS for a group of files. The
go:embed directive understands Go file globs, so patterns like
files/*.html will also work (but not
**/*.html recursive globbing).
You can read the official docs for a complete technical explanation, so here let’s take a look at some examples to see what’s possible.
Go 1.16 is shaping up to be one of the most exciting releases of Go in recent memory. Among its new features is the
//go:embed directive and the reorganization/deprecation of
io/ioutil. It’s not due out until February 2021 (Edit: it came out Feb. 16, 2021), but now I would like to write about a minor change to the standard library flag package that might otherwise be missed in the sea of changes:
I proposed and implemented
flag.Func. I have made minor contributions to the Go project before, but this is the first time I’ve added a whole top level function to the standard library, so I’d like to explain a little about what it does and the process of getting it accepted into the Go standard library.
First, some notes about who this advice is for: I am assuming you’re working for a small team with no Quality Assurance testing department making a content focused site. If you have a large team that actually QA tests IE11, this advice may not apply to your situation. If you’re just doing personal projects for fun, you should have already dropped ES5 years ago. If you’re making more of an app than a content site, you probably should have cut off IE11 years ago, but the calculations are more complex and site specific.
The promise of Web Components was that we’d get this convenience, but for a much wider range of HTML elements, developed much faster, as nobody needs to wait for the full spec + implementation process. We’d just include a script, and boom, we have more elements at our disposal!
Or, that was the idea. Somewhere along the way, the space got flooded by JS frameworks aficionados, who revel in complex APIs, overengineered build processes and dependency graphs that look like the roots of a banyan tree.
I think the issue is deeper than Verou’s complaints, although those are valid. Web Components don’t actually to solve the problem they purport to solve. The pitch is “get semantic elements from across the web!” But those are wrong problems to try to solve.
The other day, I was reading a website about some historical documents, when I saw that it had an error message on top:
Some quick searching online for the error message revealed that it was caused by a mismatch between the site’s versions of PHP and WordPress. Older versions of WordPress had a bug in the
switch statement of a certain localization component, and later versions of PHP dump a warning about this bug out to the end user HTML. When I came back to the site a few days later, it had been fixed.
The ultimate reason for my seeing for the error message as a random reader is that PHP has too many ways to deal with errors:
Builtin PHP functions, and therefore any PHP project, have a whole range of error handling mechanisms — errors, warnings, returning error values, and exceptions. At every point, calling code needs to know which system will be used to handle errors.
PHP often chooses to send warnings like this right out to the end user because it doesn’t trust operators to actually read their Apache logs. Such a practice would be very out of place in the Go programming language.
In short, I think it’s become entirely too easy for people using certain programming languages to use libraries from the wide world of clowns that is the Internet. Their ecosystems make it very very easy to become reliant on this stuff. Trouble is, those libraries are frequently 💩. If something about it is broken, you might not be able to code around it, and may have to actually deal with them to get it fixed.
Repeat 100 times, and now you have a real problem brewing.
I have a simple rule: never use a dependency that you could replace with an afternoon of programming.
Life with a toddler is always a strange combination of charming, frustrating, philosophical, exhausting, and funny. The other night, my wife tried to give my daughter a cookie. “Cut, cut!” my daughter said. So, my wife took the cookie and cut it in half. “No, no, no!” cried the toddler. “Okay, which one?” said wife, holding out a whole cookie in one hand and the half cookie in the other. “Two!” said the toddler, grabbing a cookie with each hand. She was finally happy.
But I couldn’t help but notice. “You don’t have two,” I said. “You have one and a half.” Naturally, she ignored me.
As someone who has written quite a few command line applications in Go, I was interested the other day when I saw Diving into Go by building a CLI application by Eryb on social media. In the post, the author describes writing a simple application to fetch comics from the XKCD API and display the results. It looks like this:
$ go-grab-xkcd --help Usage of go-grab-xkcd: -n int Comic number to fetch (default latest) -o string Print output in format: text/json (default "text") -s Save image to current directory -t int Client timeout in seconds (default 30)
I came away a little disappointed though because I felt like the final result was both a little undercooked and a little overcooked: undercooked in that it didn’t handle errors robustly enough and overcooked in that it created some abstractions speculatively in a way that I felt were unlikely to pay off in the long run.
Naturally, one thing lead to another, and I forked and rewrote the demo app myself to demonstrate what I consider to be just enough architecture for a Go command line app.
The other day on Reddit someone asked this:
Trueif the iterable is empty? Shouldn’t it return
if my_list:would evaluate to
Falseif the list is empty? What’s the thinking behind it returning
They have since removed their question, but it sparked a long discussion thread, and my comment was heavily upvoted, so I thought I would record it here:
This is literally a 2,500 year old debate in philosophy. The ancients thought “all unicorns are blue” should be false because there are no unicorns, but modern logic says it is true because there are no unicorns that aren’t blue. Python is just siding with modern predicate logic, but your intuition is also quite common and was the orthodox position until the last few hundred years.