Automatic UML diagrams for Dart

Most of the code I write at work is in the Dart programming language. Specifically, I’ve worked on some internal tooling that involved the static analysis libraries that ship alongside the language SDK. So when a colleague mused that he would like to be able to automatically generate UML diagrams for some of our libraries, I jumped on it. The result was the DCDG package and tool.

The first step is to install it:

$ pub global activate dcdg

Run it like this:

$ dcdg --help

You may need to add Dart’s package directory to your path before this will work, otherwise you can use pub global run dcdg instead.

To use the tool, navigate to a Dart on your file system and run the tool. By default, this will print the PlantUML markup for a pretty standard UML diagram for the package in question. You can either redirect this output to a file (dcdg > output.puml) or use the -o option to specify an output file (dcdg -o output.puml).

To turn the markup into a PNG image, just run the plantuml command on the file you created with DCDG (plantuml output.puml), this will create a file called output.png. You can also redirect the DCDG output directly into PlantUML by using the -p option, in which case the PNG image will be written to stdout, so you’ll need to redirect it to a file yourself:

$ dcdg | plantuml -p > output.png

There are a plethora of options (perhaps too many, actually). Let’s take a look at a few of them. First, it can be helpful to see just the public API for a package represented as a UML diagram, particularly if the diagram is to be included in public documentation. This is easily accomplished with the --exported-only flag:

$ dcdg --exported-only | plantuml -p > output.png

This will produce the image shown below when run on the DCDG codebase itself. DCDG doesn’t export very many classes since it is primarily intended to be used as a command line tool.

dcdg --exported-only | plantuml -p > output.png

Alternatively, someone might want to see a diagram with more detail, particularly if they plan to contribute to the code. However, we may want to exclude certain features, such as private class members, from the diagram to reduce clutter.

$ dcdg --exclude-private=field,method | plantuml -p > output.png
dcdg --exclude-private field,method | plantuml -p > output.png

It is also possible to pare the diagram down to include only classes that inherit behavior from a particular class. This is done using the --is-a option (which can be provided more than once). The example below includes only the inheritance tree rooted at the abstract DiagramBuilder class from DCDG.

dcdg --is-a DiagramBuilder | plantuml -p > output.png

There are quite a few other options as well. See dcdg --help for a full list. The source code can be found on GitHub, bug reports and pull requests are welcome.

Being null-aware

Null, as implemented in Java, JavaScript, and other similar languages, is… troublesome. But why? It doesn’t seem so bad to hear it described. If a variable is a container, a null value just means that the container is empty. This is a perfectly natural state of affairs, so what’s the big deal?

There are a couple ways to look at this issue. The one that speaks to me most clearly is to think about the impact of null values on the types used by our programs and what they mean.

Let’s say we have the following class and function:

class Person {
  String firstName;
  String lastName;
}

void sayHello(Person p) {
  print("hello there, " + p.firstName + "!");
}

The types here represent a contract between the programmer who wrote the function and the programmers who will call the function. The contract says that the caller can pass an instance of Person into the sayHello function and the computer will print a friendly, personalized greeting. If the caller passes anything other than a Person, the function isn’t guaranteed to work right. In most statically typed languages it won’t even compile / run.

Since the function will only ever see instances of Person there’s nothing wrong with immediately referencing the firstName field. After all, a Person has a firstName, that’s part of what it means to be a Person!

But if our language allows null values, the caller could also pass null into this function. This is where the trouble begins: null doesn’t have a firstName field (even though it is a perfectly legal value in any place where a Person is accepted) and the function above will crash if it receives a value with no firstName field!

Effectively, the function above doesn’t accept a Person, it accepts a Person | null (which can be read “person or null”). This is called a sum type or a union type. So every type in this hypothetical language is actually itself or null.

But we’re not even finished, it gets worse. In languages that support actual union types, the compiler / interpreter forces the programmer to handle each of the possible types that compose the union. The author of the function above was allowed to totally ignore the possibility that the function might receive a null value. A similar function that handles null appropriately is shown below:

void sayHello(Person p) {
  if (p == null) {
    print('hello there!');
  } else {
    print("hello there, " + p.firstName + "!");
  }
}

The problem with this solution is twofold. First, languages like Java don’t require this sort of defensive programming, and this has a tendency to cause bugs. Second, it’s impossible to tell if the null check is actually necessary. What if the caller has already checked for a null value?

// ...
Person person = getPerson();
if (person == null) {
  // ...
} else {
  sayHello(person);
}
// ...

In this case, the value passed to sayHello will never be null, so one of the checks is unnecessary. But which one should be removed? That’s unclear. To get around this problem, we can define a new type called Person?. We will give the new type the exact same behavior as the current version of Person, that is, we will allow variables of this type to contain null. Then, we will change the meaning of Person to exclude null as a possible value. I’ve summarized below.

Person p0 = null; // ERROR
Person p1 = new Person(); // allowed
Person? p2 = null; // allowed
Person? p3 = new Person(); // allowed

Now, when someone calls the sayHello function, they must provide an instance of the Person class, and they may not provide a null value. This means that we can safely revert to the original version of the function without the null check. It also means that any null check, if one is necessary, will have to be performed by the caller.

If we would prefer to allow null values then we can change the type to Person?. However, since we have now declared that we accept null values, the compiler or interpreter can force us to check for null, just like it would if we declared the type to be Person | null in a language with union types.

void sayHello(Person? p) {
  print(p.firstName); // ERROR - forgot to check for null
}

So why does this matter? Programming is mostly a human problem. We are almost always the weakest link. This means that anything we can do to make our code easier to write, easier to read, and easier to use, is likely to prevent bugs today and help us add new features tomorrow. By specifying clearly the contracts implied by our code we can produce better software, more easily.

Grouping Tabs

This evening I wrote and uploaded my first Chrome extension (well, technically I wrote one a few years ago, but I never really finished it).

What does it do? It lets you group related tabs together and keeps them grouped together.

Why would anyone want to do this? At any given moment while I’m at work I’m monitoring at least two or three pull requests. I try to keep their corresponding tabs grouped together for easy access, but inevitably they become lost among the 20-30 other tabs I have open.

I could pin them, but that changes the semantics of the tabs themselves and hides the title (even when the title would otherwise be visible). So I have my email and calendar pinned, because I never close those. But I wanted an intermediate state for things like pull requests. Enter “Pseudo Pins“.

Pseudo Pins allows the user to specify one or more regular expressions, which are then matched against the URLs of the tabs in each window. Tabs matching a given expression are pulled to the left and grouped together. The leftmost tabs then correspond to the first regular expression in the list, and so on rightward. The list of expressions is persisted across browser sessions (and will sync across devices if Chrome is set up to do so).

The GitHub repo is here if you are interested: https://github.com/glesica/pseudo-pins

Emacs.

The other day I opened up Vim and a bunch of formatting was messed up and things weren’t refreshing properly. Some update had probably broken something. Then I realized that my Vim config was a massive mess (you never realize stuff like that until something breaks).

I’d intended to switch to Emacs eventually, it had been kind of an elaborate dance, but I had always suspected I would end up there. I really like the idea of Lisp and I think using Emacs is actually one of the better ways to get comfortable with it, plus it’s a decent editor, or so I hear.

So now, perhaps sooner than expected, I am an Emacs user (since a couple weeks ago). I’ve got several friends helping me out and providing suggestions, and I’ve already got quite a bit of useful stuff set up. My configuration is on GitHub, because why not?

So far I am quite pleased, but wow, this is going to be a long, interesting journey.

Image credit: XKCD: Real Programmers

To Mac or not to Mac

I have been a loyal GNU/Linux users since Ubuntu 5.04 (side rant, I have no idea what stupid animal name it had and it drives me crazy that people insist on referring to them by their codenames). Over the years I owned two ThinkPads, a T61 and then later a T430s. I bought ThinkPads because they would “Just Work” with virtually all GNU/Linux distributions.

Recently, however, when it came time for a new laptop I bought a Mac and switched to OSX. I made this choice for three reasons.

First, the quality of the ThinkPad hardware, at least for my purposes, has been falling. You might have noticed, if you’re familiar with ThinkPad model numbers, that I had my T61 for quite a few years, but the T430s is still only one generation old. Why did I replace it so soon? It turned out that if you spill even a tiny amount of liquid (a few drops, caused by dropping a cookie into some milk) in the right place on a 430 series ThinkPad, the trackpad, and the TrackPoint device will stop working, permanently. In fact, if you don’t then disable their drivers in the kernel, you can’t even use the keyboard reliably. To me, this is the result of poor design. I had my T61 for so many years because it stood up to the occasional minor accident.

The second reason I bought a Mac, and this might be the most important, is the battery life. Back when I used a desktop computer I didn’t care much about power efficiency. When I started using a laptop, it was such a step up that plugging in everywhere I went didn’t really bother me. But more recently I found myself frustrated that I was basically tied to the nearest outlet everywhere I went. A MacBook is effectively a giant battery with a computer strapped to it, and that’s just fine with me.

Finally, screen quality played a role in my decision. Back when I bought my T61, pretty much all laptops had dim, washed-out screens. But I expected better by the time I bought my T430s. Unfortunately, Lenovo didn’t deliver. Many, many years ago I owned a Toshiba Satellite with a passive matrix display (the kind where the mouse pointer would get “lost”). I didn’t mind because it was a laptop and that was basically the coolest thing in the world. But my eyes aren’t what they once were, and I actually have real work to do now, so fiddling with (and squinting at) a laptop display is no longer on my list of acceptable activities.

I hope to return to GNU/Linux at some point in the future. But until the hardware ecosystem works itself out, I’ll be sticking with a Mac.