In the last post, we looked at why conversational paradigms fall short as an interface to computers, despite their universal understanding and appeal. At the end, we mentioned formal languages and systems, but didn't really dive into this concept. That's what we'll be looking at in this chapter! I definitely think the previous post is sorta a pre-requisite for this one, so definitely go check that out before you read this one. This one's a long one—so strap in tight!
Chapter 3: Task-specific Programming Languages
The chapter of this title is...dry. There's no getting around that. But it's also misleading—and maybe intentionally so. Nardi's goal in this chapter is to open up what pre-conceived notions the reader may have around the phrase, "programming languages". There are several arguments around what falls under this term—such as whether the language is Turing complete and whether it contains a specification for the machine it should run on.
However, one unifying factor amongst all programming languages is that they are all formal languages, meaning they all have their own set of defined rules for using the language. Nardi points out that although many people view this characteristic as the primary gatekeeping factor for learning these languages, this is not the case. Rather, she argues people are hesitant to learn these languages because they generally are not concerned about the inner mechanisms of computers. In other words, it's not the language itself that's difficult—its the lack of motivation for learning it in the first place.
People actually learn and use formal languages far more frequently than they would imagine—the alphabet, rules for games, calendars, musical notation, and even communicating familial relations are all formal languages.
The pervasiveness of formal systems shows that people readily invent, learn, and use them. (p.29)
One aspect about the formal languages mentioned above—that can get lost when comparing them to formal computer languages—is that they all focus on accomplishing tasks.
With some of these formal languages, the systems they interface with don't require any notation. Knowledge about whether someone is your niece or 2nd cousin once-removed isn't denoted with any specific written "syntax"—it's transmitted verbally. Many formal languages, however, do benefit greatly from notation, such as the classic staves and noteheads of musical notation.
Nardi argues that we willingly gravitate towards both the languages and their notations in the proper context. These notations, she argues, "give us access to a rich store of encoded knowledge for an immensely varied set of tasks and events." We create and use so many because we can rely on them for accurate and clear communication, even in great complexity. The same music notation used by a pianist can be used by a trumpet player, an arranger, a vocalist, and so on.
With this in mind, Nardi explains that learning a formal language comes far more easily if it matches the learner's field of interest. The concept of learning new systems and tools in a familiar domain is not new—it's rooted in plenty of cognitive science research (which Nardi delineates on page 38). Even with this seemingly obvious framework for learning, it's a commonly overlooked point when building tools. Nardi sums up this sentiment quite well:
A key to understanding end users' interactions with computers is to recognize that end users are not simply underskilled programmers who need assistance learning the complexities of programming. Rather, they are not programmers at all. They are business professionals or scientists or librarians or teachers or other kinds of domain specialists whose jobs involve computational tasks. (p.39)
Putting end-user programming in the framework of tasks allows for two main abstractions for meeting these demands:
Once the realm of what the tasks entail has been established, the basic blocks that make up this task can be defined and understood without needing to know data structures and other computing abstractions. (i.e. these are nouns! If you've reduced this realm to baseball for example, you have a common understanding of "strikes", "home runs", "teams", etc.)
In addition, the operations that take place within this realm can be abstracted as well, without worrying about low-level computation. (i.e. these are the verbs! Following the same baseball analogy, this would be understanding that a pitch can strike out a batter, or a team gets a home run when their players cross home base)
There's plenty of more explanation and nuance in this realm of how real-world entities are encoded in data structures, but for sake of brevity, I'll leave this out. The key point here is that allowing users to interact with these abstractions through their tasks is a powerful bridge for facilitating end user programming.
Nardi uses the spreadsheet formula language as an example of one of these task-specific end user programming languages. The main takeaway with this example is that spreadsheets accomplish so much because they reduce programming to two simple mappings: cells are variables, and functions can create relationships between these cells. Despite not having any "higher-level" primitives beyond numbers, spreadsheets give accountants, statisticians, and a whole slew of math-focused professionals the tools needed to program their own solutions.
Problems that are solved, getting solved, and will maybe get solved?
So we've covered the semantics aspect (formal languages), the syntax aspect (formal notation), and the motivation aspect (putting these into practice). At this point, if you're wondering, "This problem can't be this simple, if it was, it would've have been solved already!" then you would be correct. Unfortunately, it's not that simple. There's several assumptions—specifically three problems that we need to solve—we've made that Nardi addresses: reusable software, relearning interfaces, and task-specificity. The first two are more general problems faced by all types of software, while the third is specifically related to the realm of end-programming. We'll cover all of them below!
Nardi wraps up the chapter with a dive into a conceptual breakdown of what a tasks is composed of. After all, if you're going to focus on task-specific programming languages, you should be able to define tasks. Nardi looks at this through two approaches—activity theory and distributed cognition. I actually don't think diving into either of these are that relevant to the core crux of this series, unless you're literally designing the fundamental mechanics of an end-user programming language/system, so I'll leave their analysis out of this post. I have this paragraph in the first place since I'm mainly writing this series for myself, who is a designer working on tools in this realm ;)
Okay, let's talk about those problems in 2020
The first problem we have is that software is expensive to build—at least it was in 1993. Nardi narrows down core reason as a lack of “reusable software components” (p.50). All software, including task-specific languages, would be extremely costly if they were built from the ground up. However, this is largely not as much of a problem today as it was back then. Today, we have registries like npm and frameworks like React that generally solve this issue on different levels.* And these technologies themselves are built on protocols and standards used by every modern browser, allowing us to reliably build for many types of machines. There's still a long way to go, but this problem is definitely getting solved the fastest out of the three!
The second assumption deals with the issue of users having to learn multiple interfaces for multiple programs. While this is also industry wide and doesn’t pertain just to end-user programming, I believe its an important problem to dissect in general. As a designer, I believe there's two sides to this problem. On one side is users having to relearn interfaces, and on the other is companies and organizations designing different interfaces. Let’s call these “demand” and “supply”, respectively.
The demand side has been slowly improving with folks becoming technologically literate as computers become more accessible. More folks are interfacing with software products (especially now in quarantine more than ever) and learning the general patterns across these tools—the same way I imagine many folks are now getting very acquainted with their kitchen.
The supply side is definitely slower. It's mainly been supported by established UX patterns proliferating throughout the design world. As designers work on solving accessibility, avoiding dark patterns, and establishing reusable metaphors, we start to get more familiar, trustable, and predictable interactions (at the very fair cost of homogeneity).
There's also the political/economic side of this problem: how are we going to build familiar interfaces when the folks working in this space are forced to compete with each other? Adopting shared protocols and definitions for interfaces seems impossible when there's very few reasons for these companies to cooperate with each other. While there are folks attempting to solve this today (centralized frameworks, movements, methodologies, even crypto!), I admittedly am not super well-versed in this space, so I won't delineate on what's going on there.
The third problem here, Nardi explains, is the most serious: how task-specific should a task-specific language be? What level of the abstraction ladder should we be climbing to? If you come from a computer science background, this is sort of like the problem of "completeness" with respect to user's tasks rather than a problem space. For example, we can encompass a whole slew of tasks with just Excel, but we lose out on extensibility when we need to introduce web data. How do you define the scope of tasks that can be accomplished? Are there even paradigms that lets you expand this scope after its been defined, without exponentially increasing the conceptual complexity of your product? Nardi uses HyperCard as an example of over-solving completeness:
The problem may be that HyperCard-like environments are actually a bad compromise: they have the complexity of conventional programming languages but lack their speed and they are not close enough to end users' needs to provide the right kind of task specificity, nor are they general enough to be as powerful as a conventional language. (p53)
While HyperCard may have not been the end-user programming language Nardi wanted, there's far more progress in this realm today than ever before. This problem is being tackled by many companies, most of which are in the "no-code"/"low-code" realm.** There's also several companies that I believe are tangentially solving this problem. For example, although they’re not focused on end-user programming, Figma and Slack are figuring out the specificity of the tasks needed for design and communication (respectively).
While these problems are not exactly put to rest, we can still continue through Nardi's exploration of end-user programming while they slowly get solved in today's world. We've set up enough groundwork to start talking about the nitty gritty: the actual interaction techniques for end user programming itself. This is gonna be the topic of the next post, and hopefully will be less abstract and theory driven than this one 😅 See y'all then!
Footnotes
*These products have their own unique set of problems, but covering those are more about organizational management and governance, which is not something for this series.
**Between my last post and this post, I actually joined one of these companies, Clay, that is trying to figure out how to solve this problem! They're cool and you should check them out!