Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Re^6: Modern Perl Programming Highs and Lows

by Sartak (Hermit)
on Apr 30, 2009 at 01:49 UTC ( [id://761019] : note . print w/replies, xml ) Need Help??


in reply to Re^5: Modern Perl Programming Highs and Lows
in thread Modern Perl Programming Highs and Lows

Test::Exception's functions are used in 136 test files. If you really want to avoid such a large chunk of the test suite, you might as well not run tests, or ignore the Test::Exception failures. I'm much happier demanding users to install Test::Exception. That we can be sure all the error conditions of Moose fail loudly, not quietly, on each user's machine.

That's not to say we add dependencies on a whim. I added, then quickly removed, a mandatory dependency on Test::Output. We were not using it in more than a few test files, so it was not worth the hassle of a new dependency. We know installing dependencies can suck.

Chris Prather is currently finishing up a blog post justifying each dependency. In general, we'd much rather depend on a solid, tested module than poorly implement half of its functionality.

  • Comment on Re^6: Modern Perl Programming Highs and Lows

Replies are listed 'Best First'.
Re^7: Modern Perl Programming Highs and Lows
by BrowserUk (Patriarch) on Apr 30, 2009 at 07:40 UTC
    In general, we'd much rather depend on a solid, tested module than poorly implement half of its functionality.

    And there's the crux of the problem.

    If you would only need to (poorly*) implement half of a module's functionality, then it means that by using it, your are also loading the other half of its functionality that you do not need. And that is deadweight.

    It's like having: a people carrier for the seats; and a Ferrari for the speed; and a Rolls for the fridge; and a Citreon for the ride; and a Beemer for the Stereo; and a Volvo for the safety; and a Astra for the bike carrier; and a Polo for the iPod interface; and then loading them all onto a 40-ton car transporter and driving around in that to ensure you have everything you need!

    The problem with many of these modules is that their basic functionality--the stuff you want--is relatively small, but the authors feel funny about putting a module on CPAN that only contains one or two small routines, so they add 3 or 4 other flavours of the basic routines that people "might find useful". And by the time you've loaded up a dozen dependancies, you've got 1/2 a meg of stuff you need, and 10 meg of stuff you never use.

    If module authors used the Autoloader & Autosplit modules (as exemplified by modules like POSIX), so that their users could select what they need at compile time--or just stuck to providing the good bits without adding all the frills--then at least the problems of runtime bloat would recede.

    (*)Why does it have to be poorly implemented? You have the good implementation as a crib. One possibility would be to produce a Moose::Utils module that extracts (and tailors) just the bits of your dependancies that you use, and ship that with Moose instead of the dependancies.

    • Moose gets just what it needs--which helps your start-up delays and runtime bloat problems.
    • Moose users get less dependancies and more performance.
    • Moose developers and users benefit from not suffering the vagries of random authors making random changes.
    • As dependancy modules change through upgrades and bug fixes, they can be integrated back into the Utils package in a controlled fashion--if needed.
    • Moose benefits from not suffering breaks, because of accidental reliance upon bugs that get fixed.
    • And it affords the opportunity to tune, prune and even improve those dependancies in ways that might even be useful fed back to the originating modules--without stalling your improvements waiting for author approval or tuits.

    I realise that the idea of C&P reuse is an anathema most places, but re-use can be taken too far. The head/side/taillight requirements of most cars are pretty much standard, but the units are rarely re-used--even between models of the same manufacturer. The other criteria of the overall design--aerodynamics; aesthetics; etc.--dictate against such re-used despite the common requirements. The bulbs get re-used though!


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      If you would only need to (poorly*) implement half of a module's functionality, then it means that by using it, your are also loading the other half of its functionality that you do not need. And that is deadweight.

      Actually Moose and Class::MOP between them use every single function Text::Exception exports. Ack through the test suite for yourself and see how pervasive it is. In general I would agree with the sentiment you have there, but Test::Exception is (ironically) the Exception that disproves this rule.

      As for 17 dependencies, I'm not sure where that number came from for Test::Exception, by my count it has 6 dependencies. One of the dependencies Sub::Uplevel is not a core Module, it in turn depends on Carp and Test::More. The rest of those dependencies are on dual life modules, sometimes that exist in a single dist (Test::Simple, Test::More, and Test::Builder are all explicitly named although last I checked they came in the same dist) and depend on versions that exist in 5.8.9's core but not in earlier core Modules. So if you are running perl 5.10.0 which has been out over a year, or 5.8.9 which has been out since December, you'll have those dependencies in core.

        I now see why someone thinks Test::Exception has 17 dependencies. They trust someone else to do their resarch for them rather than actually going and looking at what it is they're asking. Yes if you assume that everyone is a Perl released over 6 years then yes Test::Exception will have 17 dependencies.
      As dependancy modules change through upgrades and bug fixes, they can be integrated back into the Utils package in a controlled fashion--if needed.

      I'm not sure the Moose developers want to be in the business of maintaining forks, especially given the risk of debugging (or merging) divergent behavior.

        I'm not sure the Moose developers want to be in the business of maintaining forks

        I think it only becomes a problem if you term it "forks"!

        Often as not, when you utilise subs from modules written by others, you end up writing your code around their interfaces. Adding glue code and coercions to take your data into and out of the forms required by the subs you utilise. And that has to either be done at each site you call those routines, or you wrap their sub in your own and do the coercions once there.

        Once you've wrapped their subs into your own wrappers, it's not a big step to lift their implementation into your wrapper to avoid or simplify the coercions required.

        From the sounds of things above, it looks like the Moose developers are already moving to replace some of their dependencies with XS code to improve their performance. Does that count as "forking"?

        Rolling up their dependencies into a support package, thereby isolating themselves from divergent development by myriad authors, could be a good first step on that road. As it would allow them to establish a dependency-free test-suite as a baseline for ensuring their XS conversions don't introduce bugs. It would also provide a pure-Perl fall-back position.

        One could even see the possibility of enlisting the help of the dependency authors in building and maintaining the individual parts of the Utils package. Enlisting their help and expertise in getting the "extractions" tailored to Moose requirements and maintaining them in line with developments in the dependency packages. That kind of cross-collaboration seems to be right in line with the aspirations of OSS.

        Still, I'm just floating ideas--with no expectation of anyone picking them up.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re^7: Modern Perl Programming Highs and Lows
by educated_foo (Vicar) on Apr 30, 2009 at 09:52 UTC
    Is it just me, or is this almost another CPAN one-liner?
    use Test::Simple 'no_plan'; sub throws_ok(&@) { my ($test, $qr, $msg) = @_; eval { shift->() }; ok($@ =~ /$qr/, $msg); } throws_ok { die "blah" } qr/bla/, 'blah blah';

      Your implementation provides no diagnostics on failure; that code accounts for much of the rest of Test::Exception's complexity.

      Well... you neglected to account for DESTROY { eval {} } and DESTROY { die } for any objects created in your test code. You failed to account for buggy overloading in exception objects. To do all of that correctly you've got to use the return value of the eval{} to decide failure, hook and unhook $SIG{__DIE__}, save $@ so when you stringify it, you don't accidentally cause it to clobber itself.

      Perl exception handling is actually a garden of pain.

      ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

        Those things you mention fall into the "doctor, it hurts when I do this" category. Normal code shouldn't need DESTROY at all, and if it does, it should be extremely careful. And if you're creating complex overloaded exception objects, you have already lost. What does your test module's overloaded $SIG{__DIE__} do with a module that itself overloads $SIG{__DIE__}?

        Sure, it's possible to build ever-more-complicated exception mechanisms with lots of corner cases, and to require more testing modules to control them. Sometimes you may even be forced into this silliness. But since you're inviting a world of hurt by doing so, you should ask yourself first whether a simpler solution could work.