Relearning Go: Day 11
Reading Files & Writing Files to Exit
Reading Files & Writing Files
gobyexample.com/reading-files
gobyexample.com/writing-files
Simple and self-explanatory.
IO means checking errors frequently, which brings us into close contact with Go’s error handling story. Maybe there’s an axiom in this, like: the more verbose the language, the more programmers will take shortcuts – like panicking or ignoring errors. I wonder if this is borne out in practice.
Line Filters
There’s some more ceremony to reading lines from files in Go than in other languages I’ve used.
Go’s Scanner
is also kind of ugly, since errors are not propagated. Instead,
it seems that you must check by calling Err
explicitly after Scan
returns
false. There’s nothing in its design that leads you to remember to do this.
The Scanner.Text
documentation also
doesn’t mention what will happen if the bytes scanned are not valid UTF-8. The
signature is func (s *Scanner) Text() string
so errors are not propagated this
way. Maybe the string returned can be invalid UTF-8? Or should we also check
Scanner.Err
?
File Paths & Directories & Temporary Files and Directories & Embed Directive
gobyexample.com/file-paths
gobyexample.com/directories
gobyexample.com/temporary-files-and-directories
gobyexample.com/embed-directive
Nothing to add.
Testing and Benchmarking
gobyexample.com/testing-and-benchmarking
Nothing much to add, but I thought this was cool:
t.Run
enables running “subtests”, one for each table entry. These are shown separately when executinggo test -v
.
I assume the test code is not compiled into a release build, but I’m not sure of the mechanism.
Command-Line Arguments & Command-Line Flags & Command-Line Subcommands
gobyexample.com/command-line-arguments
gobyexample.com/command-line-flags
gobyexample.com/command-line-subcommands
Almost universally the world has converged on the -s
short and --long
argument pattern, which appears to come from a combination of the POSIX
conventions for command line arguments combined with a GNU extension for long
options1. This was the prevalent style even in the 2000s when Go
first appeared.
The flag
module uses a different, outdated pattern for arguments. Tooling
created with flag
will work differently to what the majority have come to
expect. For those that aren’t aware of the distinction, it sows confusion. For
those that are, it sows distrust. It undermines the usefulness of the
command-line.
It’s a small corner of the language, but it feels needlessly contrary to promote this superseded style by bundling it in the Go standard library – meaning it can not be updated until at least a hypothetical Go 2. There are third-party packages to parse arguments that are in line with modern (i.e. since the 1990s) expectations, but not everyone will know they exist.
Environment Variables
gobyexample.com/environment-variables
Makes sense, but Go ignores that environment variables are byte strings, and not necessarily UTF-8.
Logging
Nothing to add.
HTTP Client
Nothing much to add, except that the use of defer
in the example means the
HTTP request could be kept open longer than it needs to be.
HTTP Server
Useful to have in the standard library. Like its counterpart in the Python standard library, I suspect it doesn’t get used much for “real” work though.
Context
Nothing much to add, except that the API is confusing. When do I use the various
methods? The documentation says what they do,
but not when and why I should use them. Context
feels like a grab bag of a
few things that were missed when designing channels and goroutines but couldn’t
be added because they were already set in stone.
Spawning Processes
gobyexample.com/spawning-processes
The API is familiar.
Unlike the “system” library call from C and other languages, the os/exec package intentionally does not invoke the system shell and does not expand any glob patterns or handle other expansions, pipelines, or redirections typically done by shells.
Good stuff; system(3)
is a blight.
The example ignores so much error handling. It’s almost as if Go’s idiomatic error handling gets in the way of comprehension.
Exec’ing Processes
gobyexample.com/execing-processes
The example notes:
Note that Go does not offer a classic Unix
fork
function. Usually this isn’t an issue though, since starting goroutines, spawning processes, and exec’ing processes covers most use cases forfork
.
Interesting decision, no complaints (for once), but the next example is a very UNIX subject:
Signals
Signals come in on channels! Nice indeed!
Exit
Nothing to add.
With that, we’ve completed Go by Example.
For sure I had much to complain about in Go, but Go by Example was a great primer. It kept the pace up, but also piqued my interest such that I experimented with the examples, and frequently went off to read other Go documentation, especially at pkg.go.dev.