But we all know most flame wars are started unintentionally. Someone makes a completely honest comment, someone else feels strongly about the subject and honestly replies in a way that angers more people, and eventually everything is on fire, and people accuse each other of trolling. Genuine trolls are few and far between.
The way reddit comment system works, everybody can moderate any comment by clicking either up-arrow (+1) or down-arrow (-1). If comment gets score -5 or worse, it is hidden from the default view, and can only be shown by clicking "show comment". Comments start at +1 (reddit assumes everyone up-arrows own comments), so if 6 more people down-arrow than up-arrow your comment, it will be hidden in default view.
Most of my comments on reddit are moderated positively, neutrally, or at worse slightly negatively. However, a few of them were down-arrowed intensely enough to get hidden. I'd like to take a look at them now. Maybe they'll reveal something about me, maybe they'll reveal something about reddit comment system, or reddit community. I have no idea yet.
First some statistics. I made 46 comments. The best rated of them got +17, the worst rated got -10. Mean rating is +2.5, median is +2, two comments got ratings bad enough to be hidden (-6 and -10), eight others had bad ratings but weren't hidden (-3, -2, five -1s, and 0). So ten comments weren't liked by the community. Here they are.
- -10 points, "Common Lisp sucks". It is factually correct, but feels emotionally loaded. Still, one would think that factual correction of article called "Common Lisp - Myths and Legends" that actually contains mostly propaganda would be treated better.
- -6 points, "CLOS sucks". Another 100% factually correct comment. This time it had nothing emotionally loaded inside.
- -3 points, "links to relevant content". This one baffles me most. I linked two articles that were the most relevant articles on the subject. Well, the original poster "read that Steve Yegge piece", but half of what Steve Yegge writes is related to the question, so I wasn't really sure if he meant this one. Mention of RLisp was clearly emoticoned, so it shouldn't matter.
- -2 points, "keep your rants short". This one is emotionally worded, but the point is valid - the original rant should have focused on the important issues. Instead, it criticizes everything, whether it makes sense or not. This kind of ranting is harder to read, less useful for readers, and less powerful at getting the point through, whatever the point is.
- -1 points, "formal methods suck". This one is directly related to the commented paper, and factually correct - some things (like formal methods) are popular in the academia, because they make good papers, but they aren't popular anywhere else, because they aren't very practical. Do you think you could publish a paper on unit testing a simple program ? You couldn't, but the formal method guys could. And what are you going to use in real programs anyway ? Yeah, unit tests.
- -1 points, "XML isn't that bad". A simple list of reasons why XML is better than S-expressions for markup, and better than "simple custom text-based formats" for configuration. I don't see anything controversial here.
- -1 points, "Smalltalk is better than Lisp". I point out that Smalltalk has every feature on the list, and without Lisp paretheses. I also point out that the question of relative power of Lisp macros and Ruby-style metaprogramming can be proven once and for in RLisp. It's not in the post, but heavy macro use doesn't seem to require Lisp syntax, see Nemerle macros and Dylan macros [PDF] (nobody uses Dylan, but the concept of "skeleton syntax" in the paper seems very interesting).
- -1 points, "Technical details why Common Lisp sucks". Someone disagreed with my comment, so I replied explaining some details.
- -1 points, "Common Lisp was designed for Lisp machines". Factual correction, with links to the relevant sources.
- 0 points, "Java is the most popular language". Someone claimed that Visual Basic is more popular than Java, and that popularity doesn't matter. I disagreed on both points.
So there doesn't seem to be a single troll among the downmoded comments. And it's clear that criticizing Common Lisp is a sure way of getting downmoded, whether the criticism is factual or not, and whether the comment it is emotionally loaded or not.
This posts seems pretty boring, so here are three extras.
First, a comment on reddit by some smug Lisp weenie. I think the poster was just angry at me, not trolling on purpose. Still, it's absolutely hillarious.
The main problem with Ruby is that Ruby advocates, like taw, are too much into Anime.
The second is a point from one of my downmoded comments:
If you aren't writing significant parts of your application in assembler - it's an absolute proof that performance is not critical for it.
Some people keep repeating that one "language" or another is slow. Assembly coders used to say that about C, now C luddites say that about Java, and smug Lisp weenies together with Java zealots about Ruby. The "performance" argument shows how desperate they are, having run out of more decent arguments. In the real world of course:
- It is extremely atypical for programs to be CPU-bound by operations within the program. Most programs are programmer-bound, or network-bound, or disk I/O-bound, or CPU-bound within some library code (like SSL or movie decoding or whatever).
- If the program is actually CPU-bound by own operations (what is very rare), high-level optimizations like changing the algorithm, caching, etc. usually fix it.
- In extremely rare case when high-level optimizations are not enough, performance-critical parts can usually be rewritten in lower-level language, like C for high-level languages, or assembly for C. The idea that one language must be used for everything (common among the Common Lisp folks) is retarded. The program is 99% high-level language, 1% low-level language, and performs as well or better than one written in 100% low-level language.
- There are a few cases when even rewriting small parts in lower-level languages is not enough. This is extremely rare, you're unlikely to write such program even once in your lifetime. A good example are codecs. But programs that need that much performance are never written in a single language anyway ! They invariably use very heavy assembly. Just look at any audio/video codec, or libssl, or whatever. I've never seen a single program that didn't use any assembly, and it was too performance-critical to use "99% high-level language, 1% low-level language" formula.
And the third point, one that got me downmoded most:
CLOS is much less powerful than Smalltalk/Ruby-style object oriented system. In particular, it is impossible to emulate
method_missing
/doesNotUnderstand
in CLOS.All decent object-oriented languages (like Smalltalk, Ruby, and ... did I mention Smalltalk and Ruby ?) have been designed with object-oriented programming in mind. Bolting it on later invariably results in something uglier, harder to use, and less powerful. A few languages like Python survived the retrofitting relatively well, most of the time however (Perl, OCaml, I'm not going to say a word about C++ here) the result is disastrous.
CLOS tries to bolt something it calls "object-oriented programming" on Common Lisp, mostly by heavy use of macros. The name "object-oriented programming", which originally (in Smalltalk) meant quite a lot, was abused to the point that it doesn't mean much any more. Some common abuses make it mean "class-oriented programming", "encapsulation", or even particular syntax (
obj.meth(args)
).CLOS goes even further than that. Basically, it doesn't even have methods. What it has instead "generic functions". The main reason for it is trying too hard not to introduce any syntax for method calls (also known as sending messages). So while other languages have function calls
f(a, b, c)
and method calls a.f(b,c)
, CLOS has one syntax for both (f a b c)
. The big question is - how does it know whether (f a b c)
is a function call or a method call ? It's simple - all methods used anywhere must be first predefined. So if any object has method to_yaml
, (to_yaml whatever)
will try to call method to_yaml
on whatever
, whether it has such method or not. Actually there's nothing method-like about to_yaml
- it's really just a function. This function can be defined separately for each "class". So in one place you write definition for converting lists to yaml, somewhere else for converting numbers to yaml, and even elsewhere for converting widgets to yaml.So simple object-oriented programming is possible with CLOS. Unfortunately it's impossible to emulate
method_missing
or do anything else advanced.If generic function like
to_yaml
is applied to object of unknown kind, a no-applicable-method
handler is called. Unfortunately it is impossible to setup this handler per-object ! So farewell, dynamic proxies which forward messages they receive to some other object.Even worse,
no-applicable-method
is called only when the "method" was declared as one. So (foo bar)
instead of calling bar
's no-applicable-method
handler, or even a generic no-applicable-method
handler, is going to call undefined-function
handler instead, and undefined-function
handler doesn't even get to look at function's arguments.A very limited implementation of
method-missing
in CLOS is here:
; Default method-missing
(defmethod method-missing (m (self t) &rest args)
(format t "~s not found, no replacement method provided!~%" (cons m (cons self args)))
; Should throw an exception ...
nil
)
; Override global no-applicable-method handler
(defmethod no-applicable-method (m &rest args)
(format t "~s not found, calling method-missing!~%" (cons m args))
(apply #'method-missing (cons m args))
)
; Now it's possible to make limited dynamic proxies
(defclass dynamic-proxy () ((obj :accessor obj :initarg :obj)))
(defun make-dynamic-proxy (obj) (make-instance 'dynamic-proxy :obj obj))
(defmethod set-obj ((self dynamic-proxy) new-obj) (setf (slot-value self 'obj) new-obj))
(defmethod method-missing (m (self dynamic-proxy) &rest args)
(apply m (cons (obj self) args))
)
Of course it breaks with multiple dispatch (that's a great reason not to have multiple dispatch), with methods that weren't predefined, and basically with pretty much everything non-trivial.
Anyway, The Right Thing is to separate calling functions from sending messages. So functions would be called like
(f a b c)
, and methods like (send obj 'meth foo bar)
. As sending methods is extremely common, we could even introduce different kind of parentheses for it, let's say [obj meth foo bar]
. Now just add full metaprogramming support, a decent standard library, clean up legacy stuff, and you end up with RLisp.RLisp sucks in many ways, but at least it has a decent object system. Unlike CLOS.
I tend to agree, both about Reddit and about Lisp in general. Good post.
ReplyDeleteThat's a nice article, and I agree that you weren't trolling.
ReplyDeleteI, unfortunately, can't resist a little trolling now and then.
By the way, that's a really neat cat picture. But couldn't you have waited until Friday to post your blog entry, to keep the Friday catblogging tradition alive?
http://en.wikipedia.org/wiki/List_of_blogging_terms
Ben: On this blog Everyday is Caturday !!!
ReplyDeletetaw, you must have a hell of an inferiority complex.
ReplyDeleteYou clearly dislike Lisp, which is fine (just don't use it, and don't read Lisp articles posted on reddit; nobody will care). However rather than keeping this dislike to yourself, you continuously try to convince people how smart you are, how stupid Lisp programmers are, and how Ruby is the best thing ever.
Not only that, but you are developing your own Lisp dialect in Ruby, "RLisp", a useless toy that nobody will ever use. Well, I think you win the "Social Problems of Ruby" award for this week.
Go ahead and delete my comment, I don't care.
I wonder if I have the record for most down-modded comment, I got -50:
ReplyDeletehttp://reddit.com/info/taga/comments/ctaxh
Speaking of never using RLisp... some people I have talked to have trouble installing it on default Macs, because out of the box, Macs use case-insensitive file systems. This probably affects Windows users too. Would you be able to release a version of RLisp that does not have case conflicts? (rlisp.rb vs. RLisp.rb)? That would be sweet.
ReplyDeleteEvan: I'll fix the case sensitivity issues in the next version. Thanks for the bug report.
ReplyDeleteYou can talk about this and that all day long, but why bother?
ReplyDeleteHave you ever used a functional language? Before you say it, neither, Ruby, nor Lisp are functional languages. Perhaps I need a qualifier - "pure" (functional) - one with "controlled side effects".
I think it would open your mind a bit more and give you some more ammunition to make "factually correct" statements - since this seems to be your objective.
Just a tip.
"all methods used anywhere must be first predefined."
ReplyDeleteactually only when they are called. second, it makes sense that they must be implemented if i want to use them.
"Unfortunately it's impossible to emulate method_missing or do anything else advanced.
Unfortunately it is impossible to setup this handler per-object!"
yes, because in CLOS the message passing concept is generalized (multiple dispatch of methods instead of single dispatch).
methods implement an interface (a generic function). a method doesn't belong to a particular class like in java..., so this kind of method_missing you wish for doesn't make sense in CLOS, as you have seen for yourself. and why do you think "anything else advanced" is not possible in Lisp/CLOS?
maybe CLOS is not the right thing for that what you understand of "basically with pretty much everything non-trivial."
I'm an occasional redditor and I would have downvoted some of those. I downvote comments that appear to be trying to persuade people of things, but have an emotional tone that will prevent people from being persuaded by them.
ReplyDeleteYou're a Less Wrong guy, you understand this, Yvain wrote about it. Or this: "Nyhan worked on one study in which he showed that people who were given a self-affirmation exercise were more likely to consider new information than people who had not. In other words, if you feel good about yourself, you’ll listen — and if you feel insecure or threatened, you won’t."
Especially when there's multiple comments making the same argument, but one is making it emotional and personal, and another is keeping to the facts, I downvote the emotional one and upvote the factual one, in order to make the best arguments most salient.