I didn't read past the first point because it's dead wrong. Paul is talking about abstracting efficiency away into the compiler and presenting the programmer with a more elegant interface. As an example, Paul notes that strings and lists really should just be represented as lists and the compiler should efficiently handle lists of characters as a special case. Not only in the author completely missing Paul's point (efficiently is unimportant at the axiomatic level), the author is incorrectly applying the essay to business operations instead of programming languages. In most businesses, programming language speed is NOT the bottleneck of the operation.
> Paul notes that strings and lists really should just be represented as lists and the compiler should efficiently handle lists of characters as a special case.
The sort of operations commonly done on strings only have a small overlap with list operations, though. I'm all for orthogonality in a language when it's practical, but I'm not sure that one is a good trade-off.
Erlang does that, and aside from space issues (on 64-bit, a linked list of character code ints means 16 bytes per char, due to alignment), it's kind of awkward. While using pattern matching on individual chars is nice, in those cases it probably makes more sense to tokenize the strings into atoms/tuples via REs, and then pattern-match on those instead. Binaries can also be used as pseudo-strings, but to me it feels like two half-solutions. (And its shell's printing a list of ints as chars when they're printable 7-bit ASCII characters is a bit odd.) Having atoms (AKA symbols) helps, though.
Incidentally, Lua's strings are all immutable atoms. That's practical most of the time, but occasionally a major liability. Lua has the obvious option of just using a different string implementation via userdata when it's a problem, though, so it can make such a daring trade-off.
Of course, just saving strings as arrays with a \0 at the end is also pretty brittle.
From what little I've used Haskell, the strings-as-list paradigm was exceptionally useful.
A lot of people also complain about the performance of strings in Haskell, so this is right where the tradeoff is today. Tomorrow it won't likely be an issue.
A really smart compiler should be able to optimize most of the inefficiency away; I'd bank on compilers improving fast enough to make seemingly inefficient things cost much less.
If I'm designing a language today for the future, I'd err on the side of usefulness.
The problem with "sufficiently smart compilers" is that they can make reasoning accurately about performance difficult. James Hague has a good post about that, "On Being Sufficiently Smart" (http://prog21.dadgum.com/40.html).
Lua does almost no optimization at compile time* (in part so it can compile data dumps rapidly) but I find it easy to predict the performance trade-offs in various implementation choices. In a roundabout way, it still ends up easy to tune.
Still, usefulness and performance are only indirectly related. I think that was PG's original point.
* Yet oddly it ends up quite a bit faster than Python, and there's also LuaJIT.
That's a great article. Which raises the question, do you want predictable performance, or better expected performance with occasional inexplicably slow outliers? Is it impossible to have both with high level languages? Can it be made configurable?
> From what little I've used Haskell, the strings-as-list paradigm was exceptionally useful.
It's useful from a convenience of programming point of view. The preferred underlying implementation is now something more sophisticated than a linked list of characters. See Data.ByteString. The programmer does not need to care much, though.
I didn't read past the first point because it's dead wrong.
THAT statement is dead wrong. Or at least, in dead opposition to my approach to learning. If someone says five things, and four are wrong but the last one teaches me something new or gives me a new perspective on something I already know...
Reading the four things cost me maybe two mintes. The last one cost me thirty seconds to read, plus another couple of minutes to savour. The total investment is maybe ten minutes and I learned something.
Of course, if I skip everyone who seems to be wrong right off the bat I'll avoid some situations where all five are wrong. But I have something to help me out with that: The post is on HN and some other nice people are voting it up. So something must be right, why don't I navigate into the cave, around the pit of vipers, past the deadfall, and onto the intellectual treasure?
And in fact, I read past your first statement and liked the rest. See how that works?
It's most certainly not the approach I take to learning. It is, however, the approach I take towards the conical "I disagree with Paul Graham" blog posts. Why? Because everyone's a critic. If the critic in question doesn't demonstrate a general understanding of the subject matter I do not invest my time. In this case, it was a misreading of an essay, but it really could be anything. I apply this approach to critics of all kinds. Do I apply this approach to published essayists or programmers with well known software? Not usually, because getting published or becoming well known is a sort of vetting in itself. But a vetting process is certainly necessary for blog posts on criticism or some random repository on Github. There just isn't enough time in the day to dive into things that don't have a solid foundation. Why would I read a 5000 line Ruby program whose initial functions are written poorly? At best I'll end with one or two nice tricks and a large headache.
Hey, I only disagreed with you so vehemently as a rhetorical device, to mirror your own statement:-)
Your strategy for filtering information obviously works for you, and my guess is that if you missed something good, it'll come back to you later when someone you respects says "Hey, I know Raganwald is usually a waste of time, but did you see X?"
So... Rock on! And yes, I enjoyed reading your comment and the reply to my thoughts. Thanks for taking the time to post.
> In most businesses, programming language speed is NOT the bottleneck of the operation
You know, people love to say this, but in every company I've worked at (sample size 3), past the first year or so of operations, programming language / framework speed actually was a very significant concern for the business.
This may be self-selection; scaling and performance are two of my areas of specialization. However, whenever I'm on the job market, there doesn't seem to be any shortage of companies trying to hire people to help them handle their performance and scaling problems.
It seems like you misunderstand my comment. The idea I'm getting at is that it's usually something else besides the programming that is the bottleneck, not that bottlenecks don't exist. You're going to hit architectural and database issues long before you hit Ruby's GC in most applications.
I understand your comment, and I disagree with it. Most specifically, I disagree with the "long before" part of it. In my experience, code performance has been roughly as important as architectural and database issues. You will have to deal with all of them, and on roughly the same time scale.
"Paul notes that strings and lists really should just be represented as lists and the compiler should efficiently handle lists of characters as a special case."
Instead of "strings represented as lists", I think it is more useful to say "strings and lists implementing a common interface." For example, in Clojure, it is perfectly fine to treat a string as a sequence.
I think most of what you want is entirely possible with current compiler technology. It is unfortunate that most of the effort is currently directed at optimizing hard to reason about low level code.