Matthew Walton’s Blog

Wednesday, 2nd December 2009

Translating Python

Filed under: Perl 6 — Matt Walton @ 23:08

@paulrpotts tweeted about a blog entry explaining why the founder of a company chose Common Lisp for a web project. There were good reasons given for not using Python, which Paul agrees with, and Paul linked to a blog entry about some of his own experience in Python. In that is a bit of Python code, and I started thinking what it would look like in Perl 6.

The code relies on an XML node class, which is presumably one that you can find in Python’s library. Perl 6′s library is rather sparse right now, so I’m going to partially specify a Node class which is basically the same as the bits of it used by Paul’s Python code, just with some Perl 6-ish naming conventions.

class Node {
  enum NodeType <TextNode>;
  has $.data;
  has Node @.child-nodes;
  has NodeType $.node-type;
  method get-elements-by-tag-name(Str $name) { ... }
}

This, then, is a more or less direct translation of Paul’s Python code into Perl 6. The main difference is that Perl 6 has type annotations, we don’t have to import anything to get partial function application (that’s given by .assuming) and we don’t have to do any tricks to get string reduction, because of Perl 6′s [] metaoperator.

# Given a list of nodes, return the .data member of all nodes
# which have node-type of TextNode
sub get-text-data(Node @nodelist) {
    @nodelist.grep: { .node-type ~~ Node::TextNode }.map: { .data }
}

# Given a list of nodes, extract the text data and return it as
# one long string
sub extract-text(Node @nodelist) {
    [~] get-text-data(@nodelist);
}

# Given a node tree and an element name, find the first text
# element of that name and get the text from all its child nodes
sub get-one-text-element(Node $node, Str $name) {
    my @elements = $node.get-elements-by-tag-name($name);
    @elements.elems == 1 or die;
    extract-text(@elements[0].child-nodes);
}

# Given a node tree and a list of element names, extract all the
# text from each named node and return as a hash of name => text
sub make-text-element-dict(Node $node, Str @namelist) {
    return @namelist Z @namelist.map(
      &get-one-text-element.assuming(node => $node)
    );
}

This could of course be shorter – most of these subs don’t need to be defined here, we could turn it into a very few lines indeed, but I’m assuming that the subs might be needed elsewhere in the form presented here.

A few things to point out for Perl 6 novices:

As in Perl 5, variables starting with @ are arrays (if you want to get precise, it means there’s something in there which does the Positional role, so you can expect to be able to use [] element accessors and suchlike).

The [~] construct is actually two things – first, the infix operator ~, which in Perl 6 is string concatenation, surrounded by the [] metaoperator which turns it into a list reduction. Thus the effect is to concatenate all the list elements together and return one big string.

Perl 6 identifiers can have hyphens in them. The parser’s smart enough to distinguish between those and use of infix -.

Perl 6 subroutines can have formal parameter lists. These parameters, like Perl 6 variables, can have type information explicitly declared for strict checking.

You can pass blocks of code to methods like .map and .grep using :{} syntax, which saves some nested brackety things.

You can produce a curried version of a function using its .assuming method.

The infix operator Z works like the zip function in Python and Haskell.

I haven’t checked if this would compile, but I think it’s pretty close.

No comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Login Method

OpenID

Anonymous

Powered by WordPress