Structure and Interpretation of Computer Programmers

I make it easier and faster for you to write high-quality software.

Monday, October 19, 2020

Discipline doesn’t scale

If programmers were just more disciplined, more professional, they’d write better software. All they need is a code of conduct telling them how to work like those of us who’ve worked it out.

The above statement is true, which is a good thing for those of us interested in improving the state of software and in helping our fellow professionals to improve their craft. However, it’s also very difficult and inefficient to apply, in addition to being entirely unnecessary. In the common parlance of our industry, “discipline doesn’t scale”.

Consider the trajectory of object lifecycle management in the Objective-C programming language, particularly the NeXT dialect. Between 1989 and 1995, the dominant way to deal with the lifecycle of objects was to use the +new and -free methods, which work much like malloc/free in C or new/delete in C++. Of course it’s possible to design a complex object graph using this ownership model, it just needs discipline, that’s all. Learn the heuristics that the experts use, and the techniques to ensure correctness, and get it correct.

But you know what’s better? Not having to get that right. So around 1994 people introduced new tools to do it an easier way: reference counting. With NeXTSTEP Mach Kit’s NXReference protocol and OpenStep’s NSObject, developers no longer need to know when everybody in an app is done with an object to destroy it. They can indicate when a reference is taken and when it’s relinquished, and the object itself will see when it’s no longer used and free itself. Learn the heuristics and techniques around auto releasing and unretained references, and get it correct.

But you know what’s better? Not having to get that right. So a couple of other tools were introduced, so close together that they were probably developed in parallel[*]: Objective-C 2.0 garbage collection (2006) and Automatic Reference Counting (2008). ARC “won” in popular adoption so let’s focus there: developers no longer need to know exactly when to retain, release, or autorelease objects. Instead of describing the edges of the relationships, they describe the meanings of the relationships and the compiler will automatically take care of ownership tracking. Learn the heuristics and techniques around weak references and the “weak self” dance, and get it correct.

[*] I’m ignoring here the significantly earlier integration of the Boehm conservative GC with Objective-C, because so did everybody else. That in itself is an important part of the technology adoption story.

But you know what’s better? You get the idea. You see similar things happen in other contexts: for example C++’s move from new/delete to smart pointers follows a similar trajectory over a similar time. The reliance on an entire programming community getting some difficult rules right, when faced with the alternative of using different technology on the same computer that follows the rules for you, is a tough sell.

It seems so simple: computers exist to automate repetitive information-processing tasks. Requiring programmers who have access to computers to recall and follow repetitive information processes is wasteful, when the computer can do that. So give those tasks to the computers.

And yet, for some people the problem with software isn’t a lack of automation but a lack of discipline. Software would be better if only people knew the rules, honoured them, and slowed themselves down so that instead of cutting corners they just chose to ignore important business milestones instead. Back in my day, everybody knew “no Markdown around town” and “don’t code in an IDE after Labour Day”, but now the kids do whatever they want. The motivations seem different, and I’d like to sort them out.

Let’s start with hazing. A lot of the software industry suffers from “I had to go through this, you should too”. Look at software engineering interviews, for example. I’m not sure whether anybody actually believes “I had to deal with carefully ensuring NUL-termination to avoid buffer overrun errors so you should too”, but I do occasionally still hear people telling less-experienced developers that they should learn C to learn more about how their computer works. Your computer is not a fast PDP-11, all you will learn is how the C virtual machine works.

Just as Real Men Don’t Eat Quiche, so real programmers don’t use Pascal. Real Programmers use FORTRAN. This motivation for sorting discipline from rabble is based on the idea that if it isn’t at least as hard as it was when I did this, it isn’t hard enough. And that means that the goalposts are movable, based on the orator’s experience.

This is often related to the term of their experience: you don’t need TypeScript to write good React Native code, just Javascript and some discipline. You don’t need React Native to write good front-end code, just JQuery and some discipline. You don’t need JQuery…

