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

Showing posts with label perl. Show all posts
Showing posts with label perl. Show all posts

Saturday, April 06, 2013

Various old projects migrated to githtub

Fluffy Buff Tom and Bike Frame by Chriss Pagani from flickr (CC-NC-ND)

Once upon a time I did "software triage"  to decide which of my software are viable, and which are dead.

Today I took another look at it, and decided to move most of my old projects - even ones that are pretty much dead - from variety of places like Sourceforge, GNU Savannah, Google Code, and tarball dumps on ftp server to github.

I don't really expect any of them to see much use, but there's always an off chance, and if I didn't move them to github, I might as well simply delete them from the Internet completely.

Here's the list of migrated projects:
  • RPU - my MSc thesis. If you want to see how to write compilers in OCaml, it might be somewhat useful.
  • iPod-last.fm bridge - I'm a Sansa Clip user now, and there's no way in hell I'm going back to iPods, but if you need this script updated (I have no idea if it still works or not), ask me, and I could probably figure out how to update it
  • tawbot - Wikipedia admin bot. I know once upon a time it had quite a few users, but I haven't heard from them in a while. If you need help with it, ask away.
  • XSS Shield for Rails 1.2.x - very similar system is included in recent Rails, so I doubt anybody needs this today.
  • freetable - HTML table generator. I know it had users once upon a time, no idea if they're still active.
  • jsme - Driver to use joystick as mouse on Linux. I made it ages ago because I accidentally my whole mouse port. I really doubt anybody would need that today, or that it would even work.
  • gtkidp - Interface for Internet Dictionary Project files. I like command line dictionaries, and I contributed to dictd stuff because Wikipedia made this kind of stuff cool, but these days it's probably not going to see much use. I don't even know if it works with recent varieties of Gtk.

Still TODO


I'm still not sure what to do with the ftp server I used to put my stuff on. Using less eye-violating styling would be a good start. I'm not entirely sure why the hell I picked that color scheme in the first place, and if it was meant as some kind of a joke or not.

And there's still my local ~/everything git repository. I put a few of its utilities on github, but there's orders of magnitude more code there - some even doesn't violate any website's ToS. There are 218 top level directories there, I'm sure at least 10% of them could be made public without any major problems.

And a lot of the software migrated to github still needs some serious work, like turning them into proper gems, compatibility with modern versions of everything and so on. If you have any special requests, just contact me.

Friday, June 29, 2012

Amethyst migrated to github

Feline Royalty by Photography By Shaeree from flickr (CC-NC-ND)

Amethyst was my totally awesome idea for coding Perl with Ruby syntax.

I just moved it to github, fixed a bunch of bugs, added some examples and better command line interface.

It doesn't do anything much more complicated than this:

hello = ["Hello", "world!"]

puts(hello.join(", "))

ary = [2,10,30]
ary2 = ary.map{|x| x*3}

ary.each_with_index{|element, i|
  print("* ", i, " => ", element, "\n")
}

but it's meant to be fun, not useful.

There's some interesting magic inside and if you want to learn to write parsers with Parser::RecDescent it's not the worst place to start.

If someone wants to turn it into a real language (CoffeScript kind of real), go ahead, code any patches you want, and send me pull requests on github.

As I promised, I intend to migrate the rest of my old software to github and clean it up a bit in process.

Monday, April 05, 2010

How to kill all your children

Félinitude-I by Etolane from flickr (CC-NC-ND)

I'm sure you all had this problem a few times - you tell your children to do something, and they just won't obey. At first you wait, but your patience grows thin until you come to face the inevitable - you have no choice but to kill all you children. But first you need to find them.

Unfortunately there's no portable API for traversing process tree to find all your descendant processes. ps, pstree and similar programs use operating system-specific hacks, like reading procfs (Linux) or reading tea leaves patterns (OSX). Even command line options for ps seem to follow three different mutually incompatible systems. And careful tracking of return values of fork won't do you any good, as forked processes might have kept spawning.

So we'll need more than one line of Ruby. Maybe even as many as five!

