good chemistry is complicated, and a little bit messy -LW |
|
PerlMonks |
Re^3: [DSL] disabling packages in Perl? (meditation)by haukex (Archbishop) |
on Apr 23, 2016 at 20:28 UTC ( [id://1161332]=note: print w/replies, xml ) | Need Help?? |
Hi Rolf, I've finally had some time to think about this some more. I don't think there's a clear answer here, but I've included some sample code in which I've experimented with a few things. First, the basic library, SQL_DSL.pm:
Next, my test script, in which one of the three sets of use statements at the top needs to be uncommented:
The output of version "one" is:
The output of versions "two" and "three" are:
As you can see, version "one" doesn't really add any magic except en-/disabling the SQL_DSL::WHERE and LIKE::AUTOLOAD functions in dynamic scope. Versions "two" and "three" get a little more interesting. They both work by inserting the declaration "package SQL_DSL;" inside the SQL { ... } block. Version "two" uses the experimental keyword plugin feature that is available as of v5.12. It seems to be a better solution than source filters, but it's been in experimental state for quite a while and back then the discussion sounded like it might not stay. Anyway, I implemented it using Keyword::Simple, this is SQL_DSL_Keyword.pm:
Lastly, version "three" is implemented with a source filter - yes, I know they're widely regarded as bad, and for good reason, but at least this one uses PPI for a hopefully decent parse, this is SQL_DSL_Transform.pm (based on PPI::Transform::UpdateCopyright and Programmatically Updating Code with PPI):
I realized that automatically switching packages inside an SQL { ... } block might not be such a good idea, because it breaks something as simple as sub get_value { ... }; SQL { xyz LIKE get_value() };. So instead, it might make sense to use a module or a pragma that is only effective in its lexical scope, i.e. SQL { use SQL_DSL; ...; no SQL_DSL; }. I agree that %^H does not seem to be useful to have something "unimported" or "undefined" at the end of a scope, it could only disable the functions like in my version "one" above. However, there is B::Hooks::EndOfScope, which is used by all three namespace::clean, namespace::autoclean and namespace::sweep! Or, one of the two methods above (pluggable keywords or source filters) could theoretically be used to insert a no statement at the end of the block. However, the problem that does remain is that these two methods are not particularly robust. I haven't yet come up with any better ways to enable and disable syntactical features in a certain lexical scope at compile time without reaching into the internals (then again I'm not that much of an expert, so I could be missing something - I did run across Devel::Declare but haven't had a chance to look at it more closely*). It might be possible to define your DSL in a way that a SQL { ... } block complies cleanly, and then the features used within it are only enabled at run time (sub SQL (&) { enable_features(); shift->(); disable_features(); }), but that might be too limited. On the other hand, if you're trying to add syntactic sugar that goes too far beyond what Perl can do natively, you're essentially defining a mini-language that extends Perl. And if you don't want to slap a source filter around your whole program, the simplest solution might be: sub SQL ($) { ... }; SQL q{ WHERE b LIKE %abc% }; A lot of the above thoughts are more about the technical "how" than the "why" or "what for". Of course a lot depends on what you want your DSL to look like and what you want it to do. For SQL, there's already SQL::Abstract and SQL::Statement, or perhaps, as you mentioned elsewhere in this thread, you want something more like CGI's HTML generation statements, select(from(...),where(like('b','%abc%'))... Anyways, that's just some more thoughts on the matter, maybe it helps! Regards, * Update: From the documentation of MooseX::Declare:
In Section
Seekers of Perl Wisdom
|
|