I've just opened the book Dive into Python by Mark
Pilgrim. The first example is a function that creates a
(database?) connection string. Knowing Perl, I wrote a close equivalent so I
could look at the differences.
First, the Python:
def buildConnectionString(params):
"""Build a connection string from a dictionary of parameters.
Returns string."""
return ";".join(["%s=%s" % (k,v) for k,v in params.items()])
if "__main__" == __name__:
print buildConnectionString({
"server":"localhost",
"database":"master",
"uid":"sa",
"pwd":"sekret"
})
Ok, defines a function of one parameter (buildConnectionString) -
and that parameter is called out in the signature. This leads me to
suspect that Python may actually know
that the function takes a single argument (though not it's type), and
that information may be available to the runtime (this is the kind of
thing that helps IDEs and other software tools).
Within that function we can provide a docstring. This is
something I'm familiar with from Common Lisp and it's a feature that
I like. I suspect that I'll find that this is available in the Python
runtime later on...
Then Mr. Pilgrim immediately indoctrinates us to Python's list
comprehensions. Good. We can see that literal strings have
methods (we're calling .join on the literal ";"), so they're an object
(in Perl strings are not). I'm not sure what params.items() returns
yet - a flat list? A set of pairs? I'm sure this is waiting for me
on the next few pages.
Ok, this is a simple enough task, and the code is nice and
succinct. The list comprehension makes the task clear (at least to
me). What does the equivalent Perl look like? (well, my equivalent
Perl, this is _my_ way of doing it, TIMTOWTDI
after all).
use strict;
use warnings;
=head2 buildConnectionString
Build a connection string from a dictionary of parameters.
Returns string.
=cut
sub buildConnectionString {
my($params) = @_;
return join(";",map { "$_=$params->{$_}" } keys %$params);
}
if (__PACKAGE__ eq "main") {
print buildConnectionString({
"server" => "localhost",
"database" => "master",
"uid" => "sa",
"pwd" => "sekret"
}),"\n";
}
Ok, right off, I won't write Perl without the 'use strict;' and 'use warnings;' pragmas since
they are big time savers. The first way in which they save time is
that they help identify errors at compile-time that would otherwise be
caught at run-time, and they point out conditions that you likely
weren't handling (like using the contents of a variable before
assigning to it).
Then there is the external documentation. My team has adopted the
javadoc-like habit of making the api documentation in-line with the
code. The difference here though is that there is no formal
relationship between the POD and the function
itself - unlike in the Python docstring (and in Javadoc, where
adjacency is an implicit relationship). This means that the Perl
documentation can't be (easily) the same kind of runtime value-add as
the Python docstring (go Lisp).
The next thing is the function signature, or rather lack of, in
the Perl function. Again, my team has adopted the convention of using
a single line to do function argument destructuring - that way the
first line of our functions is at least representationally equivalent
to a signature. This makes our Perl applications a lot easier to read
and maintain. Of course there are some cases (at least in Perl) when
it can be useful to slightly modify the argument list, replace your
function call with some other and pretend like the first never
happened, though I'm not going into that right now. The main
difference is that our convention isn't a formal part of the language,
while in Python it is - and may additionally be a value-add at runtime
(I'm sure I'll find out later, again, this is the kind of thing that
helps IDEs and tools).
As far as the actual implementation, there is little logical or
semantic difference between Perl's join/map and Python's
join/list-comprehension. Perl's join is not a method. Neither uses
an iterator off of the collection (as Ruby would), but both apply an
operation to every (logical) item in a set (in the Python case it's a
pair, though as I said I'm not sure how the destructuring
happens yet).
There are other smallish differences too, I could have used Perl's
sprintf, which would have more closely aligned with Python's implicit formatter:
sub buildConnectionString {
my($params) = @_;
return join(";",map { sprintf "%s=%s", $_, $params->{$_} } keys %$params);
}
But either language is more than capable. With the overloading of
curly braces, even with my 10+ years of Perl experience, it's still a
bit fuzzy to my eyes where the break is between the end of the
hash-table (which is a map) and the code-block for the map. It's not
that I can't read that code, or read it quickly, the dual meaning of
the curly braces means that I have to use grammatical cues rather than
just syntactic ones.
This is less than a first impression mind you. I'm trying hard to
stay out of the Turing Tar pit.
I like the list comprehensions and I like that Mr. Pilgrim introduced
them right away in the book. I'm looking forward to the rest of
it. |