First, let's get raw data out of the operating system. If it was Linux-only exercise, procps would work fine, but some experimentation shows that both Linux and OSX versions of ps support -eo pid,ppid options for getting process parentage graph.


$ ps -eo pid,ppid
  PID  PPID
    1     0
   10     1
   11     1
   12     1
   14     1
    ... 
 3367  3366
 3382  3367
 3441  1240
 3442  3441
 3460  3442


Before doing anything else, let's just turn it into a hash.


Hash[*`ps -eo pid,ppid`.scan(/\d+/).map{|x|x.to_i}]

Now we know what's parent of every process and the only problem is reverting this graph - something that's strangely missing from the usual Hash/Array toolkit that Ruby and Perl are based upon.

A brief pause time. You might be wondering what happens when process's parent dies. Normally it is the reparented to its grandparent, or init process, whichever is more sensible based on process groups and other things we don't need to concern ourselves with. So process graph is always proper, unless something weird happened during execution of ps (atomicity in my Unix? it's less likely than you think).

Now it's time to do a lazy half-unification. Create a list for every process containing just its id, and then go through all processes and add reference to its list to its parents' list. When we're done every process's list will contain at some depth ids of all its descendants. Now it's just flatten and remove yourself from the list to avoid a suicide.


def Process.descendant_processes(base=Process.pid)
  descendants = Hash.new{|ht,k| ht[k]=[k]}
  Hash[*`ps -eo pid,ppid`.scan(/\d+/).map{|x|x.to_i}].each{|pid,ppid|
    descendants[ppid] << descendants[pid]
  }
  descendants[base].flatten - [base]
end

Once you've found all your children and further descendants, it's just a matter of a quick kill -9 to finish them all off.

Death Sentence - Help Roni - California by fofurasfelinas from flickr (CC-NC-ND)


As a bonus, here's the version of the same function in Perl.

sub flatten {
  map{ (ref($_) eq "ARRAY") ? map{flatten($_)}@$_ : $_ } @_;
}
sub descendant_processes {
  my ($base) = (@_, $$);
  my %parentage = (`ps -eo pid,ppid` =~ /\d+/g);
  my %reverse = map { ($_, [$_]) } %parentage;
  while (($pid,$ppid) = each %parentage){
    push @{$reverse{$ppid}}, $reverse{$pid};
  }
  shift @{$reverse{$base}};
  flatten($reverse{$base});
}


Notice the advantage Ruby has over Perl - and how workaround while annoying are not that difficult:
  • There are no default arguments, but we can easily hack them with an equivalent of [*arguments, default] for one-argument functions.
  • I wanted to say in Perl we don't need to_i - but then it doesn't really change anything in Ruby either, operating on pids like 1234 instead of "1234" just feels saner.
  • We can use Ruby autovivification which takes blocks, but Perl autovivification is useless as we want hash values to default to [key] not []
  • There's no flatten so we need to implement it (and it's such an extremely common function - the reason it's not in Perl is because Perl started without any support for nested arrays whatsoever)
  • There's no way to subtract lists from each other like Ruby's [1,2,3] - [2]. Fortunately we know own pid is always first, so we can just shift the list.
As always, C++ version left as an exercise for the readers.

Friday, February 05, 2010

What is all this Perl doing in my Ruby?

Spacecat. by kmndr from flickr (CC-BY)

First, some quick background. C is a very simple programming language and doesn't have exceptions - problems are indicated with return codes, which you're supposed to check but always forget about it, resulting in all sorts of problems. C++ tried to retrofit exceptions on top of that, and it was a spectacular failure due to bad interactions between exceptions and manual memory management, but let's skip that.

Shell doesn't have exceptions either, but almost all problems result in some error message being printed to stderr, so at least you know that something went wrong.


Perl is trying to be higher level but is modeled after C and shell, so while it sort of support exceptions for some high level packages, almost all of its basic OS-interacting functions like open will fail quietly and you need to manually check their return codes - at least it's easier than C, and ... or die "Cheeseburger acquisition failed: $!"; usually suffices.

Ruby mostly copies Perl when it comes to OS interaction, but fixes this particular problem - OS interaction always raises an exception when something goes wrong. Or does it?

system()


There is one really infuriating exception, where not only Perl error handling is worse than both C and shell, Ruby copies this design failure straight from it, and it's not even fixed in Ruby 1.9 yet!

C function system is fairly straightforward - it executes whatever string you pass to it in shell. So if there's an error and let's say the command fail doesn't exist - int main(){ system("fail"); return 0; } results in "sh: fail: command not found" printed on stderr, or somesuch depending on your variant of Unix. Just like shell would do it, and what would be sane.

Both Perl and Ruby copy this function - except they do it wrong! system funciton is not terribly efficient - it first spawns shell process, which only then executes the relevant command. So some smarty pants decided to optimize it a bit - if the string passed to Perl/Ruby system looks straightforward enough, Perl/Ruby will execute it directly (split on spaces, fork, pass to exec) without spawing the shell process.

And in this micro-shell implementation inside system they both just forgot to check for error conditions altogether. system "fail >/dev/null" (in either of these languages) looks "non-trivial", so it spawns shell process, and results in sh: fail: command not found. But system "fail" - as it's so simple - goes straight to the optimized micro-shell, and fail silently. No exception, no stderr warning, no error code, nothing.

Well yes, you could check process return code - but process return code is non-zero not only on errors, but as a generic way for Unix processes to communicate - for example diff will return non-zero if files differ, which is in no way an error condition.

The fix

The optimization should be either fixed or turned off. As a trivial workaround - because the triviality check verifies that string contains none of *?{}[]<>()~&| \ $;'`" or newline, prepending "" in front of the first non-empty character passed system() seems to work well enough. "" evaluates to an empty string in shell.


$ ruby -e 'system "\"\"fail"'
sh: fail: command not found
$ ruby1.9 -e 'system "\"\"fail"'
sh: fail: command not found
$ perl -e 'system "\"\"fail"'
sh: fail: command not found

 But seriously, please fix this, okay? Even Python gets it right already.

Monday, October 20, 2008

Put your variables on diet

Svanspervot on a Chair by Steffe from flickr (CC-NC-SA)

There are all kinds of categorization schemes for programming languages, by paradigms or checklists of supported features. Categorizations criteria tend to be highly subjective (e.g. "builtin support for regular expressions"), useless (e.g. "significant indentation"), or both (e.g. "does it have a standard").

I want to propose a new categorization - objective, easy to evaluate, and at the same time exposing something very deep about programming languages.

I will divide languages into:

  • thin variable languages - where variables refer to data.
  • fat variable languages - where variables contain data. Variables can also contain references to data, but there's a distinction between direct and indirect access.


This division is very old. Assembly language is obviously a fat variable language, even though its variable system is very simple - registers and memory locations contain stuff directly, or contain references (memory addresses) to stuff. As languages need to be compiled to assembly plenty of high performance languages follow this road. Fortran and C variables are just assembly variables plus types. C++ didn't break up with it, it made container variables much fatter and much more complicated - RAII, copy constructors, assignment operators, and all the related mess. Java in spite of superficial similarity to C++ is definitely a thin variable language.

Thin variable languages are also very old. The original Lisp was the first language with thin variables, and all Lisp dialects, just like all ML and Haskell dialects, are thin variable languages. I don't think a single seriously functional language uses fat variables.

All object-oriented languages, most popular of them being Smalltalk, Ruby, Javascript, and (let's be charitable) Java - use thin variables too. There are some fat languages like C++ and Perl with objects bolted on top of them, but they are definitely not object-oriented, they just support some limited object functionality.

Scripting languages are interesting. Old languages like Unix shell have very fat variables, even though all variables are simple strings. Perl and PHP continue this tradition, but Python and Ruby are soundly in the thin variable camp.

Observations


Having categorized all popular languages let's do some observations.

  • All (pure and impure, strict and lazy) functional languages are thin variable.
  • All honestly object-oriented languages are thin variable.
  • Thin variable languages and garbage collected languages are very closely related categories. There are some reference counted languages in both camps (Perl, PHP on thick side, Python on thin side), but there seem to be no thin language with manual memory allocation or thick language with full GC.
  • All segfaulting languages (assembly, C, C++) use fat variables, but many fat variable languages are non-segfaulting (Fortran, bash, Perl, PHP).
  • Dynamic typing is on both sides (Perl/PHP vs Lisp/Ruby).
  • Explicit static typing is also on both sides (C/C++ vs Java).
  • Implicit static typing is only no the thin side (ML/Haskell) and is actually quite popular there.
  • Almost all thin languages have closures. A few languages like Python and Java have less than full closures, in form of named inner functions or anonymous inner classes. In both cases it's a syntactic not semantic limitation.
  • Almost no thick language has closures. A big exception is Perl, which has full closures.
  • Lexical and global scope exists on both sides, in almost every language.
  • Dynamic scope is unusual, but is supported on both thick (Perl), and thin (some Lisp dialects including the original Lisp, Emacs Lisp, and Common Lisp, but not Scheme) side.
  • Rich literal notation is supported (Perl vs Python/Ruby/Lisp/ML/etc.) and not supported (C/C++ vs Java) on both sides.
  • Macros exists only on the thin side (Lisps, Dylan, Nemerle). There doesn't seem to be any obvious reason for it.


I could go on. It actually surprises me how many semantic differences follow the thin vs fat divide, with Perl and Java being the biggest outliers (and also their derivatives like PHP and C#). These outliers are very interesting. Java's lack of power is definitely syntactic not semantic and there is plenty of JVM languages which are little more than fully compatible alternative syntaxes for Java with more expressive power. Nothing like that ever happened to popular fat variable languages like C/C++, which fail for semantic not syntactic reasons.

The biggest outlier on the fat side in Perl. While Perl was able to get almost 100% score on supported features checklist, it seems to be an evolutionary dead end. Every new thin variable language steals ideas from Perl, but Perl 6 effort was never able to transform the language, and Perl programmers have been leaving for Ruby and Python for years now.

If you're writing a new language today, and every programmer should do that, just forget about fat variables. They have one big advantage of allowing explicit memory management, what can still result in more memory efficient programs, but that's about it. Expressiveness of fat variable languages have been pushed to the limits by Perl, and it seems it cannot be pushed any further. Thin side is already far ahead, with Ruby, Scheme, Haskell and all the small research languages you've never heard of.

Wednesday, January 23, 2008

Really strange quirk of Ruby and Perl regular expressions

Sage's Kittens by T. Keller from flickr (CC-BY)

I've found a weird quirk of Ruby's regular expression engine. The same quirk is present in Ruby 1.8.6, Ruby 1.9.0, and Perl 5.8.8, but it is not present in Python 2.5.1. I'm going to let you decide if it's a bug or not.

I tried to replace all space at the end of a string by a single newline. The correct way to do it is str.sub(/\s*\Z/, "\n"). However I've done str.gsub(/\s*\Z/, "\n") instead. String has only one end-of-string, so there should be no way String#gsub could possibly match twice. But it does - the result is two newlines if there was any whitespace at the end, or one newline if there wasn't. I kinda see how it might be getting these results from implementation point of view, and it's not a big deal because there's a simple workaround of replacing #gsub by #sub, but it doesn't make much semantic sense to me.

Here are a few code snippets in Ruby, Perl and Python, with "foo" instead of whitespace for better readability.

# Ruby
"f".gsub(/o*\Z/, "o") # => "fo"
"fo".gsub(/o*\Z/, "o") # => "foo"
"foo".gsub(/o*\Z/, "o") # => "foo"
"fooo".gsub(/o*\Z/, "o") # => "foo"
"f".sub(/o*\Z/, "o") # => "fo"
"fo".sub(/o*\Z/, "o") # => "fo"
"foo".sub(/o*\Z/, "o") # => "fo"
"fooo".sub(/o*\Z/, "o") # => "fo"
"f".gsub(/o*\Z/, "x") # => "fx"
"fo".gsub(/o*\Z/, "x") # => "fxx"
"foo".gsub(/o*\Z/, "x") # => "fxx"
"fooo".gsub(/o*\Z/, "x") # => "fxx"

# Perl
perl -le '$_="f"; s/o*$/o/g; print $_' # => fo
perl -le '$_="fo"; s/o*$/o/g; print $_' # => foo
perl -le '$_="foo"; s/o*$/o/g; print $_' # => foo
perl -le '$_="fooo"; s/o*$/o/g; print $_' # => foo

# Python
re.compile(r'o*$').sub("o", "f") # => 'fo'
re.compile(r'o*$').sub("o", "fo") # => 'fo'
re.compile(r'o*$').sub("o", "foo") # => 'fo'
re.compile(r'o*$').sub("o", "fooo") # => 'fo'


For all you Ruby/Perl programmers out there, Python sub is equivalent of gsub or s///g not sub or s///.

Which behaviour makes more sense ? I think Python's much more intuitive. Should we treat it as a bug in Ruby/Perl and fix it, or accept it as an unintended feature ?

Wednesday, October 04, 2006

Why Perl Is a Great Language for Concurrent Programming

Cable Beach sunset by marj k from flickr (CC-BY-NC)If you asked people what things Perl is good for, they would talk about stuff like web programming, data processing, system scripting and duct taping things. But concurrency ? Can Perl seriously beat languages designed with concurrency in mind like Java (well, kinda) and Erlang ? Surprisingly it can, and here's the story.

Wikipedia contains a lot of links to other websites. About one per article. It does not have any control whatsoever over servers where they are hosted, and every day many of the links die. Some websites move somewhere else, others are removed completely, and there are even those that were incorrect in the first place due to typos etc. Nobody can seriously expect people to check millions of links by hand every day - this is something that a bot should do. And that's what tawbot does.

The first thing we need is to extract list of external links from Wikipedia database. There are basically two ways. First way is to get XML dump of Wikipedia and parse wiki markup to extract links. It's easy to get about half of the links this way, but it's pretty much impossible get close to all. There are at least three formats of external links, and even worse - links can also be assembled from pieces by templates hackery - like links to imdb in film infobox which are not specified explicitly, but only imdb code of a movie is in the article, and template turns the code into an URL.

The alternative is to use MediaWiki engine. Reparsing every single page would be really slow. That's not a new problem - every Wikipedia mirror needs to do the same thing. Fortunately they already fixed it, and Wikipedia dumps include SQL dumps of the tables that are technically derivable from the XML database, but are a bit too expensive. One of the tables is "externallinks" table (yay). Another is "page" table with pages metainformation (id, title, access restrictions etc.).

The first problem is to extract data from MySQL dumps somehow. I could run a MySQL server, but I hate using database servers for standalone applications. I would need to install MySQL server, setup user accounts, and create a special database on every computer I wanted to run the script on. And then deal with usernames, passwords and the rest of the ugly stuff. So I thought I'd use Sqlite 3 instead. It was to be expected that table creation syntaix is different - they aren't the same in any two RDBMS (SQL is as much of "a standard" as Democratic People's Republic od Korea is of "a democracy"). That part was easy. But Sqlite didn't even accept MySQL INSERTs !

Interting multiple values with a single INSERT statement is MySQL extension of SQL (a pretty useful one): INSERT INTO `externallinks` VALUES (a, b,c), (d, e, f), (g, h, i); and Sqlite doesn't support it. Now come on, MySQL is extremely popular, so being compatible with some of its extensions would really increase Sqlite's value as duct tape. Anyway, I had to write a pre-parser to split multirecord inserts into series of single inserts. It took some regexp hackery to get it right, and Sqlite started to accept the input. But it was so unbelievably slow that Perl 6 would have been released by the time Sqlite finishes importing the dump. I guess it was doing something as braindead as fsync'ing database after every insert. I couldn't find anything about it on man pages or Sqlite FAQ. But then I thought - the code that splits multi-value INSERTs into single-valued inserts is already almost parsing the data, so what the heck do I need Sqlite for ? A few more regexps, a lot of time (regexping hundreds of MBs is pretty slow, but it was way faster than Sqlite), and I have the list of links to check extracted. So far I didn't use any Perl or concurrency, it was unthreaded Ruby.

Now let's get back to Perl. There were 386705 links to check, and some time ago I wrote a Perl program verifying links. It is so simple that I'm pasting it almost whole here (headers + code to preload cache omitted):

sub link_status {
my ($ua,$link) = @_;
if(defined $CACHE{$link}) {
    return $CACHE{$link};
}
my $request = new HTTP::Request('HEAD', $link);
my $response = $ua->request($request);
my $status = $response->code;
$CACHE{$link} = $status;
return $status;
}

my $ua = LWP::UserAgent->new();
while(<>)
{
/^(.*)\t(.*)$/ or die "Bad input format of line $.: `$_'";
my($title,$link)=($1,$2);
my $result = link_status($ua, $link);
print "$result\t$title\t$link\n";
}
Basically it reads info from stdin, runs HTTP HEAD request on all URLs, and prints URL status on stdout. As the same URLs are often in multiple articles, and we want to be able to stop the program and restart it later without remembering where it finished, a simple cache system is used.

The program was getting the work done, but it was insanely slow. Most HTTP requests are very fast, so it doesn't hurt much to run them serially. But every now and then there are dead servers that do not return any answer, but simply timeout. When I came the next day to check how the script was doing, I found it was spending most of its time waiting for dead servers, and in effect it was really slow. Now there is no reason to wait, it should be possible to have multiple HTTP connections in parallel. Basically it was in need for some sort of concurrency.

Concurrent programming in most languages is ugly, and people tend to avoid it unless they really have no other choice. Well, I had no other choice. First thing that came to my mind was Erlang (really). The concurrency part shtould be simple enough, and it would be highly educational. But does it have something like LWP ? It would really suck to implement cool concurrency and then have to work with some lame half-complete HTTP library. So I didn't even try. Ruby has only interpretter threads, so concurrent I/O is not going to work. And it would be an understatement to call its HTTP library incomplete. But wait a moment - Perl has real threads. They are pretty weird, sure, but I don't need anything fancy. And I can keep using LWP.

Perl (since 5.6.0) uses theading model that is quite different from most other languages. Each thread runs as a separate virtual machine, and only data that is explicitly marked as shared can be shared. It also has syntax-based locks and a few other unusual ideas. The real reason it was done this way was retrofitting threading on fundamentally unthreadable interpretter. That's the real rationale behind Perl OO too. Of course it sucks for some things, but for some problems it happens to work very nicely.

The design I used was very simple - one master thread, and a group of worker threads. Master would keep pushing tasks on @work_todo, and threads would pop items from it and push result to @work_done. When master doesn't have anything else to do, it sets $finished to true and waits for workers to finish.

Before we spawn threads, we need to declare shared variables:
my @work_todo : shared = ();
my @work_done : shared = ();
my $finished : shared = 0;
Now let's spawn 20 workers (I originally had 5, but it wasn't enough):
my @t;
push @t, threads->new(\&worker) for 1..20;
Workers basically keep taking tasks from todo list until master says it's over:
sub worker {
my $ua = LWP::UserAgent->new(); # Initialize LWP object
while (1) {
    my $cmd;
    {
        lock @work_todo;
        $cmd = pop @work_todo; # get a command
    }
    if($cmd) { # did we get any command ?
        my ($link, $title) = @$cmd;
        # do the computations
        my $status = link_status($ua,$link);
        {
            # @done_item needs to be shared because threads are otherwise independent
            my @done_item : shared = ($status, $title, $link);
            lock @work_done;
            push @work_done, \@done_item;
        }
    } elsif($finished) { # No command, is it over already ?
        last; # Get out of the loop
    } else { # It's not over, wait for new commands from the master
        sleep 1;
    }
}
}
Link checker got even simpler, as cache logic was moved to master.
sub link_status {
my ($ua,$link) = @_;
my $request = new HTTP::Request('HEAD', $link);
my $response = $ua->request($request);
my $status = $response->code;
return $status;
}
After spawning threads master preloads cache from disk (boring code). Then it loops. The first action is clearing @work_done. The done items should be saved to disk and updated in the cache. It is important that only one thread writes to the same file. Under Unices writes are not atomic. So if one process does print("abc") and another does print("def"), it is possible that we get adbefc. Unix is again guilty of being totally broken, inconsistent and hard to use, for the sake of marginally simpler implementation.
while(1)
{
{
    lock @work_done;
    for(@work_done) {
        my ($result, $title, $link) = @$_;
        $CACHE{$link} = $result;
        print "$result\t$title\t$link\n";
    }
    @work_done = ();
}
if (@work_todo > 100) {
    sleep 1;
    next;
}
...
# More code
}
And finally the code to put new items on the todo list:
  my $cmd = <>;
last unless defined $cmd;
$cmd =~ /^(.*)\t(.*)$/ or die "Bad input format of line $.: `$_'";
my($link, $title)=($1, $2);
next if defined $CACHE{$link};
{
    # Explicit sharing of @todo_item again
    my @todo_item : shared = ($link, $title);
    lock @work_todo;
    push @work_todo, \@todo_item;
}
And the code to wait for the threads after we have nothing more to do:
$finished = 1;
$_->join for @t;
Now what's so great about Perl concurrency ?
  • It works
  • Access to all Perl libraries
  • By getting rid of really stupid "shared everything by default" paradigm, it avoids a lot of errors that plague concurrent programs
The most important are the first two points. Third is just a small bonus. Perl concurrency works a lot better than Ruby or Python (at least last time I checked). We get access to all libraries that Erlang will never ever get its hands on. And while "shared-nothing pure message passing" may be more convenient, at least explicit sharing and locking-by-scope are better than "shared everything" model. And we get the basic stuff like Thread::Queue (which I should have used instead of managing lists by hand, whatever).

Could such link checker be written in Erlang as quickly ? I don't think so. In most programs that need concurrency or would benefit from concurrency, the "concurrency" part is a very small part of the program. It's usually far better to have lame concurrency support and good rest of the language than absolutely awesome concurrency part and lame rest of the language. Every now and then you simply have to write a massively concurrent program (like a phone switch), and a special language can be really great. For everything else, there are "business as usual, with concurrency bolted-on" languages like Perl and Java.

Thursday, August 10, 2006

Amethyst

Amethyst cut by Wela49 from Wikimedia Commons (GFDL)This is some old stuff, but I haven't blogged about it before so here it goes. Amethyst is basically Perl with Ruby syntax and you can get it here. What it does is parsing Amethyst source (with Ruby-like syntax) using Parse::RecDescent, generating Perl code for the whole program at once (this is a very fragile part), and eval'ing it. Now Ruby and Perl have totally different sematics, so Amethyst hacks around both a lot - for example it can tell whether a Perl object "is" a string, or a number, so + operator acts the expected way on both, even though Perl pretends not to know that. Some examples. Iterators work:

arr = [1,2,3]
arr.each{|element| print(element,"\n")}
The code gets compiled to:
($arr = [1,2,3]);
call(
 method($arr,'each'),
 sub{
   my ($element)=@_;
   {call(function('print'), undef, $element, "\n")}
 },
 $arr
);
And of course prints 1 2 3. The following code prints "22" and "157" as expected:
xs = "15"
xi = xs.to_i

yi = 7
ys = yi.to_s

zi = xi + yi
zs = xs + ys

print(zi, "\n")
print(zs, "\n")
$xs, $xi, $yi, $ys are all native Perl objects, the same you'd get if you said $xs="15"; $yi=7 ; etc. Can you guess how to make a function that can tell them apart without looking inside Amethyst source code ? ;-) Don't expect too much. Unlike RLisp which is basically an usable (even if alpha-quality) programming language, Amethyst was just a single-evening "be evil" session, and half of it was spent learning Parse::RecDescent. Still, it was a lot of fun :-)

Tuesday, August 01, 2006

Ruby book sales pass Perl

Chart from O'Reilly
Normally I don't repost stories from other websites, but this one is just too cool to pass :-)

So basically Ruby book sales passed Perl, Python is far behind, and Javascript books are on the top, but are mostly about AJAX not actually programming in Javascript. It seems that Ruby is having huge momentum right now, as book sales indicate what people are learning now and therefore the technical trends for the next few years. So are we already getting there ?