But along with the term of experience goes the breadth. You see, the person who learned reference counting in 1995 and thinks that you can only really understand programming if you manually type out your own reference-changing events, presumably didn’t go on to use garbage collection in Java in 1996. The person who thinks you can only really write correct software if every case is accompanied by a unit test presumably didn’t learn Eiffel. The person who thinks that you can only really design systems if you use the Haskell type system may not have tried OCaml. And so on.

The conclusion is that for this variety of disciplinarian, the appropriate character and quantity of discipline is whatever they had to deal with at some specific point in their career. Probably a high point: after they’d got over the tricky bits and got productive, and after you kids came along and ruined everything.

Sometimes the reason for suggesting the disciplined approach is entomological in nature, as in the case of the eusocial insect the “performant” which, while not a real word, exists in greater quantities in older software than in newer software, apparently. The performant is capable of making software faster, or use less memory, or more concurrent, or less dependent on I/O: the specific characteristics of the performant depend heavily on context.

The performant is often not talked about in the same sentences as its usual companion species, the irrelevant. Yes, there may be opportunities to shave a few percent off the runtime of that algorithm by switching from the automatic tool to the manual, disciplined approach, but does that matter (yet, or at all)? There are software-construction domains where specific performance characteristics are desirable, indeed that’s true across a lot of software. But it’s typical to focus performance-enhancing techniques on the bits where they enhance performance that needs enhancing, not to adopt them across the whole system on the basis that it was better when everyone worked this way. You might save a few hundred cycles writing native software instead of using a VM for that UI method, but if it’s going to run after a network request completes over EDGE then trigger a 1/3s animation, nobody will notice the improvement.

Anyway, whatever the source, the problem with calls for discipline is that there’s no strong motivation to become more disciplined. I can use these tools, and my customer is this much satisfied, and my employer pays me this much. Or I can learn from you how I’m supposed to be doing it, which will slow me down, for…your satisfaction? So you know I’m doing it the way it’s supposed to be done? Or so that I can tell everyone else that they’re doing it wrong, too? Sounds like a great deal.

Therefore discipline doesn’t scale. Whenever you ask some people to slow down and think harder about what they’re doing, some fraction of them will. Some will wonder whether there’s some other way to get what you’re peddling, and may find it. Some more will not pay any attention. The dangerous ones are the ones who thought they were paying attention and yet still end up not doing the disciplined thing you asked for: they either torpedo your whole idea or turn it into not doing the thing (see OOP, Agile, Functional Programming). And still more people, by far the vast majority, just weren’t listening at all, and you’ll never reach them.

Let’s flip this around. Let’s look at where we need to be disciplined, and ask if there are gaps in the tool support for software engineers. Some people want us to always write a failing test and make it pass before adding any code (or want us to write a passing test and revert our changes if it accidentally fails): does that mean our tools should not let us write code for which there’s no test? Does the same apply for acceptance tests? Some want us to refactor mercilessly; does that mean our design tools should always propose more parsimonious alternatives for passing the same tests? Some say we should get into the discipline of writing code that always reveals its intent: should the tools make a crack at interpreting the intention of the code-as-prose?

posted by Graham at 20:14  

Wednesday, December 27, 2017

Computing’s fundamental Principle of No Learning

I haven’t used Taligent’s frameworks or operating systems directly; what I know of it comes from their documentation and the book Inside Taligent Technology. I put some small effort into finding out whether it’s possible to use the Taligent system legitimately on a modern computer, couldn’t find a way, and didn’t get too sad about that.

The Taligent frameworks look – assuming they were ever as complete as the documentation promised – like fairly standard 1990s OOP-in-C++ which almost certainly makes them less fun to use than modern Qt. What I got from considering Taligent was a reflection on trends in modern computing platforms, that discoverability is killing power user features, and the app icon is killing complex workflows.

