20
$\begingroup$

In Richard Gabriel's famous essay The Rise of Worse is Better, he contrasts caricatured versions of the MIT/Stanford (Lisp) and New Jersey (C/Unix) design philosophies along the axes of simplicity, correctness, consistency, and completeness. He gives the example of the "PC loser-ing problem" (discussed elsewhere by Josh Haberman) to argue that Unix prioritizes simplicity of implementation over simplicity of interface.

One other example I've come up with is the different approaches to numbers. Lisp can represent arbitrarily large numbers (up to the size of memory), while C limits numbers to a fixed number of bits (typically 32-64). I think this illustrates the correctness axis.

What are some examples for consistency and completeness? Here are all of Gabriel's descriptions (which he admits are caricatures):

The MIT/Stanford approach

  • Simplicity -- the design must be simple, both in implementation and interface. It is more important for the interface to be simple than the implementation.
  • Correctness -- the design must be correct in all observable aspects. Incorrectness is simply not allowed.
  • Consistency -- the design must not be inconsistent. A design is allowed to be slightly less simple and less complete to avoid inconsistency. Consistency is as important as correctness.
  • Completeness -- the design must cover as many important situations as is practical. All reasonably expected cases must be covered. Simplicity is not allowed to overly reduce completeness.

The New Jersey Approach

  • Simplicity -- the design must be simple, both in implementation and interface. It is more important for the implementation to be simple than the interface. Simplicity is the most important consideration in a design.
  • Correctness -- the design must be correct in all observable aspects. It is slightly better to be simple than correct.
  • Consistency -- the design must not be overly inconsistent. Consistency can be sacrificed for simplicity in some cases, but it is better to drop those parts of the design that deal with less common circumstances than to introduce either implementational complexity or inconsistency.
  • Completeness -- the design must cover as many important situations as is practical. All reasonably expected cases should be covered. Completeness can be sacrificed in favor of any other quality. In fact, completeness must be sacrificed whenever implementation simplicity is jeopardized. Consistency can be sacrificed to achieve completeness if simplicity is retained; especially worthless is consistency of interface.

Please note I am not asking whether Gabriel is right (which is a question not appropriate for StackExchange) but for examples of what he might have been referring to.

$\endgroup$
4
  • 6
    $\begingroup$ In case you're curious, this isn't a homework problem. I'm the teacher. :-) On second thought, maybe that makes it my homework. $\endgroup$ Apr 15, 2012 at 0:24
  • 4
    $\begingroup$ I struggle to see why this question is not on Unix & Linux (or maybe Software Engineering?). Can you elaborate on in which way you need a CS perspective on the matter? Also, please clarify whether you want positive or negative examples. $\endgroup$
    – Raphael
    Jan 14, 2013 at 22:34
  • $\begingroup$ Isn't that question more suitable on programmers.stackexchange.com ? $\endgroup$ Dec 14, 2015 at 8:53
  • $\begingroup$ I posted this to CS because I consider language design to be one of the fundamental deep areas of computer science, spanning computability, complexity, architecture, usability, etc. I could have posted it to Unix/Linux, although I was looking for a broader view. As for Programmers, people are almost always hostile to me when I post there, even when I think I'm on topic, so I stay away from there. $\endgroup$ Dec 14, 2015 at 16:55

7 Answers 7

16
$\begingroup$

The question's title suggests that some basic user interface inconsistencies may interest you:

Unix commands don't follow any particular syntax for specifying options and flags. For example, most commands use single letters preceded by '-' as flag: cat -n some_file, but exceptions like tar tf some_file.tar and dd in=some_file out=some_other_file count=2 exist in commonly used commands.

Unix and its descendants and relatives have multiple slightly different regular expression syntaxes. Shells use "*" where other programs (grep, egrep, vi) use '.*'. egrep has '+' and '|' as operators, grep does not.

The basic "everything is a file" system call interface could be viewed as incomplete: read/write/seek/close doesn't fit every I/O device. Badly needed exceptions get lumped into "ioctl" calls, but devices like sound cards don't even fit that very well.

$\endgroup$
2
  • $\begingroup$ Nice answer. When I saw the title I was immediately thinking "ioctl" (and fcntl) but now I don't have to type an answer. $\endgroup$
    – Louis
    Apr 20, 2012 at 7:51
  • 1
    $\begingroup$ glob patterns are not regexs $\endgroup$
    – jk.
    Jan 23, 2013 at 15:10
8
$\begingroup$

Consistency

Lisp has a very consistent syntax, all language extensions can be embedded naturally via macros and such. C, on the other hand, has a rather code syntax, which allows one to take some "shortcuts", so in some cases C code actually look simplier.

Completeness

