The best kittens, technology, and video games blog in the world.

Tuesday, December 26, 2006

magic/help update

I'm Cute! by Cracker Bunny from flickr (CC-NC-ND) It's been a long time since the revolution in Ruby documentation that magic/help was. And today there's a big new release. magic/help uses many interesting heuristics to look for documentation. Often it is able to find the right documentation when plain ri wouldn't. Unfortunately there seem to be a major mismatch between the reality and Ruby documentation. One good example are SomeClass.new methods. They are all documented, but none of them exists ! In reality there's only Class.new, that calls SomeClass.initialize. So magic/help "correctly" returned documentation for Class.new when asked about SomeClass.new. That was the worst case, but there are many more. In new release magic/help is much smarter. It is now able to handle many quirks it previously couldn't. It is tested against full documentation database - and it doesn't make even a single mistake now. magic/help now comes in tarball, zip, and gem formats. So just grab it, install the gem, and add the following to your ~/.irbrc:

require 'rubygems'
require 'magic_help'

Blinkenlights, part 1

Silly Furry Saturday by Buntekuh from flickr (CC-NC-SA) In two days, I'm going to the Chaos Computing Congress. CCC is about two things - computer security, and blinkenlights. I didn't want to be the only person on the whole congress who didn't make any blinkenlights in their live. Well, I did make a 16-bit ALU, and I even attached some diodes to its input and output ports, but they didn't blink, so it doesn't really count. Actually back then I wanted to build a CPU out of simple 74LS TTL parts. When I started I literally (by literally I mean literally) couldn't tell a NAND gate from a NOR gate. It was an awesomely fast way of learning about hardware. Then I learned how to run simulations on Verilog, how to solder stuff together, and how simple CPUs work. It took me ages before I could reliably connect an inverter to a diode. It gradually became easier, and finally I had a pretty decent 16-bit ALU. I didn't go any further, because by that time I already learned more about hardware than anyone can without jeopardazing their sanity. Also, the register file I wanted now seemed way too difficult to actually make. It was about as complex as the ALU when looking only at number of 74LS TTLs used, but it would be a cabling nightmare. There was just no way to do it in reasonable amount of time, and it would be very fragile. A worse problem was that I had absolutely no idea how to connect it to a computer. If I wanted to do anything useful with the CPU, it would need some way of reading and writing data, network access and so on. I wasn't anywhere hardcore enough to build memory controller and ethernet card on my own, so at least at first memory access and network would have to go through a real computer. After it works, I'd give it local memory, but a computer link would still be necessary for internet, loading code etc. So the project got shelved. If you're interested, the design, Verilog files and photos are all available. One thing I did in the mean time was getting a ColdHeat soldering iron. It's way better than traditional soldering irons, it's much safer, and it's very cheap, so there's really no excuse for not getting one if you want to play with hardware. Of course the interesting thing is connection to the computer. It seems that parallel port is exactly what I was looking for. Parallel port contains ground pins, 8 data pins, all operating on TTL-compatible 5V. It really couldn't have been any easier. Parallel ports have some anti-fry protection, but if it fails, the whole motherboard would have to be replaced, so I didn't want to test in on my machine. Instead, I took some old box, burned a Knoppix, and used the following code (run with sudo):

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/io.h>
#include <errno.h>
#include <string.h>

int main(int argc, char **argv)
{
    int port = atoi(argv[1]);
    int value = atoi(argv[2]);
    if(ioperm(port, 1, 1)) {
        fprintf(stderr, "Error: cannot access port %d: %s\n", port, strerror(errno));
        return 1;
    }
    outb(value, port);
    return 0;
}
The hardware part is a bit of an overkill. The only thing you need is to cut a parallel cable and take the 8 data wire and any ground wire, ignoring the rest, but I soldered all 25 cables to a board. It was pretty quick, the ColdHeat soldering iron is really cool compared to my old one. I can already tell that the parallel port acts as expected, using a multimeter on pins. The code above indeed changes pins in the expected way. You can see some photos from today. I could already use it for 8-diode blinkenlights, but that would be pretty lame. It should be pretty easy to create some circuit with more diodes, that accepts 8-bit commands. A trivial one would be a bunch of flip flops, 6 bits for address, 1 for value, and 1 for strobe. That would be enough for 32 bits, still not enough. So I really need some sort of address register. The computer would either first send the address, and then data, or the address register would automatically increase by one every time, whatever is more convenient. I'll think about it later, so far it's been a major success.

Sunday, December 24, 2006

iPod-last.fm bridge update

Yoda, Darth and a shuffle by yinyang from flickr (CC-NC-ND)Good news for all iPod-owning last.fmers (doesn't it sound cool to be an "iPod-owning last.fmer" ...). New version of iPod-last.fm bridge just got released and can be downloaded here.

Changes in the new release:

  • Config moved to a separate file
  • Timezone handling fixed - it should now work automatically as long as timezone on the computer and on the iPod are set correctly
  • Much more readable data format
  • Official client id from last.fm
  • A script to extract song ratings from iPod
  • Some compatibility issues fixed
The things that's still missing is a decent README file. I hope comments in config.rb are enough, but if you have any questions, problems, or praise ;-), don't hesitate to contact me.

Wednesday, December 06, 2006

My "trolling" on Reddit

Chataigne making a face at me by isazappy from flickr (CC-NC-ND)I don't consciously "troll" on reddit (in this post by "reddit" I mean programming reddit, I rarely use main or joel reddits), or for that matter anywhere else. The only place where I can remember starting flame baits on purpose was alt hierarchy on Usenet, and trolling is kinda the point there.

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.