Date: 2020-05-21
State: Done
I've been wanting to start on a graphical client for Gemini for a while. Why? Well, partly for fun, of course, but also I felt the currently available graphical clients weren't quite what I was looking for. Most clients seemed to:
1. Depend on GTK
2. Use Rust
3. Be quite difficult to setup on MacOS (not that I have a particular fond love of the platform (in fact, I'm beginning to quite heavily dislike it), but because that is the most powerful machine I have available to me at no cost, and even it is 5 years old.)
So I spent some time thinking about what I wanted in a client, engaging in the time honored tradition of complaining about how other people actually got things done and instead, philosophizing. Things I wanted:
1. Easy to set up on as many platforms as possible
2. Light on resource usage
3. Endlessly customizable
I want to really emphasize #3. HTML, while originally a markup language, has a very specific way which browsers render it. In fact, engineers on large products spend endless amounts of time ensuring that the rendered markup looks the way they want it to on various platforms. I don't know what the conditions for the original were like, but I would love to see a platform where the _user_ has control over how the document is rendered. But how? A large config file? A hierarchical property store, similar to Firefox's configuration interface? Instead:
A dynamic evaluation environment. Think a Lisp system for displaying Gemini pages (well... you don't have to think; Elpher is an Elisp Gemini Browser and it is damn good). So I came up with a thought: write code to actually do rendering, but host a Lisp on top of that so that the user can configure the browser through Lisp. Allow the user to hook however they would like into the render loop, so the user has maximum flexibility on how to use the browser. The browser can ship with sane defaults, which casual users will probably barely touch (perhaps just changing background colors, e.g. preferring "dark mode"). More advanced users can create functions and bind them to button callbacks, or to line render calls. The possibilities were endless!
When evaluating widget toolkits, I preferred Tk to Gtk approaches. Gtk was a bit complicated to distribute on older Windows systems, and could often have real issues on MacOS, unless Gtk was shipped into the binary (which would be a time consuming process). Moreover, Tk had bindings in a huge variety of languages, which kept the pool quite open.
Armed with this vision in mind, I set out to play around with architectures that fit the vision. My first attempt was structured as:
1. Nim for the skeleton render loop and widget render hooks
2. Tk widgets for display purposes
3. An embeddable Lisp to host on top of this, which hooked into Nim.
While this sounded nice in theory, the whole thing fell on its face. Nim did not have Tk bindings, only Tcl bindings. While trying to port Tk into Nim, I ran into difficulties of having little-to-no understanding of the Tk render loop, let alone the Tcl language. I decided to abandon the approach, and try other Nim GUI toolkits.
It turns out Nim just does not have any mature GUI toolkits that I could build easily on my MacOS. Each of them either had difficulties building, or would throw an exception before rendering a widget. I took another stab at generating Tk bindings, but after some initial success with the Tk main loop, I realized that porting the widgets over would be a slog. I think it would be fun to finish a proper Tk binding in Nim one day, but I wanted to focus on the browser first, and Tk second.
My next attempt was to look at McRoss, the Python/Tkinter editor. I attempted to follow the instructions, but pyenv would keep building a Python version that linked against the system Tk libraries instead of the newer version of Tk I had downloaded. This would cause the Python interpreter to crash. I finally managed to build McRoss by running it without poetry or pipenv, and making a virtualenv the old fashioned way and relying on pip to install libraries I didn't have, on the same version of Python my system had installed and linked correctly.
I was able to install McRoss and run it. While I enjoyed it, I really wanted to offer a less "raw" experience than McRoss, and eventually wanted to embed a Lisp anyway, so decided McRoss wasn't the browser I wanted. Given that I know Python quite well, I settled in to forking McRoss and working from there, and began reading about Hy and using it in Python when I had a thought.
Wait a second. Every informed opinion of Tcl I've read online alludes to the fact that Tcl is, itself, a Lisp. If Tcl was a Lisp, then to satisfy the goals I had specified earlier, it felt that I was fitting a square peg into a round hole by ignoring the language Tcl itself, which is built for seamless operation alongside Tk. This spur me on to try Tcl, and indeed, it does feel very Lisp like.
I've decided to develop this browser in Tcl/Tk.
1. Tcl/Tk seems to run on every operating system and all(-ish) hardware, maybe even a toaster or two.
2. Tcl certainly is as dynamic as I would want out of a Lisp for my purposes. While I still have to learn the language better to get a better understanding of the ergonomics, I have some ideas to offer easy hook points for a user config to hook into. Ergonomics here will be an ongoing process, so we'll see what I end up coming up with.
3. Tcl is actually fairly fun to use in practice. Like any heavily dynamic language, its abuse potential is incredibly high, but with care, it can be a highly productive language in the hands of a restrained developer.
That said, Tcl certainly has some downsides:
1. The documentation is almost unsearchable. While the reference is laid out well, learning about language constructs is an exercise in link following, and none of the easily available online resources seem to have any idioms on hand to learn how to develop Tcl. The uri package talks about registering new schemes, but does _not_ go into enough detail to discuss how to do that. Luckily, reading Tcl is fairly easy, so I was able to go through the source and find the implementation of Uri and found out what I needed to register a new scheme.
2. The standard library can feel inconsistent. Different portions of the language seem to have different feelings to them. Arrays feel like a DSL of their own, and the set and get quoting rules can feel a bit byzantine at times. When using different packages in the standard library, it rarely feels like you're using the same language. (N.B. Perhaps this is not a bug but a feature? Lisps are well known for their penchant for creating bespoke DSLs.)
3. Design decisions can often seem bizarre. Most languages these days, from Haskell, to Prolog, to Python, and Go, seem to have a consistent set of semantic concepts when dealing with arrays (grabbing elements at an index), lists (iterating through lists, applying transforms), and similar structures. While Lisps can be more verbose, they too often have the same concepts. Tcl often only has some subset of these semantic concepts, and operations which are easy in other languages need bookeeping (string indexing) in Tcl.
I'm excited to see what Tcl has in store and how my browser develops. I'm close to finishing the actual client logic, and have a mock up of the UI in Tk done already. Soon I'll connect the two and begin to refine this. After that, work will begin on the points of extension.