There are only two hard things in Computer Science: cache invalidation and naming things.Phil Karlton
Two hard things, but they are not the same kind of hard. Naming things we do it every day. And we read and re-read the things we name all day long.
This is a post about names. And ambiguity. And pain. And how to iterate on that to get something better at the end of the day.
And I’ll illustrate some ideas with the OSWindow project.
An OSWindow has a handle…
OSWindow has a
But let’s first start by saying what an
OSWindow is. An
OSWindow is an object representing an operating system window in an uniform way. Behind the scenes, an
OSWindow uses some backend like WinForms in Windows or Cocoa in OSX. The main backend implementation we have in Pharo uses SDL2, a cross-platform library, mainly designed for game dev, that can be used for windowing.
So, let’s start again. An
OSWindow has a
handle. From the explanation above, you may think the
handle is a pointer to a real window implemented in the backend framework. Actually,
handle is indeed the name we use in pharo to designate a pointer to some externally managed memory region. For those used to FFI, a handle usually designates an
ExternalAddress or pointer (And for those who aren’t you may take a look here).
However, regardless of our first impressions on this, reading the code shows another thing. The code manipulating this
handle does not use it as an
ExternalAddress, because the only thing we can do with external addresses is to read or write them (or operate with them, they are addresses after all). For example, let’s take the following piece of code from the
handle is finally not an FFI thingy as we thought! It’s more like a backend object representing the real window. Of course, the different native windows are manipulated through different sets of functions. And it seems it was decided to manage these differences as a composition of objects instead of inheritance, which we will not discuss nor argue today. Today we focus on names.
It turns out that these backend objects are also called handles. We have for example
OSNullWindowHandle. This seems consistent with what we saw before. So, an
OSWindow actually has an
OSWindowHandle and not an FFI handle. I think we now understand enough to keep reading more code…
Let’s now dive in the
Wait! What!? WAT? The
OSSDL2WindowHandle has another
handle. But this cannot be an
Or maybe now we did arrive to an
Again, reading the code tells us it is neither… For example the following pieces of code sending messages to the handle that are implemented in none of the classes we expect:
OSSDL2WindowHandle >> toggleBorderOn [ handle toggleBorder: true. ] OSSDL2WindowHandle >> getWMInfo [ | wmInfo | wmInfo := SDL_SysWMinfo new version: SDL_Version bindingVersion. handle getWMInfo: wmInfo. ^ wmInfo ]
Following a bit the code, we can see this second handle is actually an
SDL2Window implemented as an
FFIExternalObject. And then I, used to uFFI conventions, know that
FFIExternalObjects have a handle themselves, the real handle we were looking for from the beginning.
handle of the
handle is an external object, which has a
handle has a
handle with a
handle. What could go wrong here? 🙂
I don’t know you, but I felt pretty lost when reading this code, because of this overloaded terminology. The same name is used to designate three different things. And those different things are in overlapping domains! So when reading
handle we need to actively think where we are and what it is designating here and now. And for worse, this problem of overloaded terminology does not stop at the name
handle, I’ve found also that
window could designate an
OSWindow or an
OSWindowHandle. So what do you think the following expression returns?
Take your pick 🙂
Getting the names on the handles
I decided to fix this name confusiology in this Pull Request by doing a couple of renames. I chose new names, that you may not agree with btw, following a couple of guidelines:
- avoid ambiguity: three different names for three different non-interchangeable concepts
- use generic names when possible, but staying in the domain terminology
- use concrete names when variables will only reference objects of a single type/class
Reading the code and understanding the domain helped a bit in choosing the names, which now make OSWindow domain read as:
An OSWindow has a backend window, instance of OSBackendWindow. An OSSDL2BackendWindow has an sdl2Window (no need to say the class ;)). Moreover, all objects designate OSWindows as window, and OSBackendWindow as backend windows .
Name the next step!
Do you agree with the names I choose? Please share yours! Discussing names is the only way to find better ones :). And we can always iterate and improve more. No design should be carved in stone.
In the meantime, I’ll keep improving OSWindow, we should have good support for multiple native windows soon.
I hope you enjoyed and that you give good names in your programs. For your future self. Or at least for me, if I have to read your code 🙂