http://www.perlmonks.org?node_id=391941


in reply to Being more Assert-ive with Perl

You should check out Params::Validate. It's much less verbose than what you're doing, and it's coded in XS so it's reasonably fast, though perhaps not as fast as your hard-coded tests. Your first example would like this:

my ($quarter) = validate_pos( @_, { type => SCALAR, callbacks => { 'must be 25' => sub { $_[0] == 25 }, } );

I think that consistently using this module makes your code even more declarative.

Replies are listed 'Best First'.
Re^2: Being more Assert-ive with Perl
by stvn (Monsignor) on Sep 18, 2004 at 04:52 UTC
    It's much less verbose than what you're doing,...

    Personally I don't see that. I have looked at Params::Validate before, and I didn't much care for it's style of parameter handling. Even if this is in XS, I would think it wouldnt be faster than a raw boolean. Already in your example there is a subroutine call (validate_pos) as well as the construction and analysis of nested hash ref (which is surely pretty fast if in XS), and then the creation of a code-ref (which I assume brings along the overhead of creating a closure, although I may be wrong in this). This seems to me to be a lot of overhead just to get to the point at which things can be checked.

    I think that consistently using this module makes your code even more declarative.

    I disagree, I think the more declarative approach is to use the basic boolean expression. But that is likely just a matter of personal preference/style.

    -stvn

      >> It's much less verbose than what you're doing,...

      > Personally I don't see that. I have looked at Params::Validate before, and I didn't much care for it's style of parameter handling.

      When you're validating only one parameter, the framework takes up more space than the declarative parts. But as you start adding more parameters the framework pieces grow at a much slower rate than the declarative parts. But with your code, you'll be adding another "or die" for each parameter.

      Anyway, I don't think you have to use it, but I think your optimizations here are seriously premature. You should be using some sort of module for this stuff, not hand-coding it over and over and over, especially since you say you use it everywhere. You could use a source filter if you're really dead set on maximum speed.

        But as you start adding more parameters the framework pieces grow at a much slower rate than the declarative parts. But with your code, you'll be adding another "or die" for each parameter.

        I agree with you there, and I can see where this will happen if you are validating a large number of parameters coming in from say a web request or something like that. At that point, something like Params::Validate is the superior solution. However, when I speak of pre/post-conditions I am really talking about checks on arguments for methods in classes. IMO it is good style to not have more than few (2-3 tops) parameters for methods. Any more parameters, and it is likely that you need to either subclass or be more polymorphic. Validating a few (2-3) parameters with || die is not a problem, and can even be done in a single line if you want.

        Anyway, I don't think you have to use it, but I think your optimizations here are seriously premature.

        But they are not optimizations, but instead using the optimial approach. As I said in another post here, why would you not want to use the sharpest knife in the drawer? I see this style of assertions as being easy to read and understand, and having the added benefit of also being fast too. If I was going to build an application, and needed a module which had a pure perl version and an XS version, I would choose the XS version because it just makes sense (assuming of course they were reasonably equivalent).

        You should be using some sort of module for this stuff, not hand-coding it over and over and over, especially since you say you use it everywhere.

        Why should I use a module for this? It is not something that IMO needs to be in a module, and any module I would write would still require me to declare my conditions which is the bulk of what I am doing with this approach anyway.

        I agree that with a large number of parameters, each of which have specific validation needs, something like Params::Validate is the way to go, but surely you would agree that it would be overkill for just testing if an argument is defined and of a specific type? (which is the majority of what I use this for)

        Modularity is a great concept, but like all good ideas, it can be overused or just badly used. There is a time and place for a module, and there is a time and place for a good solid idiom. I think that (simple) pre/post-condition checking of method/function arguments is be done with an idiomatic approach rather than trying to abstract and modularize a general purpose approach which will almost surely never be optimal in all situations.

        You could use a source filter if you're really dead set on maximum speed.

        First of all getting even midly complex source filters right is difficult at best and impossible at worst, otherwise you would see some kind of full blown perl macro system out there. Perl is just very hard to parse, and doesn't lend itself well to things like this. Source filters are not a way to increase relability and robustness, they are a way to introduce subtle and insidious bugs.

        Second, I am not dead set on speed. I am dead set of building robust and reliable modules which break in all the right places. And in order to achieve that I would like not to have to sacrifice speed (subroutine calls) and memory (module loading). I would like the users of my modules not to have to deal with the consequences of my descisions/trade-offs.

        -stvn
Re^2: Being more Assert-ive with Perl
by stvn (Monsignor) on Sep 18, 2004 at 15:31 UTC

    I decided to benchmark this and see what the difference was. Here is the script...

    #!/usr/bin/perl use strict; use warnings; use Benchmark qw(:all); use Params::Validate qw(:all); sub ParamsValidateAssert { my ($test) = validate_pos( @_, { type => SCALAR, callbacks => { 'must be less than 50' => s +ub { $_[0] < 50 }}, } ); return $test; } sub OrAssert { my ($test) = @_; ($test < 50) || die "Test is wrong"; return $test; } my @nums = map { ((rand() * 100) % 50) } (0 .. 100); cmpthese(10_000, { 'ParamsValidateAssert' => sub { eval { ParamsValidateAssert($_) } +for (@nums) }, 'OrAssert' => sub { eval { OrAssert($_) } +for (@nums) }, });
    Here are the results...
    Benchmark: timing 10000 iterations of OrAssert, ParamsValidateAssert.. +. OrAssert: 8 wallclock secs ( 7.02 usr + 0.01 sys = 7.03 CPU) @ 14 +22.48/s (n=10000) ParamsValidateAssert: 97 wallclock secs (87.88 usr + 0.38 sys = 88.26 + CPU) @ 113.30/s (n=10000) Rate ParamsValidateAssert OrAssert ParamsValidateAssert 113/s -- -92% OrAssert 1422/s 1155% --
    It is almost 100% faster to use OR. Again, as I said, I think my style is less verbose and more readable so I favor it becuase of that as well.

    But as water points out below, it should not completely be about the speed. I am sure that Params::Validate has a number of wheels I would not want want to have to re-invent when I hand-code OR based assertions. Looking over the docs for Params::Validate, I see where this could really be a useful tool for data validation, not just of subroutine parameters, but of web queries and such. As you say it can be quite declarative and I can see how you could build some very sophisticated data validation with it. However, I do think it is overkill for subroutine params and post/pre-condition checking.

    -stvn