"When I design software, I really try hard to make it simple."
mail-archive.com
mail-archive.com
For someone it may be "the abstraction is so clean, small and self-contained that I don't have to look at the implementation", for others "the implementation is so clean, small and self-contained that I don't need any abstraction". The author of this post clearly subscribes to the latter philosophy.
I guess many flamewars on mailing lists ultimately boil down to this culture clash.
I think he calls it "worse" as a way of emphasizing that e better course of action may not feel fully responsible, as in the PC-losering example.
Has any OS other than ITS ever fully addressed this?
(I remembered reading that paragraph since before that I had only ever seen the technique mentioned in Gabriel's essay :) ).
For a practical example of worse-is-better software becoming popular, look at the World Wide Web in relationship to Project Xanadu[1]. For a practical example of The Right Thing software being popular, look at Python. The argument is broadly that New Jersey worse-is-better produces software more likely to become successful than the MIT right-thing approach. That doesn't mean that worse-is-better is always more successful, or that the MIT approach can't produce successful software, or that there's not a time and/or place for both approaches.
[0]: http://en.wikipedia.org/wiki/R/K_selection_theory [1]: http://en.wikipedia.org/wiki/Project_Xanadu
If you have a clean abstraction but a complicated implementation, then there are probably lots of moving parts and sub-steps that can go wrong. No user is completely isolated from implementation: take shell completions for example. Shell completions are this nice simple interface -- push "TAB" and the shell will help you out if it can. But because there is so much machinery going on in the background, a slow or unavailable NFS volume (for example) can make your completion suddenly grind to a halt, even if you don't care about that NFS volume right now.
What Richard is talking about in this example is having as little extraneous stuff going on as possible. The fewer moving parts, configuration files, commands you have to run, etc. the less there is to understand, the fewer states the entire system can be in, and the fewer sub-steps there are that can go wrong.
The hallmark of a truly great abstraction is that it gives you the functionality you need without getting in your way, and that higher-level abstractions can be layered on top without the lower-level abstraction getting in the way.
I'll further submit that a design that has clean abstractions and simple and small implementations either doesn't do much to the point that it's not very useful on its own, or if it is useful the complexity has moved somewhere else, perhaps in metadata or configuration or tooling. It's like there's a law of conservation of complexity; there is a certain amount of irreducible complexity in useful programs that cannot be removed, and remains after all accidental complexity has been eliminated.
Seeing http://vpri.org/html/work/ifnct.htm I think we have good reasons to believe that we currently are several orders of magnitude above that amount of irreducible complexity.
So, while I mostly agree with what you just said, I don't think we've hit the bottom yet. Silver bullet-like progress still look possible.
At the Viewpoint Research Institute[1] (co-founded by Alan Kay), they are trying to have their cake, give it to everyone, and eat it too[2]. They already have some successes, with for instance a set of several programming languages that can implement itself, from bare-bones X86 to sky-high abstraction, in less than 1500 lines; a TCP stack that takes 200 more lines (a 50 fold reduction compared to a typical C implementation); and more.
Efficiency wasn't even the priority. But as it turned out, many optimizations were either very generic (across several languages), or plain unnecessary (some things are faster than anticipated, to the point of being good enough).
[1]: http://vpri.org/
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult. It demands the same skill, devotion, insight, and even inspiration as the discovery of the simple physical laws which underlie the complex phenomena of nature."
edit: Though come to think of it it shouldn't be that surprising. If that architecture managed to work at all on 80s hardware, it should scream on anything from 2011, even a VPS.
I see it with databases, too...people often want to interject MySQL or another heavyweight database into situations where a flat text file would be vastly faster and vastly more efficient.
It's a problem of thinking that solving problems in the large is the same as solving small problems.
And fork is still kind of expensive. Say a CGI just prints out a few K; the forking time will be relatively substantial. If you can afford it, who cares, but it is an issue.
If you're willing to use pre-compiled binaries, with few or no large dependencies, then CGI becomes practical once more. From the link, it sounds like the Fossil website is run entirely on such binaries (not surprising, considering the author).
FastCGI and SCGI were popular long before Rails and Django, they predate those by at least 10 years and were popular long before those appeared.
WSGI is not even remotely relevant to the discussion, from your mention of it I can only surmise you don't have any idea what you're talking about.
Moreover, SQLite increases the executable only around 260 KB. That was one of the design goals!
However he is only able to do this because he writes in C. Python and Ruby are not slow, but they have terrible startup time, which causes a huge amount of overhead for CGI. They probably do 1,000,000x the work of a C program before getting to main(). I bet he statically links his binaries (or at least has few dependencies on share objects) because that has a pretty big cost too.
I wonder if he writing the CGIs in Lua would have the same efficiency. In Lua you would pretty much have to fork because it doesn't have threads and the interpreter is not thread-safe (no global interpreter lock). Or maybe v8 would work too.
matthew@rusticanum:~$ time python2.6 -c 'print("hello world")'
hello world
real 0m0.027s
user 0m0.024s
sys 0m0.004s
matthew@rusticanum:~$ time lua -e 'print("hello world")'
hello world
real 0m0.005s
user 0m0.000s
sys 0m0.000s
Nevertheless, what you describe is practically how Tir works: http://tir.mongrel2.org/To be fair, I haven't run any Lua programs with 20 packages... AFAIK Lua didn't have a module system until Lua 5, and it's not as capable as Python or Ruby's.
[1]: https://github.com/Neopallium/lua-llthreads [2]: http://kotisivu.dnainternet.net/askok/bin/lanes/
As mentioned in the PIL book, they are better termed "Lua Processes" even though they are implemented using OS threads.
Lua also starts up far faster than Python, and has a vastly smaller overhead for loading code (which is easily verified).
C indeed makes that stuff vastly cheaper, but Lua also has a much clearer C migration path than Python or Ruby - it was designed for embedding.
Also, Python and (especially!) Ruby are slow, they just spend much of their execution time calling out to string libraries in carefully-optimized C.
The default implementation of the Lua interpreter is not thread safe: http://lua-users.org/wiki/ThreadsTutorial
But you can't access the same lua_State from 2 C threads, and you can't share data structures between 2 separate lua_States -- you would have to serialize all your data structures the message-pass between them. So if Lua had an interpreter lock, it would enable a Lua-threading library which Python and Ruby have. The lock would just belong in the lua_State and not be an actual C global. (not saying it should add this; just pointing out the difference)
You're use of the word "slow" doesn't have any meaning. What I meant is that Python and Ruby are not slow in the sense that you couldn't write "scalable" CGIs style with them in Hipp's style IF they didn't have horrendous startup time. That is, once the interpreter is started, you can do a LOT of work in 50ms of Python or Ruby (which is exactly what sites that serve billions of page view a month are doing). It's just that loading the interpreter can take upwards of 100ms with a lot of libraries. So with those languages, people use persistent servers rather than what Hipp is advocating.
In Lua you could have a persistent C program and initialize a new lua_State for every request. That would be the moral equivalent of CGI, without the fork(), since all the state is wiped between every request. But, getting back to the original point, that wouldn't retain the ease of administration that Hipp wants because it wouldn't work with inetd.
Though it sounds simple, binary search is a minefield of edge cases and I've seen textbooks that do it wrong.
So when I read the GetMimeType function, I didn't think "how nice and simple"...I thought "Hmm, I bet that doesn't work."
Those words echo in my head daily
It's open source and POSIX, so you can use it even if you aren't on a Mac. It's pretty cool, from a design standpoint.
http://0pointer.de/blog/projects/instances.html http://lists.freedesktop.org/archives/systemd-devel/2011-Sep...
I’d appreciate a more informed comparison.
http://twit.cachefly.net/FLOSS-026.mp3
"SQLite, an in-process library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine."