irssi softignore script

A few weeks ago, my friend Steven wrote about a softignore script he wrote for X-Chat. The idea is that instead of completely blocking what a user says, the script captures their text and colors it in a way that the text “blends” with the background, so you can more easily ignore what these people say.

I’m an irssi fan myself, but I was a bit jealous of his script and quite surprised not to find anything like it in irssi’s script repository. So I decided to go ahead and write my own. Irssi is scripted in Perl, of which I have a little bit of knowledge, but definitely no expertise. The documentation on how to script Irssi is a bit thin; you can find a little on their web site, a little on the site of a guy who wrote a mini-tutorial and a little on the #irssi channel.

After a couple hours of hacking, I had a script that did what I wanted. There are three commands, /softignore_add, /softignore_remove and /softignore_list. The parameter to the add and remove commands is a regular expression to match the nickname. The list is kept in memory as well as in a plain text file with one regex string per line. I imagine that the more advanced Perl programmers will suggest better ways to accomplish this, but for the time being, this works well enough.

I have not made the color configurable yet, so if you need a color other than dark grey, just edit the script file and change the value of the $COLOR variable.

I implemented a few functions (slurp, spit and trim) that are most likely available in CPAN modules; however I did not want my simple script to have external dependencies, so I knowingly violated the DRY principle.

Here is the code for the script. Just copy it in ~/.irssi/scripts/ and use the /script load softignore command to load it.

use strict;
use warnings;

use Irssi qw(command_bind signal_add signal_continue command);

# Irssi globals
our $VERSION = "0.00";
our %IRSSI = (
    authors     => "Vincent Foley",
    contact     => "vfoleybourgon at yahoo dot ca",
    name        => "Soft Ignore",
    description => "Ignore users by putting their messages in a dim color.",
    license     => "MIT",
);

# Soft ignore globals
our $FILENAME = "$ENV{HOME}/.irssi/saved_softignore";
our $COLOR    = 14;
our @ignores  = slurp($FILENAME);

sub slurp {
    my ($filename) = @_;

    my $lines;
    {
        local $/;
        open(my $fd, '<', $filename) or return ();
        $lines = <$fd>;
    }
    return split(/\n/, $lines);
}

sub spit {
    my ($filename, @lines) = @_;

    open(my $fd, '>', $filename) or return;
    for my $line (@lines) {
        print {$fd} $line."\n";
    }
    close($fd);
}

sub trim {
    my ($str) = @_;

    $str =~ s/^\s*(\S+)\s*$/$1/;
    return $str;
}


sub softignore_add {
    my ($data, $server, $witem) = @_;

    push(@ignores, trim($data));
    spit($FILENAME, @ignores);
}

sub softignore_remove {
    my ($data, $server, $witem) = @_;

    @ignores = grep { $_ ne trim($data) } @ignores;
    spit($FILENAME, @ignores);
}

sub softignore_list {
    my ($data, $server, $witem) = @_;

    return unless $witem;

    if (@ignores == 0) {
        $witem->print("No soft ignore entries.");
    }
    else {
        $witem->print("Soft ignore list:");
        for my $ignore (@ignores) {
            $witem->print("* " . $ignore);
        }
    }
}

sub softignore_message {
    my ($server, $msg, $nick, $address, $target) = @_;

    for my $ignore (@ignores) {
        if ($nick =~ /$ignore/i) {
            signal_continue($server, "03$COLOR$msg",
                            $nick, $address, $target);
        }
    }
}

command_bind softignore_list   => \&softignore_list;
command_bind softignore_add    => \&softignore_add;
command_bind softignore_remove => \&softignore_remove;

signal_add "message public" => \&softignore_message;

Naruto Fetcher Jutsu!

A few months ago, a friend told me about “Naruto”, a japanese manga from which two TV series were made. I now hate this friend, because I was completely hooked by the story and wasted many hours that could’ve been spent more productively :) I watched nearly all of the dubbed episodes of Naruto (I got tired of the later filler episodes) as well as the Shippuden episodes.

The current story arc in Shippuden is also a filler arc that seems to be going nowhere, so I decided to get the mangas to read the canon story. There are, as of this writing, 445 mangas. That’s a lot of mangas to download by hand! (I could read them online, but I found the picture quality to be severly lacking.) Being a lazy programmer and all, instead of clicking individually on every link, I decided to write a script to fetch them all. I could’ve simply used a for loop, but I wanted to download many archives in parallel to make the process go faster, so I wrote the script in Python and I used the new multiprocessing module to make the parallelism easy (trivial, even!)

This is an extremely simple script, nothing fancy, but I give it to you anyway:

from multiprocessing import Pool
import os

def get(n):
    os.system('wget -q "http://www.narutochuushin.com/downloads/script/downloads.php?title=manga_chapter%03d"' % n)

pool = Pool(10)
pool.map(get, range(1, 446))
pool.close()

Hope you enjoy!

Explaining the null debate

A few weeks ago there was a discussion on an IRC channel I hang in about null references. That was at the time of QConLondon when Sir Tony Hoare was giving a keynote presentation calling null references his billion dollar mistake. We talked about null for about an hour before we all gave up after realizing that it was never going to get anywhere.

I like IRC, but for long discussions, especially those involving many parties, it’s not the best medium out there. This blog post is an attempt to concisely explain the problem with null.

First, what’s null? Null is a way in many languages to convey the idea of “nothingness”. The concept of nothingness is an important one in computer science, and hardly anyone is suggesting that we do without it. This is not what people are arguing about.

The debate is over whether nothingness should be explicit or implicit.

In a language like Java, nothingness is implicit. A method that expects a String parameter can also be passed null. A method that expects an array of doubles can also be passed null. We can think of null as a member of every reference type in Java. However, its semantics are different from every other value of that type; calling a method or looking up an attribute on null results in a NullPointerException. This means that special care has to be taken when dealing with null. And because the compiler can’t know whether we are calling .length() on null or a valid string, it cannot prevent us from doing bad things. The responsibility of safe code rests entirely on the programmer’s shoulders.

On the other hand, in a language like Haskell, nothingness is explicit. Haskell has a type called Maybe a (this could be written Maybe<t> in Java) which has exactly two values: Nothing, which is the value you use to describe the absence of a value and Just x, which we can think of as a box containing the actual value. (The value needs to be “pulled out” of the box to be used normally.) The type Maybe String (a concrete instance of Maybe a) cannot be used where a String is expected, and vice-versa; you cannot call the function length on Nothing, the type checker will not allow it. This eliminates the NullPointerException problem. Furthermore, because Haskell knows that Maybe a has two values, it can warn you when it detects that you forgot to handle either case in a function. Haskell can actually help us avoid making errors.

Response to the prefix syntax “debate”

In a recent blog post, Brian Carper came out in defense of Lisp’s unusual syntax citing regularity and ease of manipulation (by humans and computer programs). The response in the comments and on Reddit were mixed with many people — myself included — echoing Brian’s sentiment and as many people voicing their distaste for Lisp’s syntax.

A common criticism put forth by opponent of prefix syntax is that it makes maths “unnatural”. They write formulaes in infix and prefix styles and expect the reader to see how much clearer and natural and intuitive infix is. Here’s an example taken from a comment:

1 + 2*f(x,y) + 3*g(x)

vs.

(+ 1 (* 2 (f x y)) (* 3 (g x)))

Of course, most Lisp programmers would probably prefer to break down the prefix version into multiple lines to show the structure of the expression more clearly:

(+ 1
   (* 2 (f x y))
   (* 3 (g x)))

The “problem” with this solution was that it was now on three lines and apparently, it was “66% less productive” than the equivalent infix representation. I have my doubts on this claim. :)

However, if you thrown in comparison, boolean and bit operators, now your can have some fun. Quick, parenthesize the following expression without looking up your C reference: 1 & 2 * 3 || 4 + 5 ^ 6 < 7 - 8 == 9.

Regardless of how one feels about whether infix operators are more natural, it doesn’t really matter, because most of us don’t write programs with a lot of long mathematical expressions. In fact, I’d bet that the vast majority of arithmetic operations are adding 1 or subtracting 1 from a value. Hardly worthy of a debate.

Functions are used a lot more than operators, and in all mainstream languages they have the prefix form and nobody seems too stumped by them. Why is that? And if you aren’t stumped by prefix syntax, wouldn’t you like it if it was the same for every operation so that your code was effectively a tree that you could manipulate at compile-time with macros to add your own syntax and extensions to the language? Surely you would!

Bitten by dynamic typing :-/

Quick post about a bug that I fixed this morning that was quite embarrassing. I had a Python function in which I did something like the following:

if getattr(obj, method):
    ...

Experienced Pythonistas will spot the problem immediately: if obj doesn’t have an attribute method, getattr will throw an exception. What I should have done (and this is how I fixed the bug) was to add a third parameter to specify a default value when the attribute doesn’t exist.

I usually like dynamic typing, but this time I hated it: if this had been Haskell, the type checker would’ve called me a retard and refused to compile my code.

Oh well, what doesn’t kill you makes you stronger I guess. I probably should have had a test for this particular case too.