proj-plbook-plChCriteria

Table of Contents for Programming Languages: a survey

Chapter ?: Design Criteria for programming languages

"When someone says 'I want a programming language in which I need only say what I wish done,' give him a lollipop." -- Alan Perlis (see also [1])

"Design depends largely on constraints." -- Charles Eames

High-level criteria

readability, simplicity, expressiveness, power (expressiveness/simplicity), succinctness, metaprogrammability, communal gravity,

"Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary." -- Scheme specifications starting with R3RS

principal of least astonishment (minimize gotchas)

performance fast throuput fast startup low latency real-time profilable

fast prototyping

longevity

code reuse

assisting the work of teams

homoiconicity

ease of use

brevity/succinctness -- http://www.paulgraham.com/power.html , http://www.paulgraham.com/popular.html

minimize boilerplate (and 'obvious' repetition; make D.R.Y. possible, ie Don't Repeat Yourself) (this is a subset of 'power' but to many, perhaps the most important subset)

be the scripting language of a popular platform -- http://www.paulgraham.com/popular.html

have a free implementation -- http://www.paulgraham.com/popular.html

have a book written about it -- http://www.paulgraham.com/popular.html

clean design (eg http://web.cs.wpi.edu/~jshutt/kernel.html )

exposes internals as much as possible without endangering runtime systems like the garbage collector -- http://www.paulgraham.com/popular.html

good for writing throwaway programs -- http://www.paulgraham.com/popular.html

has large libraries -- http://www.paulgraham.com/popular.html [2] (and many other sources)

popularity -- http://www.paulgraham.com/popular.html

productivity

expressiveness

" "Locally transformable" is the criterium for relative expressiveness, as devised by Felleisen: if you can locally rewrite (think: macro-expand) one language construct into another, i.e., without a whole-program transformation, than it is no more expressive. -- http://lambda-the-ultimate.org/node/4735 "

maintainability

correctness and safety

REPL

compiled

http://en.wikipedia.org/wiki/Separation_of_concerns

todo add everything from http://en.wikipedia.org/wiki/Unix_philosophy

" While the Scheme community has many constituencies, we believe that one primary purpose of a programming language is to program. A programming language is a notation that's supposed to help programmers to construct better programs in better ways:

" -- Scheme Steering Committee Position Statement (2009)

" Wulf’s Guidelines for ISAs “Compilers and Computer Architecture” by William Wulf, IEEE Computer, 14(8), 1981. Architectures (ISAs) should be designed with compiler in mind • Regularity

If something is done in one way in one place, it ought to be done the same way everywhere • Orthogonality
It should be possible to divide the machine definition into a set of separate issues (e.g., data types, addressing, etc.) and define them independently • Composability
It should be possible to compose the regular and orthogonal issues in arbitrary ways, e.g., every addressing mode should be usable with every operation type and every data type

Wulf’s Guidelines, cont’d Writing a compiler is very difficult, so architects should make it simpler if possible • Much of the compiler is essentially a big switch statement, so we want to reduce the number of special cases More specific principles: • One vs. al l: There should be exactly one way to do something or all ways should be possible – Counter-example: providing NA ND instruction but not AND • Provide primitives, not solutions

Don’t design architecture to solve complicated problems—design an architecture with good primitive capabilities that the compiler can use to build solutions – Counter-example: VAX provided in struction to solve polynomial equations

" -- https://www.cs.duke.edu/courses/fall06/cps220/lectures/2-isa.pdf

what else

"there's more than one way to do it"

"Easy things should be easy and hard things should be possible"

Readability

[3] criticizes Golang for being "too paratactic"

"Parataxis is a literary technique ... that favors short, simple sentences, with the use of coordinating rather than subordinating conjunctions" -- https://en.wikipedia.org/wiki/Parataxis . Eg "The sun was shining brightly. We went for a walk." instead of "The sun was shining brightly, so we went for a walk."

Extensibility vs. gravity and readability

http://www.winestockwebdesign.com/Essays/Lisp_Curse.html

"You will be interested to learn that the very first version of Smalltalk (-72) had a completely extensible syntax (in fact the writing of a class also automatically supplied the grammar). This worked very well, except ... that too much freedom here leads to a Tower of Babel as far as other users are concerned. This has also been the experience with the few other really good extensible languages (like Ned Iron's IMP). Extreme extensibility was removed in the next major design of Smalltalk (-76) in favor of a syntax that could read by anyone, regardless of how many classes had been defined ... I.e., getting stronger meanings turned out to be more important in the end than making language structures fit the task." -- Alan Kay

regularity

e.g. of irregularity in C: in one context (inside a function), the 'static' keyword is used to make a variable persist across calls to that function. But in another context (outside of any function), the same 'static' keyword is used as an access modifier to define visibility to other compilation units!

from http://www.catb.org/esr/writings/taoup/html/ : Rule of Modularity: Write simple parts connected by clean interfaces. Rule of Clarity: Clarity is better than cleverness. Rule of Composition: Design programs to be connected with other programs. Rule of Separation: Separate policy from mechanism; separate interfaces from engines. Rule of Simplicity: Design for simplicity; add complexity only where you must. Rule of Parsimony: Write a big program only when it is clear by demonstration that nothing else will do. Rule of Transparency: Design for visibility to make inspection and debugging easier. Rule of Robustness: Robustness is the child of transparency and simplicity. Rule of Representation: Fold knowledge into data, so program logic can be stupid and robust. Rule of Least Surprise: In interface design, always do the least surprising thing. Rule of Silence: When a program has nothing surprising to say, it should say nothing. Rule of Repair: Repair what you can — but when you must fail, fail noisily and as soon as possible. Rule of Economy: Programmer time is expensive; conserve it in preference to machine time. Rule of Generation: Avoid hand-hacking; write programs to write programs when you can. Rule of Optimization: Prototype before polishing. Get it working before you optimize it. Rule of Diversity: Distrust all claims for one true way. Rule of Extensibility: Design for the future, because it will be here sooner than you think. KISS

from http://www.catb.org/esr/writings/taoup/html/ :

Don't Repeat Yourself (DRY)

http://www.catb.org/esr/writings/taoup/html/ch04s02.html#spot_rule following Brian Kernighan calls DRY Single Point Of Truth or SPOT rule, and also says "There is an analog of the SPOT rule for data structures: “No junk, no confusion”. “No junk” says that the data structure (the model) should be minimal, e.g., not made so general that it can represent situations which cannot exist. “No confusion” says that states which must be kept distinct in the real-world problem must be kept distinct in the model. In short, the SPOT rule advocates seeking a data structure whose states have a one-to-one correspondence with the states of the real-world system to be modeled."

textuality

(end http://www.catb.org/esr/writings/taoup/html/ section )

hot having to write boilerplate

Learnability

What makes a language learnable?

Links:

Simple made easy notes

Simple Made Easy [4], Rich Hickey's thoughts on what 'simple' is, and how "easy" is not always "simple". Some notes:

And sort of overlapping with that is they may be about a particular dimension of the problem that you're trying to solve. The critical thing there, though, is that when you're looking for something that's simple, you want to see it have focus in these areas. You don't want to see it combining things.

On the other hand, we can't get too fixated about one. In particular, simple doesn't mean that there's only one of them. Right? It also doesn't mean an interface that only has one operation. So it's important to distinguish cardinality, right, counting things from actual interleaving. What matters for simplicity is that there is no interleaving, not that there's only one thing, and that's very important.

Okay, the other critical thing about simple, as we've just described it, right, is if something is interleaved or not, that's sort of an objective thing. You can probably go and look and see. I don't see any connections. I don't see anywhere where this twist was something else, so simple is actually an objective notion. That's also very important in deciding the difference between simple and easy. "

" Simple:

Easy:

Judge the software tools by the software that results. Programmer convenience and programmer replacability is not as important as software quality, correctness, maintainability, flexibility in the face of change.

Limits:

todo: read, take notes on the rest; start with 'So how do we change our software?' on https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md ; keep https://github.com/newhavenrb/conferences/blob/master/Rich-Hickey-Keynote.md open to for the slides in plaintext

Minimalism vs DRY

Mid-level criteria and specific criteria

zen of python

C++ (and systems programming in general): "you only pay for what you use"

complete control over memory management

(excepting variable shadowing) the semantics of a subroutine should be the same as inlining the code into its caller

differences between the semantics of a subroutine and inlining the code into its caller should never be such that some lengthly (measured in terms of lines of code (LOC)) operation can't be delegated to a subroutine

"All manipulable entities should be first-class objects" (Kernel)

(how to define first-class? Kernel says "To borrow a phrase from the original description of first- and second-class objects by Christopher Strachey, they have to appear in person under their own names.", but there are other properties that things can have that make them even 'more' first-class; eg if one can assign a boolean expression to a variable and pass it around, that's great, but it's even more powerful if you can also dynamically introspect, build, and modify boolean expressions that you are given in variables

" any subexpression can be named and "pulled out", modulo name capture, of course...any kind of fragment of the language should be nameable and reusable " -- http://augustss.blogspot.com/2011/05/more-points-for-lazy-evaluation-in.html

Metaprogrammability: "Programmer-defined facilities should be able to duplicate all the capabilities and properties of built-in facilities" (Kernel)

Protecting you from yourself: "Dangerous computation behaviors (e.g., hygiene violations), while permitted on general principle, should be difficult to program by accident" (Kernel)

todo find others from [6]

Uniform access principle: "All services offered by a module should be available through a uniform notation, which does not betray whether they are implemented through storage or through computation" (Eiffel)

ability for the typical programmer to simulate in their head the precise operation of any visible part of the language (e.g. the type system is visible; the garbage collector is invisible. see https://news.ycombinator.com/item?id=6442550 )

no expressiveness penalty for indirection: you should be able to take a big block of code and separate it into different functions which call each other, or different modules which call each other, without losing expressiveness or flexibility.

no flexibility penalty for factoring: if there are two functions or modules A and B, and if they both have a subroutine C that does the same thing with the same code, then you should be able to factor out C into its own function or module, depended upon by both A and B, without losing any flexibility or modularity beyond the loss of the ability to make a separate version of C for A and for B"

Determinisism except where desired / "Deterministic by Default"

readability :: not too much punctuation ('line noise'): when there are too many punctuation characters in frequently-encountered source code, this gives an impression of unreadability (the term 'line noise' goes back to the days of acoustic modems, in which a terminal sessions were often not error-corrected, and sometimes spurious strange characters would appear due to noise in the underlying communications medium). An example of a language which tends towards 'line noise' is Perl.

readability :: not too little punctuation: judicious use of punctuation can greatly aid visual scanning and brevity. An example is array assignment; in languages that use something like "arr[i1, i2] = x", it's easy to scan through the program and look for the pattern 'arr[..] = ..', and there is not many characters wasted. An example of a language that doesn't make much use of punctuation is Common Lisp; the above would be '(setf (aref arr i1 i2) x)', which is both more verbose and harder to scan for. In Clojure we'd have something like (aset arr i1 i2 x).

"the new words defined by a library should look just like primitives of the language" -- "Growing a Language" by Guy L. Steele Jr

functions that do similar things/are used together, e.g. car/cdr should have identical numbers of characters in their names, so that they make code easy to line up on a page (-- http://www.paulgraham.com/popular.html )

sandwich complexity model: the bottom level architecture should be as simple as possible, and the top-level language should be as easy to understand as possible; where complexity is inevitable, it should be pushed into the "middle layers (paraphrase of [7])

semantic constraints on the programmer

goto considered harmful

continuations as the functional goto

referential transparency

Tradeoffs

The expressiveness/simplicity tradeoff

Often there is a choice whether to permit (or even require) the use of very expressive semantics in some area, or to permit only an (over)simplified variant. The benefit of the former choice could be in conciseness through abstraction, performance, safety, or in some other area. The benefit of the latter choice is simplicity, with all that implies (decreased cognitive load, easier to read, learnability).

Examples of places where this comes up are the type system (e.g. do we have type signatures for first-class functions that describe the types of the arguments?), argument passing semantics (e.g. do we allow the explicit choice of either of call-by-value and call-by-reference?), concurrency semantics (e.g. do we allow a choice of various memory consistency models for atomic operations?), evaluation strategy (e.g. do we allow an explict choice between lazy and strict evaluation? if lazy, do we allow a choice between e.g. foldl and foldr?), memory allocation (e.g. do we allow explict selection of allocation on the stack or the heap? do we allow explicit destructors?)

Pros and cons of static/dynamic typing

The "Bánffy-Bray criteria for selecting between static and dynamic type systems:

    Static typing’s attractiveness is a direct function (and dynamic typing’s an inverse function) of API surface size.
    Dynamic typing’s attractiveness is a direct function (and static typing’s an inverse function) of unit testing workability." -- https://www.tbray.org/ongoing/When/201x/2011/12/27/Type-Systems

ppl's dream language descriptions

" By way of summary, let's try describing the hacker's dream language. The dream language is beautiful, clean, and terse. It has an interactive toplevel that starts up fast. You can write programs to solve common problems with very little code. Nearly all the code in any program you write is code that's specific to your application. Everything else has been done for you.

The syntax of the language is brief to a fault. You never have to type an unnecessary character, or even to use the shift key much.

Using big abstractions you can write the first version of a program very quickly. Later, when you want to optimize, there's a really good profiler that tells you where to focus your attention. You can make inner loops blindingly fast, even writing inline byte code if you need to.

There are lots of good examples to learn from, and the language is intuitive enough that you can learn how to use it from examples in a couple minutes. You don't need to look in the manual much. The manual is thin, and has few warnings and qualifications.

The language has a small core, and powerful, highly orthogonal libraries that are as carefully designed as the core language. The libraries all work well together; everything in the language fits together like the parts in a fine camera. Nothing is deprecated, or retained for compatibility. The source code of all the libraries is readily available. It's easy to talk to the operating system and to applications written in other languages.

The language is built in layers. The higher-level abstractions are built in a very transparent way out of lower-level abstractions, which you can get hold of if you want.

Nothing is hidden from you that doesn't absolutely have to be. The language offers abstractions only as a way of saving you work, rather than as a way of telling you what to do. In fact, the language encourages you to be an equal participant in its design. You can change everything about it, including even its syntax, and anything you write has, as much as possible, the same status as what comes predefined. " -- http://www.paulgraham.com/popular.html

Approaches to programming language design

There are various schools of thought on how programming languages should be designed, and what is important.

Is syntax and usability important?

On the one hand are those

"Choice of lexical syntax is arbitrary, uninteresting, and quite often distracts from actual substance in comparative language discussion. If there is one central theme it is that the design of the core calculus should drive development, not the frontend language." -- Stephen Diehl

on the other:

"One thing abundantly clear is that syntax matters. It matters an awful lot. It's like the styling on a car — if the styling is not appealing, it simply doesn't matter how hot the performance is. The syntax needs to be something your target audience will like. " -- So You Want To Write Your Own Language? By Walter Bright

"Choosing the best notation is the entire problem of programming language design, and this problem is neither mathematical nor scientific. A programming language is a user interface for programmers, and if you can reduce UI design to math, science, or any formal process, call Apple, not me." -- Mencius Moldbug (Curtis Yarvin)

"Even if you’re designing for professional programmers, in the end your programming language is basically a user-interface design." -- Alan Kay

misc

"In every coherent system for building stuff there are primitive parts, the means by which they can be combined, and means by which combinations can be abstracted so that they can be named and treated as if they are primitive." -- https://groups.csail.mit.edu/mac/users/gjs/propagators/revised-html.html#SECTION00040000000000000000

"local reasoning is perhaps the most important thing in software development." -- [8]

Conciseness cannot be the only criterion for a programming language. Because almost any language L can be made more concise by replacing it by the language L', where the source code of L' is just L after run through a compression algorithm; let L' would not be considered as good a language as L (even if compilers accepted L', people would still write in L and then run the compression algorithm, rather than writing directly in L', in which case the 'true source code' would be L, not L').

" We asked both current and potential users what most stands in the way of their using Rust, and got some pretty clear answers:

    1 in 4: learning curve
    1 in 7: lack of libraries
    1 in 9: general “maturity” concerns
    1 in 19: lack of IDEs (1 in 4 non-users)
    1 in 20: compiler performance" -- https://github.com/aturon/rfcs/blob/roadmap-2017/text/0000-roadmap-2017.md

"What does it mean for Rust to be successful? ... people should be using Rust in production and reaping clear benefits from doing so." -- https://github.com/aturon/rfcs/blob/roadmap-2017/text/0000-roadmap-2017.md

Links

---

todo

argues that the benefits of static typing have diminishing returns with more powerful static typing systems; since more powerful static typing also has a cost, this implies that there is a 'sweet spot':

https://blog.merovius.de/2017/09/12/diminishing-returns-of-static-typing.html

---