My Programming Language Wants

Sev­eral times now I was asked what ex­actly makes for a good pro­gram­ming lan­guage in my opin­ion. I never could ar­tic­u­late an an­swer that I was truly sat­is­fied with but then again this seem­ingly in­nocu­ous ques­tion is ac­tu­ally very hard to an­swer pre­cisely and suc­cinctly. The rea­son is that the ques­tion is quite open and thus can be an­swered on dif­fer­ent lev­els. You can pro­vide a vague an­swer de­scrib­ing the char­ac­ter of a good lan­guage and the feel­ings such a lan­guage evokes in your­self or oth­ers when using it. Al­though per­fectly fine and in fact the only sen­si­ble level for a non-tech­ni­cal in­quirer I am not re­ally sat­is­fied with such an an­swer. A more pre­cise ap­proach would be to metic­u­lously enu­mer­ate the fea­tures that make for a good pro­gram­ming lan­guage. Even this would be gloss­ing over the im­por­tant fact that a pro­gram­ming lan­guage is more than the sum of its parts. A pro­gram­ming lan­guage is de­signed and not sim­ply as­sem­bled. I still like the more pre­cise spirit of the lat­ter ap­proach which I am going to take in this ar­ti­cle and I would like to re­place the ad­jec­tive “good” with the ad­jec­tive “con­ve­nient”. In the fol­low­ing I will thus enu­mer­ate those con­cepts or fea­tures that to­gether lead to a gen­eral-pur­pose pro­gram­ming lan­guage which is con­ve­nient from my point of view.123

Be­fore I begin let me cite a para­graph from the first chap­ter of SICP that states the ac­tual pur­pose of pow­er­ful (here con­ve­nient) pro­gram­ming lan­guages and the three gen­eral mech­a­nisms they must pro­vide:

A pow­er­ful pro­gram­ming lan­guage is more than just a means for in­struct­ing a com­puter to per­form tasks. The lan­guage also serves as a frame­work within which we or­ga­nize our ideas about processes. Thus, when we de­scribe a lan­guage, we should pay par­tic­u­lar at­ten­tion to the means that the lan­guage pro­vides for com­bin­ing sim­ple ideas to form more com­plex ideas. Every pow­er­ful lan­guage has three mech­a­nisms for ac­com­plish­ing this:

  • prim­i­tive ex­pres­sions, which rep­re­sent the sim­plest en­ti­ties the lan­guage is con­cerned with,
  • means of com­bi­na­tion, by which com­pound el­e­ments are built from sim­pler ones, and
  • means of ab­strac­tion, by which com­pound el­e­ments can be named and ma­nip­u­lated as units.

I en­tirely con­cur with this view­point. Serv­ing the pro­gram­mer is a pro­gram­ming lan­guage’s pri­mary pur­pose and serv­ing the com­puter the sec­ondary. The three mech­a­nisms are gen­eral in the sense that they can be re­dis­cov­ered on dif­fer­ent lev­els in a con­ve­nient pro­gram­ming lan­guage’s fea­ture set. To give you the sim­plest ex­am­ple, num­bers are prim­i­tive ex­pres­sions that can be com­bined to com­pound ex­pres­sions with op­er­a­tors which in turn can be ab­stracted by giv­ing them a name. I just wanted to cite the above part from SICP in order to put the sub­se­quent list into an ap­pro­pri­ate frame of un­der­stand­ing. That is, the fea­tures are use­ful be­cause they make pro­gram­ming more con­ve­nient for the human pro­gram­mer and the three gen­eral mech­a­nisms are a com­mon pat­tern in most of the fea­tures.

Basic fea­tures:4

Ad­vanced fea­tures:

In ad­di­tion to the above fea­tures a con­ve­nient pro­gram­ming lan­guage’s syn­tax and se­man­tics must be con­sis­tent, suc­cinct and rea­son­able. The syn­tax must be easy on the eyes as well, as much as pos­si­ble should be an ex­pres­sion for bet­ter nest­ing ca­pa­bil­i­ties and there should be de­fault syn­tax sugar to im­prove pro­gram­ming con­ve­nience for often en­coun­tered use cases. The syn­tax should nei­ther waste too much ver­ti­cal nor hor­i­zon­tal space for typ­i­cal pro­grams. The fea­tures sim­ply must be im­ple­mented in such a way that there is a beau­ti­ful bal­ance among them. Do the lan­guage fea­tures play nicely to­gether and is it clear what id­iomatic code is? Both parts of the ques­tion should have the an­swer yes. On the other hand, pro­gram­ming lan­guage de­sign is all about trade-offs so achiev­ing a beau­ti­ful bal­ance or, worse, a bal­ance most peo­ple will like is prob­a­bly im­pos­si­ble. 282930

This ar­ti­cle dealt with syn­tac­ti­cal and se­man­ti­cal con­cepts or fea­tures that I want to have in a pro­gram­ming lan­guage to call it con­ve­nient. The next ar­ti­cle is about a pro­gram­ming lan­guage’s ecosys­tem which is ar­guably more im­por­tant than the lan­guage it­self. 31

  1. In the end, the as­pi­ra­tion to be fully pre­cise is fu­tile and mis­guided any­way. What does “good” in the con­text of pro­gram­ming lan­guages even mean ex­actly? It is just the na­ture of pro­gram­ming lan­guages that de­nies an ob­jec­tive and pre­cise no­tion of good­ness. As hinted in the in­tro­duc­tory para­graph lik­ing a pro­gram­ming lan­guage is not just about its tech­ni­cal fea­tures but about taste and a lot of other soft fac­tors. As the say­ing goes there is no ac­count­ing for taste. This is why I like the ad­jec­tive “con­ve­nient” as it has a sub­jec­tive fla­vor and puts the user first. An­other al­ter­na­tive would be “us­able” but it seems to me that “us­able” would steer the focus a tri­fle too much in the fu­tile ob­jec­tive di­rec­tion. 

  2. An­other prob­lem with an­swer­ing such a ques­tion is that your pref­er­ences are mostly man­i­fested im­plic­itly. It is not easy to name them off­hand. Some of your pre­ferred fea­tures you do not even know your­self until you lose them e.g. by switch­ing to an­other lan­guage and long­ing for them. This ar­ti­cle is thus also an at­tempt by me to make my im­plicit pref­er­ences ex­plicit. 

  3. The pri­mary source for this list is the great paper Fun­da­men­tal Con­cepts in Pro­gram­ming Lan­guage by Christo­pher Stra­chey. Other sources in­clude SICP and Wikipedia. I was also in­spired by Chris Done’s blog post One step for­ward, two steps back

  4. These fea­tures are in­spired by struc­tured pro­gram­ming, the com­monly needed struc­tures for a lan­guage to be tur­ing-com­plete, and the other men­tioned sources. The basic fea­tures are those that al­most any pro­gram­ming lan­guage has. 

  5. Iden­ti­fiers, num­bers, strings, char­ac­ters etc. 

  6. Arith­metic op­er­a­tions, com­par­i­son op­er­a­tions, boolean op­er­a­tions, bit­wise op­er­a­tions etc. 

  7. Or some­thing com­pa­ra­ble in terms of com­putabil­ity like first-or­der func­tions and re­cur­sion. Sim­ply to achieve tur­ing-com­plete­ness. See also BlooP and FlooP 

  8. E.g. loop­ing con­structs (for loop, while loop), short­cuts (break, re­turn) etc. 

  9. First-class func­tions imply higher-or­der func­tions

  10. Most com­monly im­ple­mented by clo­sures

  11. A suc­cinct syn­tax is very im­por­tant as any­thing else would con­tra­dict the pur­pose of anony­mous func­tions. 

  12. Sup­port for free vari­ables and first-class func­tions ac­tu­ally imply re­cur­sion. 

  13. That is, func­tions which have no re­turn type (void) and who are just ex­e­cuted for their side-ef­fects. In my book, a syn­tac­ti­cal dis­tinc­tion be­tween pro­ce­dures and func­tions is not nec­es­sary. 

  14. Very im­por­tant for the us­abil­ity of the lan­guage and its APIs. 

  15. User-de­fined com­pound data types, ab­stract data types etc. 

  16. Local type in­fer­ence is more than enough since one gen­er­ally wants to ex­plic­itly list the types of top-level con­structs any­way. 

  17. Dif­fer­ent pa­ra­me­ters for each case must be al­lowed and there must be a type switch with ex­haus­tive­ness check­ing. 

  18. “Tu­ples” 

  19. “Sta­tic dis­patch” 

  20. In the form of dy­namic dis­patch, ide­ally mul­ti­ple dis­patch. On the other hand, one can make the ar­gu­ment that mul­ti­ple dis­patch is anti-en­cap­su­la­tion. 

  21. Or some­thing like con­cepts in C++. Ba­si­cally, con­straints on generic pa­ra­me­ters. De­riva­tions like in Haskell should be sup­ported. 

  22. E.g. name­spaces, pri­vate and pub­lic el­e­ments. Re­lated to en­cap­su­la­tion

  23. Tem­plates, macros. 

  24. Or just some way of using user-de­fined sym­bolic infix op­er­a­tors - best with mul­ti­ple dis­patch. 

  25. Such a “dot-call” or “thread­ing” syn­tax makes the di­rec­tion of a flow of calls nat­ural, saves re­dun­dant brack­ets, makes flu­ent in­ter­faces easy and pro­vides con­text that can be ex­ploited by IDEs in order to pro­vide au­to­com­plete sug­ges­tions with in­line doc­u­men­ta­tion which is not just “nice to have” but a pretty big deal. Re­lated to ex­ten­sion meth­ods

  26. Mostly im­plied by macros. 

  27. Mostly for null check­ing. The more con­ve­nient al­ter­na­tive to op­tion types. 

  28. Of course, a pro­gram­ming lan­guage may have many more fea­tures (not in an ar­bi­trary man­ner!) but with­out most of the above I prob­a­bly would not call the lan­guage it­self very con­ve­nient — it can nonethe­less be very use­ful. Truth to be told, there (al­most…) does not exist a lan­guage I would call con­ve­nient (or it is con­ve­nient but has other se­vere draw­backs) al­though I do not even find my list so­phis­ti­cated. But this is no prob­lem as for get­ting a spe­cific task done the lan­guage’s ecosys­tem is much more im­por­tant than the lan­guage it­self any­way. 

  29. I ac­tu­ally also like a lan­guage to be as sta­tic as pos­si­ble even though the above fea­tures do not re­quire it (ex­cept maybe over­load­ing but I be­lieve Smalltalk has in fact over­load­ing and maybe para­met­ric poly­mor­phism but the prob­lem it solves would not even be an issue in a dy­namic lan­guage any­way) for a lot of rea­sons that would be too nu­mer­ous to ex­plic­itly list here and so are re­served for an­other ar­ti­cle. 

  30. I ac­tu­ally omit­ted fea­tures in the list that I find cool but which would re­ally com­pli­cate the im­ple­men­ta­tion or whose po­ten­tial ben­e­fits and draw­backs are not en­tirely un­der­stood yet. You may thus see the list as a lower bound if you like. 

  31. An­other in­ter­est­ing ques­tion to think about is which fea­tures or com­bi­na­tion of fea­tures a pro­gram­ming lan­guage should def­i­nitely not have. Con­crete ex­am­ples could be de­rived from ex­ist­ing lan­guages which is in fact quite often done in prac­tice. One ex­em­plary point I made in pre­vi­ous ar­ti­cles is that I find 1. lazi­ness by de­fault and 2. pure­ness by de­fault to­gether with monadic code for I/O to be bad trade­offs. The scope of a gen­eral analy­sis of “bad” fea­tures would be quite big though. 

Published: 07.04.2014
Comment? Mail or tweet me.