There is a false dichotomy at work in modern app design: the drive is for apps to be so simple you can use them as soon as you’ve tapped the app icon, but this is taken to mean that there doesn’t need to be anything more to do in the app than what you can see when you have tapped the app icon. As an example, the bookmark manager app on my iPad shows me a list of my bookmarks, and there’s an “add” button. If I click on a bookmark, I can view it. That’s it. That’s “managing” bookmarks, app-style.

But I would suggest it’s not just the apps that are doing it, it’s the platforms too, and that’s how Taligent entered this story. Their central desktop metaphor was called People, Places, and Things, based on the idea that I might want to focus on those things mediated by a computer, rather than on applications that support particular use cases.

A particular example of the People metaphor is that I might want to show a photo of my cat to my sister. Back in the days of the real world, I would do that by showing the photo of my cat to my sister. In the brave new world of the app, I look at my phone to see what messaging apps I have, try to remember which ones I have her as a contact on, which ones she will actually check, whether they send pictures and if so, what that costs or how it futzes with the picture. I launch that, go to the conversation with her (or start one), add an attachment, try to think about whether this needs to be a Camera, Gallery or File attachment, and so on. This is made most painful when using the in-car voice UI on my smartphone, which seems able to transcribe messages well but unable to listen when it asks what app I want it to use to send the message. Like I care, cost and picture-futzing aside (though I’m not usually taking photos with the in-car voice UI).

The Places metaphor is particularly interesting. Each “place” is a virtual gallery, town hall, office lobby, project office, or whatever that contextualises a given group of People and Things relevant to the place. In implementation, it’s a sort of context-sensitive desktop management. In concept, it makes me think of the PARC Ubiquitous Computing principle and how a location-sensitive computing environment could adapt as I sit at my desk, join some colleagues in a meeting room, or collapse on a beanbag at home. It makes me think of the Eudaimonia machine and how a context-sensitive computing interface could support shallow-and-broad to narrow-and-deep work environments. Then I consider how the static home screen on my smartphone…does not do those things.

An important part, I believe, in the downfall of People, Places and Things is what it means for software. Rather than buying “an application” from a vendor that shows me their beautiful, crafted icon until I’m ready to launch it, I buy a capability that extends the ways in which I can interact with the People, Places and Things represented on my computer. My computer does not become Photoshop, rather I gain Photoshopness by adding it to my computer.

It seems that any shift from app-centric to goal-centric computer interfaces – PPT, OpenDoc/OLE, NeXT Services, scripting/automation tools, the spatial desktop – is going to violate the Fundamental Paradox of App Marketing: my app is so intuitive and easy to use that it just fades into the backdrop of your life, however it must never fade so far that you do not see its name or its icon, and are not reminded who we are and how much we help you.

Similarly, they violate the Principle of No Learning discussed above: I must be able to see how to do everything, and I must only be able to do everything that can be seen. The desktop metaphor allows for spatial arrangement of your work, just like strewing papers around a real desktop, but you need to work out what those funny line-and-square icons mean and which bits of the window frame are draggable, so that goes. Drag-and-drop makes for a visual placement metaphor letting me “put” work from one document into another, but you can’t obviously see that it’s a thing (nor indeed what it will actually achieve; Drag and Drop seems intuitively destructive, sometimes is, but sometimes surprisingly isn’t. What’s the metaphorical outcome expected when you try to put your Hackers DVD in the Trash?). Therefore drag-and-drop is replaced with “Share Sheets”. Scripting not only requires learning a new application – the script editor – but a whole new user interface for existing applications. Forget it.

The Paradox of App Marketing is merely misguided self-aggrandisement, but the Principle of No Learning is one of computing’s big, stultifying, patronising, antiethical mistakes. It makes out that computers are simplistic tools that can do the things delivered unto the users by the app makers, and nothing else. It hides the magic behind the curtain by removing the curtain and the magic.

posted by Graham at 21:28  

Thursday, January 2, 2014

ClassBrowser: warts and all

I previously gave a sneak peak of ClassBrowser, a dynamic execution environment for Objective-C. It’s not anything like ready for general use (in fact it can’t really do ObjC very well at all), but it’s at the point where you can kick the tyres and contribute pull requests. Here’s what you need to know:

