When you look up the word “Language” in the dictionary, you’ll find a few variations of similar definitions. My trusty Google Dictionary extension in Chrome, offers up a first definition that is rather obvious, but nevertheless:
The method of human communication, either spoken or written, consisting of the use of words in a structured and conventional way.
The second definition is more interesting because it allows me to make a point about programming languages. It goes like this:
The system of communication used by a particular community or country.
Look at that. Isn’t that beautiful? Let’s cut the country stuff, and shorten it to, “The system of communication used by a particular community”. Tell me that it’s not a spectacular definition of programming languages as well.
We can look at programming languages in two possible ways. They can be a means for us to instruct the computer, and incidentally communicate with fellow programmers, or they can allow us to communicate with other programmers in specific terms that are ultimately executable.
The first interpretation is technically more accurate. Or, more specifically, the worst kind of accurate.
I like to think of programming languages as languages because, outside of trivial programs, above all else they enable programmers to communicate with one another when it comes to resolving a problem or completing a particular task, while incidentally producing code that is also understood by computers via “translators” (e.g., compilers and interpreters).
There are plenty of benefits that can be derived from thinking about programming in terms of the second interpretation.
When you view programming in this light, you start considering the value of comments expressing the “why” and code expressing the “how”. Tests become a way to communicate to a fellow programmer [1] that a change to the codebase has violated a given requirement. Above all, writing idiomatic code becomes important.
Remember the Google Dictionary definition. “The system of communication of a particular community”. It is in fact the community, for better or for worse, that sets the guidelines and de facto style of a particular programming language.
Since we are operating in a system of communication, programming is a bit like driving: predictability is important. That’s why in the Ruby community we have the principle of least surprise [2] and in the Python community one finds the “There should be one – and preferably only one – obvious way to do it” mantra. [3]
When you think of programming languages as a series of conventions adopted by a community, and incidentally understood by the computer, you avoid recklessly monkey patching classes just because you can. You know that doing so leads to unexpected results and miscommunications with other programmers.
You also avoid features which are technically legal in the language, but rare and confusing to fellow experienced developers in that particular language. For an example of this, take a look a the Flip-Flop operator in Ruby (a reminiscence of its Perl heritage). Technically legal Ruby, even useful at times, however you shouldn’t use it because only a handful of fellow Rubyists will understand what you’re doing. [4] Worse yet, it will be hard to Google for an answer unless you are familiar with its name already.
When you’re first picking up a human language, you learn the meaning of common words. Then you use simple sentences that you memorize from books, people around you, songs, movies, and TV shows. Then you try to mimic the idioms you see used by mother tongue speakers. Often you won’t get them right at first, resulting in comical variations that are close, but not quite right, to those of native speakers.
When it comes to programming languages things really aren’t all that different. That’s why it’s critically important to read other people’s code, write code and receive feedback in code reviews, do peer programming, take part in open source contribution, and engage in other forms of social interaction pertaining to code. Doing so is immensely important when it comes to truly mastering a programming language.
Another consequence of this emphasis on communication among programmers through a common, precise, language whose style, conventions, and idioms are defined by the community is that you should choose your communities carefully. What are the community’s priorities? If there is no love for beautiful, clear, elegant, easy to understand code, tread very carefully. [5]
As someone who had to learn English as an adult, pretty much from scratch, I can tell you that I find many parallels between the experience of learning a new language and that of a new programming language.
Even if the two are not a perfect match – and seeing a programming language as a human language is an imperfect metaphor – I feel that thinking of it in these terms has allowed me to quickly acquire key skills in the programming languages that I needed or wanted to learn.
- At times, that other programmer is just your future self. ↑
- Well, at least that doesn’t surprise Ruby’s creator, Matz. ↑
- Run
import this
in your iPython shell. What, you’re not using iPython? Stop reading. Go install it. ↑ - Idiomatic code can be hard to understand by inexperienced programmers. And that’s fine, as long as reasonably experienced developers can parse its meaning. If nine out of ten programmers in your own community are unable to understand what you are doing, you are equivalent to someone who intentionally uses archaic words in conversation. You end up sounding grandiloquent for no good reason. ↑
- At the risk of getting backlash for these remarks, the PHP community comes to mind. Or even the JavaScript community prior to jQuery and Node.js. There were excellent PHP and JavaScript developers back in the day of course, but the community as a whole had a tendency to hack things together, as long as it worked. Somehow. ↑
Get more stuff like this
Subscribe to my mailing list to receive similar updates about programming.
Thank you for subscribing. Please check your email to confirm your subscription.
Something went wrong.
“We can look at programming languages in two possible ways. They can be a means for us to instruct the computer, and incidentally communicate with fellow programmers, or they can allow us to communicate with other programmers in specific terms that are ultimately executable.”
If anyone doubted this, of course an essential purpose of programming languages is for communicating with humans. The computer only needs the binary code that triggers the microprocessor’s electric signals in the right combinations.
What’s a shame is that the common features of these languages have been shaped more by the needs of the machine than the needs of the developers – and not just that: by the needs of 1950’s computers.
A lot of developers understanding of what a programming language is has been shaped by those early efforts, and dragged through 60+ years of PL history. More modern languages have overcome the most glaring inconveniences of early languages, but the core assumption -that a PL’s purpose is ultimately and exclusively as a tool to convey instructions to the machine- remains unchallenged.
But that’s not an essential property of human-machine languages, and programming doesn’t need to be that way. From the fields of End-User Development and semiotics applied to Human-Computer Interaction come some alternatives that propose an understanding of programming which is closer to the second way to look at PLs, as a tool for human comunication.
An interesting possibility is using computers in a way close to the “augmenting the human intelect” vision that existed before programing languages. In this approach, you don’t have the loom-like vision of programs as sequences of instructions that are number-crunched by the machine, but as sets of symbols intended to be shown to humans, who would reason about them and extract meaning.
In this view, data and code are interleaved as a single entity, and not all content in the program needs to be executable – only some bits are crunched and passed to the processor for execution as a side-effect of using the tool, but the whole thing is intended as a sort of interactive novel that the user reads and steers to create some content or the other. (This is the approach of Literate programming, and is also related to Live Programming).
This can be taken to the extreme by creating instruction sets that work as recipies and can produce different executable code, so their meaning changes depending on the context on which they’re executed. Configuration files and XML domain-specific languages may work this way, and it’s a very natural way to reason for the human mind, yet this approach has not been fully explored by general-purpose programming languages.
I absolutely agree. Thank you for your comment. Are there any particular languages that you feel have come closer to the approaches you outline? Of the languages I have had personal experience with, Haskell comes to mind. But perhaps there are worthwhile efforts I have been ignoring.
Haskell has the essential prerequisite of being extremely purely declarative, but its read–eval–print-loop IDE is quite primitive from the “semiotic” point of view I’m talking about, and its mathematical approach is the opposite of what I’m thinking 🙂
I know it’s hard to explain, as no tool exists that works exactly like what I have in mind. The “office suite as a programming tool” that I talk about in my Ars Technica comment[1] is what I think of as a user-friendly development environment.
The properties of such language should be something among the lines of Bret Victor’s “Inventing on principle” Learnable Programming, and “always-on debugging” IDEs like Light Table, where the environment blends with the programming language and the data you use to test and debug the program become a part of the program.
The way to work in an environment like this is not by creating an formal set of instructions (either declarative like Haskell, or imperative like Ruby or Python) that perfectly define all the possible behaviors and is understood as “the program”, which is later “executed” on a set of “input data”. That instruction set would be created in the end through the programming activity, but it’s not the main focus of the developer (at least not while building the program, maybe only for verifying that it’s correct).
In my desired approach, functions are defined by the developer performing commands that change a subset of data in the desired way, and then generalizing from there to add more code to handle other edge cases not covered by the first programmed commands.
This exploratory behavior happens to be how inexperienced developers create their programs, by trial-and-error. I know it’s error prone and anti-engineering, but those are the same qualities that make it user-friendly. I expect that by integrating lots of error checks and semi-automating best practices in the environment, the tool would be useful enough to build programs of mid complexity without being too dangerous to their users/developers. And maybe, if this approach is adopted, we could learn enough about it to be able to build robust tools on top of it as well.
The “why” that explain the reasons to create the tool and the shape it takes would not be conveyed only through comments, but also by the very examples of data used to define each function. Ideally this data should come from a real problem the developer was facing, and would provide real-world context for how the program is expected to be used. Classic programming languages are not good at this.
[1] http://arstechnica.com/gadgets/2014/11/preview-office-for-android-tablets-is-like-office-for-ipad-but-on-android/?comments=1&post=28036889
Earlier I posted a somewhat-related post at Ars Technica about using MS Office as a programming language in the way I describe above. You may find it interesting as well.
http://arstechnica.com/gadgets/2014/11/preview-office-for-android-tablets-is-like-office-for-ipad-but-on-android/?comments=1&post=28036889
I think much of modern PL research (and with a little poetic license, all of the original PL research) is actually dedicated to developing PLs as human languages for expressing pure, true thoughts.
What is “pure and true” in this context? That was the question which originally drove the invention of PLs or logical languages and answers were attempted near the beginning of the 20th century by Russell, Whitehead, Frege, and many others. Church, Curry, and Turing, notable names in early CS, were actually largely driven by *Hilbert*’s mathematical program of formalizing mathematical logic.
So what does this effort look like today? PLs of this pedigree are *barely at all* interested in how binary codes work (unless they are the object of study, of course). Instead, they endeavor to provide valid logical frameworks of massive expressive power. Modern PLs in this vein can formalize mathematical logic through *intuitionistic logic* which is something like the logic most people learned in school but augmented with a notion of the limits of the Halting Problem. This means that it lives nicely between logic and computer science.
So why do we think that PLs are an object of computer commands? Because the popular ones which were both widely implemented and fast had that property. It’s essentially a walk in the park to convert Assembly to machine instructions compared to an intuitionistic logic calculus. We then proceeded not by seeking to make Assembly more like its forebearing languages but instead to layer abstraction after abstraction atop it to make the fundamentally alien task at hand more manageable.
This is ultimately a flawed program judged that way. Furthermore, it’s also less than half as old as the original PL research efforts, but it already had us develop many techniques for efficiently executing complex tasks on a computer. This is an important part of “computer algorithm design” but essentially a side-topic to the larger field of PL research.
You’re right that modern PLs avoid low-level stuff, and that this is more and more a thing of the future. I’m not sure that being “more mathematical” is the way to go, though; there will always be people who like to work with an extremely formal notation, but I think they’re the minority. Working formally is extremely taxing to the mind, and it’s not needed essential during most programming steps, just for making sure of verification and correctness – if you look at things like Wolfram Alfra’s “Language for Knowledge-Based Programming” (people who are not suspicious of being anti-formalisms), the things they sell as a radical advance and advantages of their new product are interactive evaluation, categorization and search of primitives, and mixing natural language with data and code. Yes, it has a lot of marketing exaggeration, but I think it’s a valid trend.
“I think much of modern PL research (and with a little poetic license, all of the original PL research) is actually dedicated to developing PLs as human languages for expressing pure, true thoughts.”
Very true. Not all research is like that, though. 😉 As I explain in my other comments, some schools of End-User Development actually try to develop “human-like” programming languages that express muddy, ambiguous thoughts, and still arrive to something useful. EUD doesn’t talk of “programs”, but “software artifacts” (mixtures of code and data) that allow users to build automated behaviors, to avoid repetitive behavior and create events that will happen in the future without human intervention. Those two actions are the essence of programming from this perspective, the real “benefits” of being a developer and not a mere power user of pre-built applications.
Admittedly this is a non-orthodox approach, far away from the main PL trends (so you and I are not talking exactly about the same thing). It’s a shame that few developers are aware of this school, though Bret Victor has made it noticed in the large for the first time. Several development tools are adopting the most useful parts of it, and I think both trends will become unified in some not-too-distant future.
From a developers perspective, these tools look a lot like a programming environment that is always in debug mode and where code and data can be updated on the fly. “Reactive programming” languages are like this, though they still have too formal syntax for my tastes; something like REBOL, with minimal “Tcl-like” syntax is more adequate for non-technical users.
I believe a combination of REBOL (for the syntax) + Federated wiki (for storage and collaboration) + the “Leo/IPython bridge” (for the interaction model) would satisfy the needs of naive users to build simple programs. That’s what I’m most interested about programming languages, because that’s a niche that (Hypercard was the closest popular thing that has existed for this, but it still requires a fairly sophisticated end user).
If you’re not familiar with these tools I recommend you to learn a bit about them; I think in the future programming will look a lot like this, as I’ve seen the trends, and many of them point in that direction (except for the “simple syntax” part, that professional developers will never adopt as it’s not really efficient).
“that’s a niche that” … -> has been little explored.
The use of the word “language” to describe specific flavors of machine-command syntax used in computer-programming is an absolute misnomer, if not an oxymoron.
So-called “computer languages” were invented to “communicate” with machines, not other human beings; their convergence in the last 80~90 years with certain aspects of human languages is minimal, yet.
The very essentials of what makes a human language go beyond the translation of collections-of-glyphs to a finite set of invariant machine-commands that have “intrinsic meaning” only in so far as they are interpreted in the frame of reference of the creators and implementers of the programming syntax, and the hardware systems that underlie it.
A human language is the vehicle for the translation of speech, and glyphs, into “meaning” (morphemes), and is inherently parallel, involves multiple dimensions of “resonance,” and exists in an ever-changing matrix of social-cultural identity, and the enactment of roles (where “role” refers to specific psycho-physical states of perception as well as qualities of behavior and perception in interpersonal communication).
The pasting of a “happy face” on machine-command syntax by equating it with “language” may have served a certain purpose to render people less afraid of computers, programming, programmers, etc. But, it’s a con.
Tune to paka dala be….. !!@@!! 🙂 🙂
Interesting thoughts, both in the article and comments. But perhaps too formal for my interests.
The important point that I take away is
PLs should be “human-machine” methods of communication. That resonates.
First, humans (at least some) and machines (at least some) must BOTH be capable of easily understanding the language.
For communicating what? Instructions for completing some task. Focus on specific types of task can minimize the scope of the language.
For communicating to whom, specifically? Everyone and every machine is ideal, but I believe focus on some subset of humans and machines facilitate simplicity and understanding.
The needs for focus in the message and target audience results in a plethora of – if you will, please – DSLs.
I long for the days when COBOL was used to satisfy most business requirements. I remember fondly code reviews that included end-users (i.e., domain experts), where everyone in the room understood and contributed.
Those days are gone. Conversations between domain and machine experts require translators, and there are very few people that understand both areas well.
We need to strive for PLs that maximize the number of humans than can understand the code (perhaps within a given specific context). I don’t see any obvious examples in todays most prevalent languages.
As others have said, the goal of programming languages is to communicate with machines, and as such they are syntactically and topically constrained. They are also entirely purpose-driven. More importantly, in general terms, all words in programming languages are derived from existing words in a true language, usually English.
It’s easy to conflate programming languages and true languages, but this is manifestly easier because we’ve chosen to apply the word “language” to machine syntax for lack of a better term. We’re always looking for a good “handle”.
I can’t believe nobody brought up the Donald Knuth quote which reminds us that “programming is a literary act”. In the 35+ years that I’ve been writing software, I’ve seen at least two “literate programming” efforts, and read of another that predated my entry into the craft in ’79.
I’ve worked in dozens of languages over my career, and none have felt as amenable to that style of expressive code-as-English-prose as well-written Ruby. This is explicitly alluded to in tools like Cucumber, but only marginally less so in the application and spec code that you end up writing.
Note that I used the adjective expressive rather than verbose; a word which reminds me of my experience with COBOL back in the day. :shudder: