One of the most dangerous books I've ever even partially read is MJD's Higher Order Perl. In particular, its description of subroutine currying -- that is, building more specific functions out of more general purpose ones -- is a pattern I find incredibly useful.
The other day I found myself writing a number of routines that were surprisingly similar... kinda. They all implemented a common pattern, but across routines that were rather... different. I found myself wistfully longing for the familiar pattern of currying, and then realized -- I'm working in PERL, DAMNIT.
This is part of recent work of
Test::Moose::More to use subtests where they make sense. Here I was able to
curry one function --
_validate_subtest_wrapper() -- by passing it a
reference to another function, that it then invokes.
Excellent. Life is easier, as it should be.
...largely because I needed to relax, and wrote MooseX::Meta::TypeConstraint::Mooish :)
That means you can now pass a coderef to has() in isa that, like with Moo, dies on validation failure and lives on validation success:
I'm in lovely Madison, WI right now, and will be headed over to my first YAPC::NA tomorrow. The first couple days are the hackathon, at which I think I'm going to work on a Dist::Zilla::Role::Stash to hold repository related information. There are a bunch of Git related plugins for Dist::Zilla, a couple that I maintain, and a lot of code is duplicated between them; a stash should resolve that.
I hope to meet you all there! :)
I've seen a couple references lately to using lazy attributes as a form of caching. This is a great approach to thinking about lazy attributes, as they share a number of characteristics with traditional caching: you only have to build a (potentially) expensive value once, and then only when you actually need it.
But what about when that lazily generated value is too old to trust?
A lazy attribute isn't going to help you much then, as your instance is quite happy to keep on returning the same value forever once it has been built, unless you clear or change it manually. This is no good when, say, you've run a database query and you can really only expect your painfully contorted query to get the twitter ids of all the left handed Justin Beiber fans north of the Mason-Dixon line who own hypo-allergenic cats to be valid for, oh, say 55 minutes or so.
You could add an attribute to store the age of the value generated for the lazy attribute and check it either manually (boring!), or by wrapping the reader method (less boring, but still, unsightly).
Ok, method modifiers can be fun, but still... That's a lot of annoying little code that, well, isn't Moose there to help reduce that sort of code in our lives?
What we're running into here is that while we implement one part of a cache (generate once, return many), lazy attributes don't have any internal logic to determine when a value is no longer good. They don't even have any concept of that, just "someone needed my value, so we're going to get it and hang on to it until told otherwise".
This is just the sort of behaviour an attribute trait can alter.
The MooseX::AutoDestruct Moose attribute trait allows us to specify an expiration date for our stored values. We can specify a time-to-live option at attribute creation, and then every time a value is set, the set time is stored. Every time the value is accessed, the attribute checks to make sure the value isn't older than the set time to live, and if it is, clears the value. This allows the lazy value generation to kick in once more, without requiring any extra effort on the part of the user -- just as one would expect.