New Go Module: tychoish/godmenu

A while back, I wrote a little Go wrapper library around the dmenu library and called it go-dmenu but then realized that without the hyphen the ambiguity between god-menu and go-dmenu was sort of delicious so I went with it.

This is not an exciting piece of software, and is just a wrapper that calls dmenu in a subprocess and passes it a list of options and returns to that Go program, the selection provided by the user (via dmenu.) That’s it. One function, one configuration structure, that’s it, no dependencies, no more features.

Check the repo tychoish/godmenu, and also the docs.

In some ways that’s the post. I should end here, but…


dmenu?

dmenu is this great, super simple piece of software that is just a menu for selecting items from a list. That’s it. You send it a list of selections on standard input, it does it’s thing and returns, on standard output, the selection. That’s it.

It comes with this dmenu_run script that gets a list of all the executable files on the PATH runs whatever command you select. So it works kind of like a launcher, and I think by and large, this is the main thing that it’s used for, but it’s so simple, and so good at doing it’s thing that there’s no reason you can’t use it for anything menu-ish or selection-ish.


What?

godmenu is just a wrapper that lets you do call dmenu from Go programs, so that it’s easy to use it from within Go programs, and so that you don’t need to write shell scripts to use it. There’s sort of nothing to the library, and maybe that’s the point: better to have it be the kind of thing that you write once, get it right and then sort of never have to think about it or do it again.

I’ve been thinking recently about what it means for a library to be useable or possible for a project to adopt or include it, and this is an experiment in a couple of these ideas: it’s simple and focused, provides a single piece of functionality, manages no state, has no dependencies, and makes it easier to do a thing that would otherwise be annoying.


Why?

A while ago I decided that I was done writing tools for my own personal use as shell scripts, because I was tired of having to fix them after years of not thinking about them, or figuring out what their dependencies were when I got a new computer, and just generally in writing the kind of very defensive code that you end up needing to write.

I’ve gotten pretty good at subprocess management (mostly because of jasper, though the godmenu wrapper doesn’t use jasper to minimize the dependency proliferation,) but adding interactivity directly in a simple program gets very complex quickly. Beyond a certain point, as soon as you you need user input or interaction, writing a little script becomes incredibly complex. Command line only interfaces–subcommands and flags–are simple but there are lots of parsing edge cases and countless competing libraries/frameworks, and when there are lots of options and the “default” options aren’t frequently used, usability falls apart. Richer interfaces–GUI or console widgets–add a lot of technical complexity and overhead (almost all of the time,) and you still have to design the interface.

Using dmenu selectors in your scripts, means you can (basically) write a bunch of small commands, stick them in a menu, and get fuzzy-text selection based on the options you provide. This gets you pretty far with minimal additional technical or conceptual complexity.


How?

It’s a normal Go package, nothing crazy, here’s a minimal example, for a user to select between the first four lowercase ASCII characters.

	output, err := godmenu.Do(ctx,
		godmenu.Operation{
			Selections: []string{"a", "c", "d", "b"},
			Sorted: true,
			DMenu: &godmenu.Configuration{
				Path:            godmenu.DefaultDMenuPath,
				BackgroundColor: godmenu.DefaultBackgroundColor,
				TextColor:       godmenu.DefaultTextColor,
				Font:            "Source Code Pro-13",
				Lines:           16,
				Prompt:          "=>>",
			}
		},
	)
	if err != nil {
		return err
	}
	// &etc...

The Do function returns the string the user selected with the menu, or the empty string ("") and the godmenu.ErrSelectionMissing error.

Interesting options here:

  • the “lines” option makes the number of visable lines the menu uses configurable. I only discovered this option recently and I rather like it.

  • I don’t use it myself, but there’s a Bottom boolean option that makes the menu appear at the bottom rather than the top of the screen.

There’s also the Run method which does the same thing, but uses the so-called “functional arguments” pattern for a bit more flexibility at the site. Since dmenu and godmenu have some reasonable default options, you don’t need to specify all the options, or even many of them, so something like the following would work.

	out, err := godmenu.Run(ctx
		godmenu.Selections("a", "b", "c", "d"), 
		godmenu.Prompt("godmenu =>>"),
		godmenu.Sorted(), 
	)
	if err != nil {
		return err 
	}
	// &etc...