In Lisp, if you do not have a specific language feature you need, you can implement it yourself with macros. C too has preprocessor, but it is rather perplexing.

$\endgroup$
8
$\begingroup$

C's strings cannot contain character 0 and its library functions are unsuitable for dealing with binary data.

Filenames on Unix systems cannot contain character 0 or character 47 (the slash).

In the original implementation of Unix, filenames were limited to 14 characters. Later versions only relaxed this limitation; they did not eliminate it.

Added: The E2BIG system error condition, when one tried to exec with an argument list that had too many arguments, or occupied too much memory, or an environment that was too big.

Unix is notorious for this sort of arbitrary limitation. Until the advent of Perl in 1987, handling large data sets, or data sets with long records, or binary data, was extremely unreliable.

$\endgroup$
6
  • $\begingroup$ Not allowing / is not arbitrary, it is necessary (?) to resolve ambiguities as / is the path separator. I just created a file 000, apparently that specific restriction is gone in the days of a modern GNU/Linux. $\endgroup$
    – Raphael
    Jan 21, 2013 at 11:02
  • $\begingroup$ I did not mean to say that the prohibition of / was arbitrary, only that line length and file size limits were arbitrary.The point, however, is that a different design could have allowed filenames to contain slashes, but the designers of Unix didn't consider it important. $\endgroup$ Jan 21, 2013 at 22:04
  • 1
    $\begingroup$ I am sure that at the time, those limits were introduced due to performance considerations; undeveloped techniques may play into it, too. From today's point of view, they seem questionable, that's for sure. Regarding /, I am curious: assuming a path should be encoded as string, how do you do that without a reserved character for path separation? $\endgroup$
    – Raphael
    Jan 21, 2013 at 23:09
  • $\begingroup$ I don't understand what your point is. The question asks for "examples of inconsistency and incompleteness in Unix/C"; it does not mention performance. $\endgroup$ Jan 22, 2013 at 0:25
  • 1
    $\begingroup$ @Raphael: You get rid of silly separator problems by defining a path abstract data type, and use that in your interfaces instead of exposing a particular implementation (null-terminated ascii strings). $\endgroup$ May 22, 2014 at 12:05
4
$\begingroup$

IIRC my teacher said the inability to use char * variables in switch statements in C is a matter of inconsistency, but for me that was generality (completeness) issue. I think it's better to use "consistency" just in your algorithms or software design not in programming language itself (at least not in languages like C. maybe a buggy language has consistency issue), because programming languages have solid standards that define domain of rules and work by applying input to rules. So if something is not allowed in language, it planned to not being allowed and is not inconsistency in language, IMHO.


  1. I've used generality as completeness. i think they are same thing. maybe I'm wrong.
  2. This is Not an answer. maybe suggestion or my opinion.
$\endgroup$
1
  • $\begingroup$ It would actually be inconsistent if you could switch on a char*, unless you mean by switching on the address of a variable: switch(pMyChar) {case &charA: ... case &charB: ...} $\endgroup$
    – user253751
    May 31, 2021 at 13:14
3
$\begingroup$

The best example I have is the poor user who had a file named .. -r and typed in rm *.

Whether this story is true or not, it's become a Unix hater's classic.

See The Unix-Haters Handbook, which has an introduction by Dennis Ritchie himself, for lots of these examples.

I'll add furthermore that avoiding these types of problems was a major force in the design of Microsoft's Power Shell.

$\endgroup$
1
  • $\begingroup$ I read Richard Gabriel's essay in the back of The Unix-Haters Handbook. :-) $\endgroup$ Jan 16, 2013 at 4:42
3
$\begingroup$
  • Certainly the myriad meanings of the same (short) flags to commands is an inconsistency.
  • Each program that uses regular expressions has it's own syntax for them
  • Configuration files for services are all different syntax (that can be forgiven in part, your mailer daemon has little in common with your webserver or system startup, but still)
  • There are different editors! Users use different shells!! Why are there so many desktop environments?!?

OTOH, the fact that the shell expands globs, and not the progam, eliminates a lot of irritating inconsistencies present in other systems. Ditto the fact that you can use the same command to copy a file from one place to the other in the filessytem, to a diskette or from a Zip disk to tape.

So, yes, Unix is inconsistent. So are the other systems, just differently ;-)

$\endgroup$
3
$\begingroup$

LISP supporting infinite precision numbers versus C supporting only machine integers is not an example of language 'correctness'. It is a simple matter arising from the fact that the languages had very different design goals.

The point of C was to be a language close to the machine that could be used to implement operating systems. Machines (mostly) don't support infinite-precision decimal numbers. Machines (mostly) do have integers of fixed bit-lengths.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.