My original title was "Another context trap", but I changed my mind. Read on.
Easy things are supposed to be easy to write in Perl, right? Let's try this one: write an (efficient) identity subroutine one that returns the arguments given to it.
Ok. Now you write down your solution and come back to read the rest of it.
That was my first attempt:
my $id = sub { return @_; }
And it looked like a good idea at first, as the @_ gets all the sub arguments (whether they be zero, one or many). And it seemingly worked.
$id->() returns ()
$id->(42) returns (42)
$id->(42,42) returns (42,42)
Except that I was not totally honest, and these succeed only when the function is called at list context. If that's not the case, we have:
$c = $id->(42); # 1 gets assigned to $c (from scalar @_)
($a) = $id->(42); # good, 42 gets assigned to $c
The quick solution is to take into account the calling context and write down the code as:
$id = sub {
wantarray : @_ ? $_[0]
}
The fault of this is a Perl feature that most other programming languages don't have: sensitivity to calling context. Those others are happily relieved of this because they are poorer in semantics than Perl in that aspect.
The identity function needs to be defined from a domain into the same domain: A -> A. Then, if we consider the non-void contexts Perl 5 provides: list and scalar, we have not one but two identity functions to consider. Namely,
sub { @_ } # for list context, LIST -> LIST
sub ($) { $_[0] } # for scalar context, SCALAR -> SCALAR
Note. The sneaky prototype in the last sub definition entered the scene to guarantee the homomorphism between the domain and image, that was not tested in the previous definition. So to be 100% correct, that should do it:
sub {
if ( wantarray ) {
@_;
} else {
die "too many arguments" unless @_==1;
$_[0];
}
}
The trap in this case is more like a fault of all other programming languages that don't have call context sensitivity. Because if they did, like Perl, they would push you up to think harder about some concepts like identity itself.
What Perl 6 will have to do with this? You bet no one is going to draw back and "relieve" Perl 6 of that extra expressiveness her mother language has. On contrary, Perl 6 will have even more call context sensitivity. But IIRC you will be able to define nice multisubs which dispatch on return type and will avoid unintented uses.
Hm, how they would look like? I am not sure. Maybe
multi sub id(Any $x -> Any: ) { $x }
A few days ago I was bitten by the dual nature of reverse.
$s = reverse 'xyz'; # 'zyx'
@a = reverse qw(a b c); # qw(c b a)
This is even worse because
reversein scalar context may receive a LIST as argument and concatenates the reverse string of each element.$s = reverse qw(abc mni xyz); # 'zyxinmcba'
There was a code where a method history was meant to return
an array and got used in a few places this way:
$history_size = $o->history();
which worked ok, until I found a subclass implementation which had been coded like this:
sub history {
shift;
return ( reverse @history );
}
and the assumption that it should return the (history) array size when called in scalar context failed miserably. The solution was to document what it was supposed to return in list and scalar contexts and to change the implementation to:
sub history {
shift;
return my @h = reverse @history;
}
That's the kind of thing I remember when someone says: "What's so wrong with Perl 5 that we might need Perl 6?"
That's the kind of subtlety that caught you by surprise occasionally, beginner or not. Idiosyncracies like that put a burden at the programmer which has not a perfect memory (and I am one of such programmers unfortunately, I have to admit) and makes some feel that a Perl guru is needed to detect, solve and explain such bits. This kind of weird behavior leads sometimes to workarounds because there is no time to think about what's wrong with that f... piece of code. And then it distorts into ugly things that contributes to the bad fame of our favourite language.
Such ugly things go like a current joke on Portuguese ortography. A friend tells the other: "Write me a check of 'sessenta' Brazilian Reais" -- "How dow I spell 'sessenta'?" -- "Well, make two checks of thirty." (Background: the word "sessenta" (sixty) may raise doubts to careless people about the right spelling: "sesenta", "seçenta", "sessenta".)
If Perl 6 fails to get rid of such subtleties (and this is one of its design principles), it will have failed to be the improvement of the Perl language it was supposed to be.
I wrote a monkeypatch (dirty, I have to confess) so that I could use CPAN::Reporter with my ISP mail server which requires authentication.
I had to do it by touching Test::Reporter as well. They will be available in CPAN at:
In Test::Reporter, a new accessor net_smtp_args was created which should be filled with a hash ref. If it contains something like:
{ auth => { user => 'me', pass => '***' } }
and Net::SMTP is the current transport, it will attempt to do SASL authentication after opening the connection.
In CPAN::Reporter, the changes allow the code to read from the config file ~/.cpanreporter/config.ini two config values which get passed into Test::Reporter:
# to do SMTP/SASL authentication, add these to ~/.cpanreporter/config.ini
smtp_auth_user=me
smtp_auth_pass=***
This is not a permanent solution because CPANT::Testers::Transport::* is in the works to replace Test::Reporter which will allow more natural/elegant approaches to that.
WWW::CPAN is a work in progress (slow progress, to be honest). At 0.004 release, its API got one more method, query(). The whole API is:
Ain't that promising? Everyone will use it when 0.999 release arrives. (Yes, updates are by +0.001 increments.)
This week has seen a turmoil of emotions surrounding Perl 6. (Just take a look at the last articles here at use.perl and perlbuzz.) This entry is just to gladly announce that in this very moment (Dec 28 2007 18:30 GMT-0200) three microarticles of the series on Perl 6 operators are in The Hot 25 list of O'Reilly Network Blogs.
It may be due to other causes:
But on the other hand, given that I am not that endowed for writing, I rather believe that
So I want to thank everyone that contributed to the current state of Perl 6-related development and to wish much fun in the future work that will bring us this long-awaited Christmas gift.
Also known as "The trap of SUPER::method"
I never have grokked SUPER and what exactly it could do for me. Until I wrote Class::Constructor::Factory.
Class::Constructor::Factory is a helper module to prevent me to spend time writing constructors after using a module like Class::Accessor (and siblings) that gave me for free handy accessors/mutators for object fields. Those modules also gave me a constructor for free. But I am never happy with constructors without a way to specify defaults when initial field values are not given.
So C::C::F is a step towards a more automated way to get better constructors for free and, in a near future, to write classes with less effort. Actually, using the 0.001 version of the module looks like this:
package Foo;
use Class::Constructor::Factory;
use base qw( Class::Accessor Class::Constructor::Factory );
__PACKAGE__->mk_constructor0({
foo => 42,
bar => 'fourty-two',
});
__PACKAGE__->mk_accessors(qw( foo bar ));
The thing is that C::C::F augments the actual constructor provided by the IS-A chain of the new package with handling of defaults. So a closure is built and then installed into &Foo::new to work out the wanted magic.
my $foo = Foo->new();
is( $foo->foo, 42 );
is( $foo->bar, 'fourty-two' );
That is rather straightforward and involved code which looked like this:
package Class::Constructor::Factory;
...
sub make_constructor0 {
my $self = shift;
...
# return a closure
return sub {
my $self = shift;
...
return $self->SUPER::new->( $self, \%f ); # XXX the offender
};
}
I must say that this code is deceptively simple and wrong. Because ->SUPER::new tried to invoke the &new of Class::Constructor::Factory superclasses (which does not exist). I understood why by reading the docs of SUPER and perlobj. The relevant quote is:
"It is important to note that SUPER refers to the superclass(es) of the current package and not to the superclass(es) of the object."
So even after the closure was installed into the Foo namespace, ->SUPER::new yet referred to the superclass of the package where the code was compiled, not doing what I wanted.
The solution is to use SUPER and then rewrite the offender code line as:
return $self->super('new')->( $self, \%f );
After this, the &new is searched correctly among the superclasses of the object and everything is just fine.
This entry is just to remind me that ->SUPER::method and dynamic code generation (or equivalently dealing with closures in namespaces other than where they were compiled) is at least misleading. I don't claim to have understood all the issues SUPER may address, but I learned something. Thanks, chromatic! And maybe this trap may be turned off in the future by toogling a feature in a future Perl 5 version.
I would really appreciate feedback on these texts. Corrections, suggestions, etc.
So far I published more two articles at OnLAMP:
and got to complete three drafts for the next ones:
I would appreciate that interested people read them and point mistakes and improvements to be done.
P.S. Yeah, sadly I know that some links at OnLAMP are wrong or missing. But I am afraid that MT changes URL again if I try to update the articles. I don't know what is worse.
After a long pause, I am back to the business of writing the micro-articles on Perl 6 operators.
The article I want to publish tomorrow is about boolean operators and it is here. I welcome corrections and suggestions.
The fearless Michael Schwern kept his word and brought back the Test-Simple fix for BEGIN { use_ok } bug. (If you don't know what I am saying, read this).
Some CPAN dists remain unfixed and those who upgraded the basic test modules will see an error while testing. Outstanding examples are:
Notice that the fix called for in this distributions is a real test bug. A contrived example is of the form:
use Test::More;
if ( $exp_which_tells_test_should_be_skipped ) {
plan tests => 1; # this will happen at runtime
} else {
plan 'skip_all' => 1; # that too
}
BEGIN { use_ok 'File::Temp' } # at compile-time
ok(1);
When Test::Builder had the bug, it swallowed the exception during BEGIN { use_ok } which announced a test before planning. Also, the test count should not include the use_ok to get pass this point. Yes, for some time, we authors have introduced not one, but two bugs in this type of code to make it work. Now it is time to write it down correctly.
use Test::More;
BEGIN {
if ( $exp_which_tells_test_should_be_skipped ) {
plan tests => 2;
} else {
plan 'skip_all' => 1;
}
}
BEGIN { use_ok 'File::Temp' } # at compile-time
ok(1);
Bonus point: The fixed version works ok with the old (buggy) and the new Test-Simple.