There are also some helpers and aliases among the options provided, to make it easier to call this in various ways:

	out, err := godmenu.Do(ctx, 
		godmenu.ResolveOptions(
			godmenu.Items("a", "b", "c"), 
			godmenu.Prompt("=>"),
			godmenu.WithFlags(godmenu.DefaultFlags())))
	if err != nil {
		return err
	}

	// or... 
	
	out, err = godmenu.Do(ctx, MakeOptions("a", "b", "c"))
	if err != nil {
		return err
	}

	// or ... 

	out, err = godmenu.Run(ctx, WithOptions(MakeOptions("a", "b", "c")))
	if err != nil {
		return err
	}

Though possible, these exact examples might not make a lot of sense, but it becomes possible to refactor the way you call godmenu, to reuse as much of the configuration as possible.

But other than this one operation–“select item from list”–and the ways you can call things… but this that’s it!


What if..?

Nope.

I’ve been playing with building out some additional tools here, but I’m disinclined to creep the scope (or the dependencies) of the core package for this, in the same way that dmenu itself is unlikely to ever really gain new features:

  • For sequences of options or “nesting” of menus so that you can use dmenu either as a prompt for occasional interaction or simply as an easy way to help users navigate a tree-like structure.

    I think this is cool, but it feels like something that you could do in downstream code more easily and clearly.

  • I think it’d be cool if you could define a bunch of options, and not have to write the glue code between “the selection” and the option you have. Write the command/options as a map of strings to functions, and then call dmenu for the keys in the map and run the options.

    Seems cool to have routing/dispatching driven by dmenu, and totally possible, though again, this is probably shouldn’t be a core feature.

  • The one exception to the “no new features ever” rule, might be to have an alternate top-level call that takes configuration as functional arguments rather in the structured form.

    Done, oops.


Anyway, give it a try!

And, if you thought this was cool, stay tuned, I’ve got a few more blog posts in the works, more thoughts on this kind of “tools for ‘personal computing’” software that I’ve been working on!

New Go Module: tychoish/fun

This is a follow up to my New Go Modules post about a project that I’ve been working on for the past few months: github.com/tychoish/fun.

fun is a collection of simple libraries using generics to do a collection of relatively mundane things, with a focus on well-built tools to make it easier for developers to solve higher level problems without needing to re-implement low level infrastructure, or use some rougher parts of the go standard library. It has no dependencies outside of the standard library, and contains a number of pretty cool tools, which were fun to write. Some of the basic structures were:

  • I wrote linked list implementations (single and double), adapted a single-ended Queue implementation that a former coworker did as part of an abandoned branch of development, and wrote a similar Deque. (fun/pubsub)

  • I adapted and developed a “broker” which uses the queues above to be able to do one-to-many pubsub implementations. (fun/pubsub)

  • I made a few high-level standard library synchronization tools (sync.Map, sync.WaitGroup, sync.Pool, and atomic.Value) even more higher level, with the help generics and more than a few stubborn opinions. (fun/adt; atomic data types)

  • I revisited an error aggregation tool that I wrote years ago, and made it a bunch faster, less quirky, and more compatible with the current state of the art with regards to error handling (e.g. errors.Is and errors.As). (fun/erc). I also wrote some basic error mangling tools including a more concise errors.Join, tools to unwind/unwrap errors, and a simple error type ers.Error to provide for constant errors (fun/ers)

  • I wrote an iterator interface and a collection of function wrappers and types (in the top level package), for interacting with iterators (or really, streams in one way or another,) and decorating those handlers and processors with common kinds of operations, modifications, and simple semantics.

    I don’t know that it will catch on, but I’ve written (and rewritten) a lot of worker pools and stream processing things to use thse tools, and it’s been a useful programming model. By providing the wrappers and the iteration, users can implement features almost functionally (which should be easy to test.)

  • I built a collection of service orchestration tools to manage long running application services (e.g. http servers, worker pools, long running subprocesses, etc.) and a collection of context helpers to make it easier to manage the lifecycle of the kind of long-running applications I end up working on most of the time. Every time I’ve joined a new project… ever, I end up doing some amount of service orchestration work, and this is the toolkit I would want. (fun/srv)

  • I wrote some simple testing frameworks and tools for assertions (fun/assert) halt-on-failure, but with a tesitfy-inspired interface, and better reporiting along with a mirrored, fail-but-continue fun/assert/check, along with fun/testt (“test-tee” or “testy”) which has a bunch of simple helpers for using contexts, and fun/ensure, which is a totally different take on an assertion library.