Have a lot of fun!

ClassBrowser is distributed under the terms of the University of Illinois/NCSA licence (because it is based partially on code distributed with clang, which is itself under that licence).

posted by Graham at 21:43  

Wednesday, December 18, 2013

A sneaky preview of ClassBrowser

Let me start with a few admissions. Firstly, I have been computering for a good long time now, and I still don’t really understand compilers. Secondly, work on my GNUstep Web side-project has tailed off for a while, because I decided I wanted to try something out to learn about the compiler before carrying on with that work. This post will mostly be about that something.

My final admission: if you saw my presentation on the ObjC runtime you will have seen an app called “ClassBrowser” where I showed that some of the Foundation classes are really in the CoreFoundation library. Well, there were two halves to the ClassBrowser window, and I only showed you the top half that looked like the Smalltalk class browser. I’m sorry.

So what’s the bottom half?

This is what the bottom half gives me. It lets me go from this:

ClassBrowser before

via this:

Who are you calling a doIt?

to this:

ClassBrowser after

What just happened?

You just saw some C source being compiled to LLVM bit code, which is compiled just-in-time to native code and executed, all inside that browser app.

Why?

Well why not? Less facetiously:

  • I’m a fan of Smalltalk. I want to build a thing that’s sort of a Smalltalk, except that rather than being the Smalltalk language on the Objective-C runtime (like F-Script or objective-smalltalk), it’ll be the Objective-C(++) language on the Objective-C runtime. So really, a different way of writing Objective-C.
  • I want to know more about how clang and LLVM work, and this is as good a way as any.
  • I think, when it actually gets off the ground, this will be a faster way of writing test-first Objective-C than anything Xcode can do. I like Xcode 5, I think it’s the better Xcode 4 that I always wanted, but there are gains to be had by going in a completely different direction. I just think that whoever strikes out in such a direction should not release their research project as a new version of Xcode :-).

Where can I get me a ClassBrowser?

You can’t, yet. There’s some necessary housekeeping that needs to be done before a first release, replacing some “research” hacks with good old-fashioned tested code and ensuring GNUstep-GUI compatibility. Once that’s done, a rough-and-nearly-ready first open source release will ensue.

Then there’s more work to be done before it’s anything like useful. Particularly while it’s possible to use it to run Objective-C, it’s far from pleasant. I’ve had some great advice from LLVM IRC on how to address that and will try to get it to happen soon.

posted by Graham at 01:51  

Thursday, June 6, 2013

enum class in C++11

I’ve opened the new edition of Cuboid Stroustrup exactly once, and I’ve already learned exactly one useful thing.

Before going into what that thing was, a comment on the book: The C++ Programming Language is, along with Object-Oriented Software Construction by Bertrand Meyer, one of the best books on a programming language I’ve ever read. In addition to explaining the language and its features, they explain why it is as designed. You get to find out how the various features are expected to be used: a great introduction to the idioms adopted by its community.

Conversely, Object-Oriented Programming: An Evolutionary Approach discusses much of the philosophy without going into much depth on the language itself. The C Programming Language goes the other way, explaining the language in detail without describing why any of its features were designed the way they were, or how you mighty want to put them together. It also happens that both of these books are too old to learn modern idiomatic use of their languages from.

Anyway, enum class. The problems with C enum are twofold: firstly the named values go into the global namespace so you can’t duplicate names (imagine if you wanted to have text alignment and view content mode enumerations, for example, you couldn’t use “Left” and “Right” as names in both places). Secondly, the values are just integers, so nonsense expressions like Left + Right are possible. What C++ has added recently is strongly typed, namespaced enum classes:

enum class TextAlignment { Left, Right };

You can’t do silly things like add these together, because TextAlignment is a custom type so it can’t be treated as an integer. You’d have to overload operator+() before addition could work—meaning you’d need a reason to do it. This is something I’ve long wanted in Objective-C, though my expectation is that Objective-C enumerations would be Flyweight Objective-C objects:

