Saturday, May 25, 2013

surprising interaction betwen list context and range operator

I was preparing a quiz for Perl programmers, and wanted a question on the difference between arrays and lists. So the question was : what do you get in $x and $y after

my @tab = ( 1, 3, 6..9 );
my $x   = @tab;
my $y   = ( 1, 3, 6..9 );

where I expected $x to be 6 (the number of items) and $y to be 9 (the last element), as documented in perldata. But before distributing the quiz I thought it was worth a test ... and indeed it was! To my surprise, $y turns out to be an empty string. It took me some time to find out why : with

my $y = ( 1, 3, 6, 7, 8, 9 );

the result is 9 indeed. But with

my $y = ( 1, 3, 6..9 );

the interpreter does not build the range in  list context, and then keep the last element. What happens instead is that it throws away the beginning of the list (there is even a warning about that), and then evaluates

my $y = 6..9;

in scalar context; where 6..9 is no longer a range, but a flip-flop operator. Now since the left-hand side is true, that operator is supposed to be true, right ? Well, no, because 6 is a constant, and in that case, perlop tells us that the flip-flop is  "considered true if it is equal (==) to the current input line number (the $. variable)". So $y ends up being an empty string, while

my $six  = 6;
my $nine = 9;
my $y    = $six..$nine;

would yield 1E0!

I couldn't be that nasty to the interviewed programmers, so in the end that question will not be part of the quiz.

Sunday, February 17, 2013

Slices of method calls within an object

Several years ago I complained that object accessors prevent you from using some common Perl idioms; in particular, you couldn't take a slice of several attributes within an object.

Now I just discovered the Want module; this is great for playing with lvalue subroutines. With the help of this module, I was finally able to use slices of method calls within an object : see . There are some caveats in comparison with a usual hash slice, but nevertheless the main point is there : you can extract some values :

  my @coordinates = mslice($point, qw/x y/);

or even assign to them:

  (mslice($point, qw/x y/)) = (11, 22);

This was written just for fun ... but in the end I might use it in real code, because in some situations, I find slices to be more compact and high-level than multiple assignment statements.

Sunday, December 2, 2012

How to test if something is a Perl class ?

For Data::Domain I needed a way to test if something is a Perl class. Since UNIVERSAL is the mother of all classes, it seemed to make sense to define the test as

defined($_) && !ref($_) && $_->isa('UNIVERSAL')

Other people did it through $_->can('can') or UNIVERSAL::can($_, 'can'). Both ways used to work fine, but this is no longer true since June 22, 2012 (bleadperl commit 68b4061) : now just any string matches these conditions.

At first I found this change a bit odd, but somehow it makes sense because any string will answer to the 'can' and 'isa' methods. Also, bless({}, 'Foo') and 'Foo' now return consistent answers for ->isa() and for ->can(), which was not the case before. So let's agree that this change was a good step.

But now it means that  while every object or class is a UNIVERSAL, the reverse is not true : things that are UNIVERSAL are not necessarily objects or classes. Hum ... but what "is a class", really ?

Moose type 'ClassName' defines this through Class::Load::is_class_loaded, which returns true if this is a package with a $VERSION, with an @ISA, or with at least one method. By this definition, an empty package is not a class. However, perldoc says that a class is "just a package", with no restriction.

So after some thoughts I ended up with this implementation :

defined($_) && !ref($_) && $_->can($_)

This returns true for any defined package, false otherwise, and works both before and after June 22.

Thoughts ?

Saturday, December 1, 2012

Hash key order : beware of implicit assumptions

Perl hashes are not ordered, so one is not supposed to make assumptions about the key order. I thought I did not ... but Perl 5.17.6 showed me that I was wrong !

About two weeks ago I started receiving report about test failures which were totally incomprehensible to me. Since I work on Windows, I had no bleadperl environment, so it was hard to guess what was wrong just from the test reports. Andreas K├Ânig kindly opened a ticket in which he spotted that the problem was related to a recent change in bleadperl : now Perl not only makes no guarantee about the key order, it even guarantees that the key order will be different through several runs of the same program!

This convinced me of investing some time to get a bleadperl environment on my Windows machine : VMware player + a virtual Unbutu + perlbrew did the job perfectly. Now I could start working on the bug.

The point were I was making an implicit assumption was a bit nasty, so I thought it was worth writing this post to share it : the code in SQL::Abstract::More more or less went like this :

  my $ops   = join "|", map quotemeta, keys %hash;
  my $regex = qr/^($ops)?($rest_of_regex)/;

See the problem ? The regex starts with an alternation derived from the hash keys. At first glance one would think that the order of members in the alternation is not important ... except when one member is a prefix of the other, because the first member wins. For example, matching "absurd" against qr/^(a|ab|abc)?(.*)/ is not the same as qr/^(abc|ab|a)?(.*)/ : in one case $1 will contain 'a', in the other case it will contain 'ab'.

To fix the problem, the code above was rewritten to put the longest keys first, and everything is fine again.

Saturday, November 10, 2012

updated Data::Domain and new Test::InDomain

This is to announce new modules on CPAN:

a) module Data::Domain, written a couple of years ago for checking input from Web forms, now has number of new functionalities in v1.02  : new builtin domains 'Nat', 'Handle', 'Regexp', 'Obj', 'Class', 'Ref', new properties like -isweak, -readonly, etc., and new experimental support for checking method calls and coderef calls. Also, it now relies on Scalar::Does (for having a nice uniform way of checking what references "do" either as builtin Perl operations, or through overloaded methods), and on Sub::Exporter (for allowing clients to rename the imported functions). If you need to check deep datastructures against complex constraints, possibly with contextual dependencies or even recursive rules, then this module may be of interest to you.

b) the new module Test::InDomain is a wrapper around Data::Domain for writing automated tests; it sits more or less in the same niche as Test::Deep, but with a different API and some differences in functionalities.

I hope it might be useful to some of you. Have a look, and let me know of any comments/suggestions.

Friday, October 19, 2012

Rediscovering smart match

Perl's "smart match" operator came with Perl 5.10 in 2007 (as a matter of fact, it was released during the French Perl Workshop 2007, and this is where I also learned about this new feature). I immediately thought : "Wow, this is great, it is going to dramatically change my way of programming!".

Unfortunately, our infrastructure at work still remained Perl 5.8 for several years, and by the time I was at last able to make use of smart match, I had nearly forgotten all its virtues. I have a couple of lines of code with "given/when" statements, but that's mostly it.

However, after having recently attended the excellent class by Damian Conway on Advanced Perl Programming Techniques, smart match came back to my mind. I know, it's been criticized for being too complex in some of its matching rules; but the basic rules are great and yield more readable and more flexible code.

Here are some examples that I'm refactoring right now :

List membership

Instead of 

  if ($data =~ /^(@{[join '|', @array]})$/) {...}


  use List::MoreUtils qw/any/;
 if (any {$data eq $_} @array) {...}


 if ($data ~~ @array) {...}

No need to test for undef

Instead of

  my $string = $hashref->{some_field} || '';
  if ($string eq 'foo') {...}


  if ($hashref->{some_field} ~~ 'foo') {...}

No need to test for numish

Instead of

  use Scalar::Util qw/looks_like_number/;
  my $both_nums = looks_like_number($x)
               && looks_like_number($y);
  if ($both_nums ? $x == $y : $x eq $y) {...}


  if ($x ~~ $y) {...}