I don’t want this post to be documentation about the project; there are a lot of docs in the README and on the go documentation, also the implementations are meant to be pretty readable, so feel free to dive in there. I did want to call out a few ways that I’ve begun using this library and the lessons it’s taught me in my day to day work.

  • I set a goal of writing code that’s 100% covered by tests. This is hard, and only possible in part because it’s a stateless library without dependencies, but I learned a lot about the code I was writing, and feel quite confident in it as a result.

  • I also set a goal of having no dependencies outside of the module and the standard library: I didn’t want to require users opt in using a set of tools that I liked, or that would require on going maintenance to update and manage. Not only is this a good goal in terms of facilitating adoption, it also constrained what I would do, and forced me to write things with external extensibility: there had to be hooks, interfaces and function types had to be easy to implement, and I decided to limit scope for other things.

  • Having a more complete set of atomic types and tools (particularly the map and the typed atomic value, also the typed integer atomic types in the standard library are great), has allowed me to approach concurrent programming problems without doing crazy things with channels or putting mutexes everywhere. I don’t think either channels or mutexes are a problem in the hands of a practiced practitioner, but having a viable alternative means it’s one less thing to go wrong, and you can save the big guns (mutexes) for more complex synchronization problems.

  • Linked lists are super cool. I’ve previously taken the opinion that you shouldn’t implement your own sequence types ever, and mostly avoided writing one of these before now. Having now done it, and now having truly double-ended structures means things like “adding something to the beginning” or “inserting into/removing from the middle” of a sequence isn’t so awkward.

    The experimental slices library makes this a little less awkward with standard library slices/arrays, and they are proably faster.

  • It was really fun to take the concept of an interator and then build out from this concept to build tools that would make them easy to use, and got some good filtering, parallel processing, and map/reduce tools. I definitely learned a bunch but also I think (and have found) these tools useful.

  • I’ve long held that most go applications should be structured so that you shouldn’t really need to think too much about concurrency when writing business logic. I’ve previously tried to posit that the solution to this was to provide robust queue processing tools (e.g. amboy), but I think that’s too heavy weight, and it was cool to be able to think about the solution to this concept from a different angle.

Anyway, give it a try, and tell me what you think! Also pull requests are welcome!

New Go Modules

I’ve been doing a little big of programming for fun in recent months, and I wanted to do a little big of blogging here to explain myself, so expect that over the next few days.

By way of creating the facade of suspense--which the astute among you will be able to spoil by way of looking at my github--this post isn’t going to mention the actual projects, but rather answer two questions that are more interesting from a higher level:

  • Why have you done this?
  • What have you learned about yourself as a programmer?

Why Have You Done This?

First, while I’ve been writing Go for a long time, I must confess that I was a long time generic skeptic and hold out. Writing some code for myself and I wanted to get a better feeling for how they worked, now that they’re here.

In my day to day work, I have found myself writing similar code again and again: while I have definitely have a class of problems that I tend to work on, I’d be ok if I never wrote another function to bridge the gap between a [context.Context]{.title-ref} and a [sync.WaitGroup]{.title-ref} (and I’m not holding my breath for this in the standard library.)

Finally, over the years I’ve written a few projects that I’ve worked on professionally have been open source, and a few of them I’ve even rather liked, and I wanted to see what it would be like to revisit some of these projects with (potentially) wiser eyes and fingers.

What Have You Learned?

I actually think I’ve learned a lot:

  • While I’m a big fan of the part of software development which is “deleting code” in my professional work, it was really interesting to take that to code that I knew I’d written (or directed the writing of) and been able to see what I could cut.
  • I’m better at writing tests and writing testable code than I was even a few years ago. While I believe in writing defensive code, I found myself writing an implementation and then going through and deleting a lot of code that wasn’t useful or was (fundamentally) superstitious when I discovered that there was no way to trigger a potential case.
  • Because I’ve spent a few years mostly working on relatively high level projects and not having the bandwidth to work on lower (mid?) level infrastructural code, in practice I’ve spent more time assessing other people’s libraries and I was keenly aware that I was developing code that would only be used if someone chose to, and in thinking about how those decisions are made.
  • This isn’t new, but I care a lot about the erognomics of software. I think 5 or 6 years ago, ergonomics meant “code which was easy to use and provented users from doing wrong things,” and I think my view of erognomic code has expanded to include interfaces that are easy and obvious to use, and promote the users of my code to write better code.