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 ?
Sunday, December 2, 2012
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.
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.
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 :
if ($data =~ /^(@{[join '|', @array]})$/) {...}
or
use List::MoreUtils qw/any/;
if (any {$data eq $_} @array) {...}
write
if ($data ~~ @array) {...}
my $string = $hashref->{some_field} || '';
if ($string eq 'foo') {...}
write
if ($hashref->{some_field} ~~ 'foo') {...}
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) {...}
write
if ($x ~~ $y) {...}
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 ofif ($data =~ /^(@{[join '|', @array]})$/) {...}
or
use List::MoreUtils qw/any/;
if (any {$data eq $_} @array) {...}
write
if ($data ~~ @array) {...}
No need to test for undef
Instead ofmy $string = $hashref->{some_field} || '';
if ($string eq 'foo') {...}
write
if ($hashref->{some_field} ~~ 'foo') {...}
No need to test for numish
Instead ofuse 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) {...}
write
if ($x ~~ $y) {...}
Friday, June 29, 2012
My slides for FPW12 are online
Thursday, June 7, 2012
Translating user queries into SQL::Abstract (using Regexp::Grammars)
As mentioned in my previous post , I've been quite interested in Regexp::Grammars recently.
My latest project with it is SQL::Abstract::FromQuery , just released to CPAN. This is a module to help building Web applications with complex search forms. It translates user input, as obtained from an HTML form, into a datastructure suitable as a %where clause for SQL::Abstract; that module will in turn produce the SQL statement and bind parameters to query the database.Users can type regular values, comparison operators, patterns, etc
Technically, this uses many advanced features of Regexp::Grammars : list results,"autoactions", named grammars, multiple inheritance of grammars and of actions, etc.; so if you ever wondered what these features are good for, here is an example. It also uses some great features of Perl, like dynamic loading of components, dynamic building of an anonymous subclass ... I really feel happy to work with a language that has such a wonderful toolbox.
Nate Wiger, the original author of SQL::Abstract, already had this vision of translating Web forms into SQL queries; here we elaborate on that idea and support some more details.
The module is still in early infancy, so avoid using it in production (the API probably needs some improvements); suggestions are welcome. Enjoy!
My latest project with it is SQL::Abstract::FromQuery , just released to CPAN. This is a module to help building Web applications with complex search forms. It translates user input, as obtained from an HTML form, into a datastructure suitable as a %where clause for SQL::Abstract; that module will in turn produce the SQL statement and bind parameters to query the database.Users can type regular values, comparison operators, patterns, etc
Technically, this uses many advanced features of Regexp::Grammars : list results,"autoactions", named grammars, multiple inheritance of grammars and of actions, etc.; so if you ever wondered what these features are good for, here is an example. It also uses some great features of Perl, like dynamic loading of components, dynamic building of an anonymous subclass ... I really feel happy to work with a language that has such a wonderful toolbox.
Nate Wiger, the original author of SQL::Abstract, already had this vision of translating Web forms into SQL queries; here we elaborate on that idea and support some more details.
The module is still in early infancy, so avoid using it in production (the API probably needs some improvements); suggestions are welcome. Enjoy!
Sunday, May 20, 2012
Who is using Regexp::Grammars ?
I've been using Regexp::Grammars for a couple of projects and I find this module really amazing : so much power in such a compact form ! Actually, the whole thing is a gigantic hack, but an extremely clever hack. The learning curve is steep, but once you are used to it, playing with callback actions, grammar inheritance trees or fancy "result distillation" strategies is a joy. The only drawback is that running callback functions within the Perl regexp compiler is sometimes tricky (in Perl versions prior to 5.14, the regexp engine is not reentrant; and debugging may be acrobatic).
I just had a look at the reverse dependencies and was very surprised to find so few CPAN modules that depend on Regexp::Grammars, while its predecessor Parse::RecDescent has many more dependencies. So I'm wondering why Regexp::Grammars is not more widespread. Some guesses :
I just had a look at the reverse dependencies and was very surprised to find so few CPAN modules that depend on Regexp::Grammars, while its predecessor Parse::RecDescent has many more dependencies. So I'm wondering why Regexp::Grammars is not more widespread. Some guesses :
- the learning curve
- the dependency on perl 5.10 regexes
- the regex engine reentrance problem mentioned above
Thursday, February 9, 2012
Plat-forms 2012 : Perl on Amazon Web Services ?
A new edition of the Plat-forms programming competition will take place in Berlin, 2nd to 4th of April --- see the announcement.
I enjoyed participating in the 2007 edition (see our report from that time), so I'm tempted to apply again; currently I'm trying to convince people to join in a team, and to convince my management to support this initiative.
However, my worry is that this year's competition is targeted at cloud computing and Amazon Web Services (AWS), of which I know nothing ... and the http://aws.amazon.com/ web site, which advertises "developer centers" for various languages, doesn't say a word about Perl. So it seems that quite an investment will be needed for learning how to use this environment and build up appropriate tools for a Perl AWS ecosystem.
Am I too pessimistic ? Is it much simpler than what I think ? If anybody has information about Perl projects on AWS, please post comments here. Thanks in advance, Laurent D.
I enjoyed participating in the 2007 edition (see our report from that time), so I'm tempted to apply again; currently I'm trying to convince people to join in a team, and to convince my management to support this initiative.
However, my worry is that this year's competition is targeted at cloud computing and Amazon Web Services (AWS), of which I know nothing ... and the http://aws.amazon.com/ web site, which advertises "developer centers" for various languages, doesn't say a word about Perl. So it seems that quite an investment will be needed for learning how to use this environment and build up appropriate tools for a Perl AWS ecosystem.
Am I too pessimistic ? Is it much simpler than what I think ? If anybody has information about Perl projects on AWS, please post comments here. Thanks in advance, Laurent D.
Subscribe to:
Posts (Atom)