Introducing Marks

In my previous two posts (here and here), I described how and why programming languages can’t talk about many issues that affect programmers–important issues like product requirements, design constraints, intellectual property, and more. I also inventoried the mechanisms that extend the semantics of languages today, and explored why those mechanisms have limited value. If you haven’t read those posts, please do; what I say next won’t make a lot of sense without that foundation.

In the intent programming language that I’m creating, the solution to this problem is called “marks” (a name which alludes to linguistic markedness). Marks play a role somewhat analogous to adjectives and adverbs in human language; they are crucial enrichers. They resemble decorators or annotations in other languages, though their power is much, much greater.

Without further ado, let me provide a blueprint for this bridge across the semantic gap that I’ve been lamenting–the design guidelines for “marks.” Then I’m going to show you an example of how easy it could be to use marks, and how much power they give you.

image credit: Curious Expeditions (Flickr)

Blueprint

  1. Code and its compiler(s) must have a compile-time API specified by the language.
    It’s not okay if Clang generates one type of AST, GCC a second, and MSVC a third; all compilers that support the language must expose a spec-compatible, programmable API for all language constructs. For example, I need to be able to find out what parameters and local variables are declared in a function, and what their data types and other characteristics are. This is similar to what reflection offers, but reflection doesn’t help at all, because I need this before run-time. (Kudos to D, which provides compile-time reflection very similar to what I’m describing…) As I mentioned in my post about making a codebase const-correct, the lack of this feature is really a serious design flaw. Why should code, of all things programmers deal with, be impossible to code against?

Continue reading

Lacunas Everywhere

I’m told that in Czech, the word “prozvonit” means “to call a mobile phone and let it ring once so that the other person will call back, saving the first caller money.”

Image credit: AstridWestvang (Flickr)

How would you translate this word to someone in New Guinea who has never experienced electricity, let alone a telephone or a bill from Verizon? You wouldn’t. This is an example of a “lacuna“–a translation problem caused by semantic gaps in a target language. Lacunas occur in programming languages. You might know a few; maybe you wish C++ had python-style generators–or that Java had Haskell’s notion of pure functions–or that C supported PHP-style string interpolation. But what if I told you that semantic misalignment between any pair of programming languages is just minor details? What if I claimed that all programming languages I’ve used have numerous, pernicious, and expensive semantic gaps? That we don’t see these gaps for the same reasons that a stone-age hunter-gatherer fails to notice his inability to discuss patterns of cell phone usage? Would you think I’m crazy? Continue reading

When good comments mean bad language

I’ve had an epiphany.

For years, I’ve urged developers to write better comments. I still claim that’s a good idea (a very good one), but as I’ve pondered what a better programming language might look like, I’ve come to an important conclusion:

A lot of “best practice” commenting is just workarounds for inadequate language design.

This might seem like a crazy or arrogant claim. The Wirths and Matsumotos and Hejlsbergs and van Rossums and McCarthys of the world are incredibly smart people; how could I claim to know something that they do not? Each of these language designers has probably forgotten more about computer science than I will ever learn.

And yet, I think Randall Munroe (the cartoonist at xkcd) was right to make fun of our industry’s facile assumption that context-free grammar is all you need to know about formal language:

image credit: xkcd.com

To show you what I mean, I’ve inlined snippets of code from a variety of programming languages below. Don’t worry about digesting them carefully right now, but give them a quick glance and then move on to my analysis, and see if you agree with my claim about an unhealthy pattern. Continue reading

How Sutter’s Wrong About const in C++ 11

Herb Sutter recently gave a talk about how the const keyword and the mutable keyword have subtle but profoundly different semantics in C++ 11. In a nutshell, he says that C++ 11 corrects the wishy-washy definition of const in C++ 98; const used to mean “logically constant,” but now it means thread-safe. And mutable now means thread-safe as well. His summary slide says:

const == mutable == thread safe (bitwise const or internally synchronized)

Editor’s note: Since this post was written, Herb has updated his slide. See Herb’s note in the comment stream below.

Now, I think Herb’s talk is quite informative, and I don’t dispute the core of what he was trying to convey. It’s a good insight, well worth the community’s attention. I learned something important; I recommend that you watch the talk. Using const well is an essential skill. But I think in his enthusiasm about the way the language has evolved to make semantics clearer, Herb does us a disservice by oversimplifying.

When Herb uses the C++ == operator to boil his point down to a pithy summary, he’s implying true equivalence; what’s on one side of the operator is, for all intents and purposes, identical to or indistinguishable from what’s on the other side. And while const and mutable and thread-safe are highly related concepts, they are not equivalent enough to each other for ==.

To understand why, answer the following question: Why would good code use const and/or mutable even if it’s single-threaded?

Ah. I imagine you nodding your head sagely. You see where I’m going, don’t you?

These two keywords don’t just define semantics for cross-thread access; they define the semantics a variable or object supports when accessed by various scopes (e.g., subroutines or code blocks) on the same thread. If you pass a const Widget & to a function, that function can’t call Widget::modifyState() even if it’s the only thread in the universe. If you declare a m_lazy_init member variable to be mutable, you are telling the compiler to let you change it where it would normally be disallowed, including on the same thread.

So: const means unchangeable in whatever scope sees const (including many threads), which is why it also implies thread-safe (if all threads see const); mutable means changing safely in one or many threads, which is why it also implies thread-safe (if all threads see const). In C++ 98, these semantics were a bit loose. You could use them carelessly, cast away parts of their guarantees, and generally operate as a law unto yourself. In C++ 11 the semantics of const and mutable are explicit and exacting; the standard library demands thread-safe copy construction. As a result, their role in thread safety is clarified, and we all write better code. Mutexes and atomics and certain kinds of queues are inherently safe to change from any thread; they deserve and require the mutable keyword.

Instead of Herb’s final equation, I’d propose a Venn diagram:

The const and mutable keywords are not equivalent in C++ 11, but they do share guarantees about thread safety.

The const and mutable keywords are not equivalent in C++ 11, but they do share guarantees about thread safety.

// Comments on Comments

/*

We spend little or no time teaching programmers how to write good comments.

This is surprising, when you consider how often “total lack of comments” or “poor comments” are cited as evidence that certain modules (or the programmer who wrote them) are the worst thing that ever happened to the technoverse.

Comments shouldn’t leave you–or anybody else–mystified. :-) Image credit: xkcd

I happen to think that there are much yuckier tech things than poor or missing comments in code. But I still think our general level of comment proficiency is lower than it should be.

Here is my attempt to raise the bar a little.

Why We Comment

Sooner or later, most interesting programming problems require a sophisticated mental model of a problem. Building these models is hard work, and once we have them, we are paid to share with our team (or our future selves).

The best way to share mental models with other engineers is Continue reading