An Objective-C specific @enum type, which creates static, immutable instances. So you could have:

@enum GLScreenOrientation {Landscape, Portrait};

which creates a class like this:

@interface GLScreenOrientation: NSObject <NSCopying>
+ (GLScreenOrientation *)Landscape;
+ (GLScreenOrientation *)Portrait;
+ (NSSet *)values;
@end

This is modelled after the Java enum.

posted by Graham at 08:25  

Wednesday, May 12, 2010

LLVM projects you may not be aware of

All Mac and iPhone OS developers must by now be familiar with LLVM, the Low-Level Virtual Machine compiler that Apple has backed in preference to GCC (presumably at least partially because because GCC 4.5 is now a GPLv3 project, in addition to technical problems with improving the older compiler). You’ll also be familiar with Clang, the modular C/ObjC/C++ lexer/parser that can be used as an LLVM front-end, or as a library for providing static analysis, refactoring and other code comprehension facilities. And of course MacRuby uses LLVM’s optimisation libraries.

The LLVM umbrella also covers a number of other projects that Mac/iPhone developers may not yet have heard about, but which nonetheless are pretty cool. This post is just a little tour of some of those. There are other projects that have made use of LLVM code, but which aren’t part of the compiler project – they are not the subject of this post.

LibC++ is a C++ library, targeting 100% compatibility with the C++0x (draft) standard.

KLEE looks very cool. It’s a “symbolic execution tool”, capable of automatically generating unit tests for software with high degrees of coverage (well over 90%). Additionally, given information about an application’s constraints and requirements it can automatically discover bugs, generating failing tests to demonstrate the bug and become part of the test suite. There’s a paper describing KLEE including a walkthrough of discovering a bug in tr, and tutorials in its use.

vmkit is a substrate layer for running bytecode. It takes high-level bytecode (currently JVM bytecode or IL, the bytecode of the .Net runtime) and translates it to IR, the LLVM intermediate representation. In doing so it can make use of LLVM’s optimisations and make better decisions regarding garbage collection.

posted by Graham Lee at 09:49  

Saturday, April 25, 2009

On dynamic vs. static polymorphism

An interesting juxtaposition in the ACCU 2009 schedule put my talk on “adopting MVC in Objective-C and Cocoa” next to Peter Sommerlad’s talk on “Design patterns with modern C++”. So the subject matter in each case was fairly similar, but then the solutions we came up with were entirely different.

One key factor was that Peter’s solutions try to push all of the “smarts” of a design pattern into the compiler, using templates and metaprogramming to separate implementations from interfaces. On the other hand, my solutions use duck typing and dynamic method resolution to push all of the complexity into the runtime. Both solutions work, of course. It’s also fairly obvious that they’re both chosen based on the limitations and capabilities of the language we were each using. Nonetheless, it was interesting that we both had justifications for our chosen (and thus One True) approach.

In the Stroustroup corner, the justification is this: by making the compiler resolve all of the decisions, any problems in the code are resolved before it ever gets run, let alone before it gets into the hands of a user. Whereas the Cox defence argues that my time as a programmer is too expensive to spend sitting around waiting for g++ to generate metaprogramming code, so replace the compilation with comparitively cheap lookups at runtime – which also allows for classes that couldn’t have possibly existed at compiletime, such as those added by the Python or Perl bridge.

This provided concrete evidence of a position that I’ve argued before – namely that Design Patterns are language-dependent. We both implemented Template Method. Peter’s implementation involved a templatized abstract class which took a concrete subclass in the realisation (i.e. as the parameter in the <T>). My implementation is the usual Cocoa delegate pattern – the “abstract” (or more correctly undecorated) class takes any old id as the delegate, then tests whether it implements the delegation sequence points at runtime. Both implement the pattern, and that’s about where the similiarities end.

posted by Graham Lee at 19:44  

Powered by WordPress