I've been programming in Go since 2013-ish, but I took a sort of sabbatical as a craftsman at Clean Coders Studio doing a lot with Clojure, which introduced me to the beautiful world of functional programming. Now, back in Gopherland I have found myself craving more functional-style building blocks. Also, the nature of my work since then has required me to write literally hundreds of scripts in Go, as opposed to long-lived services and enterprise software. When writing scripts the rules are different--you don't have to gracefully handle errors (you just kill the program). So, I've built several loosely related utilities to facilitate scripting in Go in a functional style, all of which I present to you here:
github.com/mdwhatcott/go-cli-template
I enjoy building small, simple CLI tools. This template is a pretty decent starting point for me, and it's compatible with the gonew utility. This template includes almost all of the libraries listed below.
github.com/mdwhatcott/tui
Sometimes using a CLI is tedious ('what are the flags and options?') and I'd instead prefer that the program guide me through a series of UI elements and steps. This little repo is a no-frills text UI toolkit featering prompts, select menus, and confirmation dialogs. By default it interacts with os.Stdin
and os.Stdout
, but is compatible with any io.Reader/Writer
combination.
github.com/mdwhatcott/slogging
A simple library to extend the output of the new "log/slog"
package released in Go 1.21. Options exist for:
- Setting the log level,
- Emitting time values with log statements using a custom format string (such as
time.TimeOnly
), and - Emitting just the source file basename of a log statement (not the full source path).
github.com/mdwhatcott/exec
Admittedly, a hack that employed bash -c ...
to execute arbitrary shell commands from Go without needing to quote each argument. Maybe be careful with this one.
import "github.com/mdwhatcott/exec"
func main() {
output, err := exec.Run("ls -1",
exec.Options.At(folderPath),
exec.Options.Out(writer),
)
// ...
}
github.com/mdwhatcott/funcy
Many clojure.core
functions implemented in Go with generics to faciliate functional-style programming, with apologies to Go purists who would rather just use for loops, as well as functional programmers who expect lazy behavior similar to what is offered in Clojure and other functional languages. Here's a short example of how I would use the funcy
package to solve Project Euler Problem #6:
func main() {
fmt.Println(Calculate006(10)) // 2640
fmt.Println(Calculate006(100)) // 25164150
}
func Calculate006(n int) int { return SquareOfSums(n) - SumOfSquares(n) }
func SquareOfSums(n int) int { return Square(Sum(Range(1, n+1))) }
func SumOfSquares(n int) int { return Sum(Map(Square[int], Range(1, n+1))) }
UPDATE: github.com/mdwhatcott/funcy/ranger provides a similar approach to the clojure core library in Go, but also features lazy evaluation wherever possible, made possible by Go's new iterator feature (released in Go 1.23). Several examples provided.
github.com/mdwhatcott/must
For anyone who is tired of writing scripts and having to deal with error handling of the form:
thing, err := someFunction()
if err != nil {
panic(err)
}
When working backend enterprise software (web servers, daemons, etc.) which are long-lived, error handling cannot be avoided, but when you're writing a script, crashing fast is often very desirable, so error handling becomes very repetitive (see above snippet). With must
, you can write this:
thing := must.Value(someFunction())
... and trust that if an error surfaces, the program will panic and (probably) crash.
This module also defines sub-packages that correspond with standard library packages:
file := osmust.Create(path)
content := jsonmust.Marshal(stuff)
// etc..
github.com/mdwhatcott/go-set
Try a few queries on pkg.go.dev and you'll quickly find more implementations of set data structures than you'll ever need. It's clear we programmers like working with sets. A proposal to add sets to the standard library was started, but has since been locked. Who knows why (I tried skimming the discussion thread, but it got way too long and convoluted). So, not to be outdone, I decided to muddy the waters by creating my own implementation.
I might merge this with the funcy module someday because I so often use these modules together.
github.com/mdwhatcott/tiny-should
Sometimes I don't need my trusty x-unit test runner. I just need a few simple assertion helpers. That's what this package is for:
func TestPassing(t *testing.T) {
should.So(t, 1, should.Equal, 1)
should.So(t, false, should.BeFalse)
should.So(t, true, should.BeTrue)
should.So(t, nil, should.BeNil)
should.So(t, 1, should.NOT.BeNil)
now1 := time.Now()
now2 := now1.In(time.UTC)
should.So(t, now1, should.Equal, now2)
}
func TestFailing(t *testing.T) {
t.Skip("comment me to see the failures below")
should.So(nil, 1, should.Equal, 2)
should.So(t, 1, should.Equal, 2)
should.So(t, true, should.BeFalse)
should.So(t, false, should.BeTrue)
should.So(t, 1, should.BeNil)
should.So(t, nil, should.NOT.BeNil)
should.So(t, uint64(1), should.Equal, uint64(2))
should.So(t, time.Now(), should.Equal, time.Now())
}