Knuth and the Unix Way
leancrew.com
leancrew.com
This vision seems to have failed for about 5 decades now, but it is still a popular one. After no one was able to build serious applications by gluing together Unix utilities, it was thought that we would instead build serious applications by gluing together objects. This vision has also failed (or at least, not succeeded in a very apparent way). In the mean time there have been other promised types of "componentry", like COM, CORBA, WS-*, ..., all more or less complete failures (or at least, egregiously unapparent successes).
So, in my view, it is Knuth who has the last laugh.
In a way, a lot of folks are just plumbing together little utilities and libraries to do what they could be programming themselves. jQuery, Rails, node.js, redis, sqllite, etc. are the components for larger plumbed systems.
So, with the overwhelming reliance on frameworks and third-party libraries in modern software development, perhaps McIlroy may be shown right yet.
Now, if you mean that no one is writing full-scale professional desktop or web applications by piping together sed and awk, then of course you are correct. But many projects come closer to this than you might think. Git, for instance, was originally just a large collection of shell scripts, which has over time been gradually rewritten in C. Many pieces of it are, even today, still written in the POSIX Bourne shell.
To me, the principal reason for the astounding longevity of Unix is its emphasis on composability. The tiny, ubiquitous Unix utilities are ancient compared to virtually every other piece of software in common usage, yet are no less useful today than when ken and dmr first conceived of them. By making the system modular and composable at a very fundamental level, they ensured (perhaps as much by accident as by intentional design) that users far into the future would be able to continue using their tools for purposes not yet dreamed of. And indeed, this is very much the case, and is likely to continue that way for some time to come. Whatever the eventual successor to Unix turns out to be, it's likely to have the same emphasis on small building blocks with enormous synergy when composed.
Likewise, when you say that the vision of "building serious applications by gluing together objects" has "not succeeded in a very apparent way", I frankly have no idea what you're talking about. OOP is the dominant paradigm in modern programming, bar none. You might argue that some other paradigm (FP, perhaps?) would have served us better in retrospect, but the last thing anyone can truthfully claim about the object-oriented approach is that it has not been successful. It's hard to think of even one popular programming language that doesn't borrow at least a few ideas from that school of thought.
I do agree with you about the "enterprisey" component approach being largely a failure. Thankfully, CORBA, SOAP, et al. seem to be mostly behind us, or at least rapidly receding.
Doesn't that prove the parent's point that the collection of supposedly reusable components could not be glued together to form GIT? There must be a reason why it was rewritten in C. And if it had to be rewritten then I don't think we can call the "large collection of shell scripts" a complete success.
"OOP is the dominant paradigm in modern programming, bar none."
There could be many reasons for that but it does not address the point that the actual gluing together of objects has somehow succeeded. How much of that OO work is actually shared between projects? How much of it is simply OOP for the sake of it because the language was chosen for convenience?
To me success would be using objects from one project without modification in a completely separate project. I just don't see that happening very often. If the objects are simple enough you might as well just rewrite them. If they are complicated enough to be of value to import, they usually require modification.
Ironically the main reason for the rewriting was the poor performance of the fork system call in Windows (Cygwin) which make the system slow particularly for shell scripting.
The problem was Windows, not Git.
http://libgit2.github.com/
from the blurb: libgit2 is a portable, pure C implementation of the Git core methods provided as a re-entrant linkable library with a solid API, allowing you to write native speed custom Git applications in any language which supports C bindings.and
www.jgit.org
The point here seems to be that (ba)sh scripts are wonderful for one-time tasks. Not so much for anything else/permanent workflows.The motivation for rewriting in C was performance on Windows - since Windows has very inefficient forking, shell scripts are very slow there[1]. I think that says more about the deficiencies of Windows than the deficiencies of the shell scripting/pipeline model.
This is slightly besides the point anyways. Even though most of Git has been rewritten in C, Git is still comprised of lots of little single-purpose commands, which I have used in pipelines to do some "outside the box" processing on Git repositories.
[1] http://en.wikipedia.org/wiki/Git_%28software%29#Portability
That very much depends on where you draw the line. If Ruby on Rails, Wordpress or Jango are "professional web applications", then so is werc [1] "web anti-framework" which is built entirely on rc, the plan9 shell.
xmobar [2], dzen [3] and geektool [4] are all notification tools that can watch files for changes or accept, process and display input through pipes.
Quicksilver [5] uses piping, though this is arguably a different paradigm — select object first, then the action, then details. I wonder why this paradigm isn't more popular and articulated, especially in the light of ubiquity of indexing tools like Spotlight on OS X, wonderbar in Firefox and DDG bang operators [6]. It would be interesting to see the trend continue.
Overall, this piping stuff around approach is very subdued, but virtually omnipresent. Modern cell phones (Samsung Bada, anyone?), TVs and other "smart" appliances provide a glimpse of the non-composable world.
[1] http://werc.cat-v.org/docs/
[2] http://projects.haskell.org/xmobar/
[3] https://sites.google.com/site/gotmor/dzen
> When I read comments like this I'm left with two questions: 1. What are "serious applications"? 2. How does this person define "failed"?
> This task of counting word frequencies looks like an ideal job for awk. But there are many ways to do it, and with different UNIX's utilities. UNIX is remarkably flexible.
> When awk was introduced it was not imagined that people would try to write 10 page programs with it. But of course, they did. Are these the so-called "serious applications" that some people want to write?
> If UNIX utilities and pipes (including the pipe function in C) are a "failure", why are they still with us after so many years? If that's failure, then what is "success"?
> I think it comes down to what you're trying to do. For processing text, such as the task discussed in the article, I find UNIX utilities to be enough.
> I still have no idea what "serious applications" are. Is text processing "serious"?
> Much of UNIX's userland was intended for text processing. And for that it works very well.
I guess "us" means 1% of the population, since Mac/Windows users generally don't use the command line and the apps they do use aren't held together with pipes.
(BTW, how did sedq get banned within 3 hours of creating an account? This doesn't seem right.)
Or FRAN
http://conal.net/papers/icfp97/
I am sure there are pretty cool smalltalk and lips examples too.
Knuth is an algorithms engineer and his solution reflects his background. McIlroy's invention of pipes was seminal, but the state of the art has moved on much further. I think David Turner's contributions via SASL have never been fully acknowledged. I would posit that we now know how to go about achieving the composability techniques required to build large programs easily. The answer seems to lie embedded somewhere deep inside category theory! The ideas are slowly getting popular and seeping over to mainstream languages. At least for me, the way I program today is fundamentally different from 12 years ago.
Word counting is one of those simple, domain-independent problems that lend themselves well to code reuse. It's a rare type of problem. Most tasks presented to a professional software developer could not be solved by a small shell script. A large and unmaintainable one, maybe.
# First, tr anslate multiple s queezed occurrences of the c omplement of A-Za-z (non-word-characters) into a line separator
tr -cs A-Za-z '\n' |
# and then lowercase every word. tr A-Z a-z |
# Sort the words with a disk-based mergesort. sort |
# Count the unique characters :: [String] -> [(Int,String)] uniq -c |
# And do a reverse numerical disk-based merge sort. sort -rn |
# And write the first $1 lines and then quit. sed ${1}q
Personally, I'd change the last two to be sort -rn -k 1,1 | head -n $1
but that's just bikeshedding. And the parts it's made from are so modular, and so focused, that you can wrap your head around all of the problem, without worrying about how sort works, or how tr expands character ranges.If I gave a "professional software developer" in 2011 a problem to count word frequencies, and I got back 10 pages of Pascal that didn't go much faster than code that fits on a Post-It note for my problem, I wouldn't trust that developer with anything else important.
If you wanted to change it, to split "words" by blanks and punctuation characters, you would have
tr -s [:blank:][:punct:] "\n"
and then that piece would fit into the pipeline, the rest unscathed. This design is far easier to reason about than a dozen pages of Pascal.I disagree that code reuse only works in rare instances. Rather, code reuse works better in small focused pieces than in large chunks. Utilities such as "sort" get used a lot.
The rare part is that a whole solution can be made out of UNIX utilities, not that UNIX utilities are used at all. Being familiar with the utilities available, much of the coding I currently do ends up being domain specific processing called from a shell script, often in a pipeline.
Also, sometimes I can answer feature requests from users by showing them how they can actually do it themselves on the UNIX command line. This kind of thing is often forgotten, as it's no longer "development." But code I don't have to write because the user can reuse utilities should certainly count.
My first thought for a fix (still split on spaces, but now we need to remove left-over punctuation):
tr -s '[:space:]' '\n' | tr -d '",.!?' | etc.
I doubt the tr of that time had the character classes, but it gets at the general idea. I'm sure there are better alternatives, and I would be happy to hear them.Oh, and also, we would need to remove single-quotes that might be left-over too, but only if they are at word boundaries, rather than within words. I'm going back to work now...
> In particular the isolation of words, the handling of punctuation, and the treatment of case distinctions are built in.
I suppose then the problem would be counting "can't" as a "can" and a "not".
Bug-free seems a bit of a strong claim either way, good catch ;)
Let's agree that a word is a sequence of one or more contiguous letters; "Bentley" is a word, but "ain't" isn't. The sequence of letters should be maximal, in the sense that it cannot be lengthened without including a nonletter.
I'd say 0x5f375a86/10000 ~ 160000 (known) / 1 (never see) is a highly optimistic ratio.
I'm not sure when head(1) was added, but I doubt it was added by anyone in the Unix core team at Bell Labs, it certainly didn't make it to Plan 9 because it is redundant with sed as the example illustrates.
sed 11q is concise and clear, no need for shortcuts, but if you really have to, you can write your own head shell script that just calls sed.
But, the other guy just used unix commands.
I wonder how many lines of code all those utilities add up to.
Probably more than 10 printed pages.
term% wc /sys/src/cmd/^(tr.c sort.c uniq.c sed.c)
356 998 5993 /sys/src/cmd/tr.c
1752 4526 28371 /sys/src/cmd/sort.c
165 346 2185 /sys/src/cmd/uniq.c
1455 4267 26848 /sys/src/cmd/sed.c
3728 10137 63397 total
term% wc <{man tr} <{man sort} <{man uniq} <{man sed}
54 269 2028 /fd/8
137 741 5542 /fd/7
41 149 1225 /fd/6
208 1078 8464 /fd/5
440 2237 17259 total'Make one-word lines by transliterating the complement (-c) of the alphabet into newlines (note the quoted newline), and squeezing out (-a) multiple newlines.'
Surely should be 'squeezing out (-s)'? Unless I'm missing something?
It's a nit I know, but it confused me when I wanted to run through it and understand what was going on :)
And UTF-8 was invented by Ken Thompson and Rob Pike while working on Plan9.
For example, I copy my database class into each new project :(
How can I approach PHP development in a McIlroy manner?
Part of the problem is not understanding how to incorporate library repositories into my specific project repo.
pic challenge-accepted.ms | tbl | eqn | troff -ms hyphen < input | layout | topostscript
where hyphen inserts all possible hyphens, and layout discards those which are unnecessary.