qk: Quote Membership Hash Keys
I recently
ran across an article (http://neilb.org/2016/08/08/quoted-words-arrayref.html)
by neilb, advocating for a qa() operator that
returns an arrayref instead of a plain array:
our
$cars_ref = qa(sedan hatchback coupe);
I love this idea, and I would use it a lot.
I’d like to
add onto this proposal another qw()-style
operator to quote the keys of a membership hash. I suggest qk() for “quote keys.” So
our
%is_color = qk(red
blue green);
is the same as
our
%is_color = map { $_ => 1 } qw(red blue green);
or
our
@colors = qw(red blue green);
our
%is_color = map { $_ => 1 } @colors;
Unlike my previous entry (symbolic “xor”), I wrote this line of code ALL THE TIME. Taking a cue from neilb, I looked on CPAN to see how often other people do this. It turns out the answer is “a really, really lot”: http://grep.cpan.me/?q=map%5Cs*%7B%5Cs*%5C%28%3F%5Cs*%5C%24_%5Cs*%3D%3E%5Cs*1%5Cs*%5C%29%3F%5Cs*%5C%7D%5Cs*qw
I like your idea, although I found the result of
qk()
quite surprising. It's useful after all.However, I suggest writing
my $arrayref = qa[foo bar]
to make it look like an arrayref constructor.I assume that qa() and qk() would allow to use any character or delimiter pair, just as q(), qq(), and qw() do.
That said, I would agree that qa[] is the most natural way of using qa().
Well, if you're going to document qa() as qa[], you should then use qk{}, no?
I thought about it, but "qk{}" implies that qk() returns a hash REF. As proposed above, at least, it returns a plain hash.
If you think qk() should return a hash ref instead, that's a different discussion, but I think consistency with the baked-in feature qw() outweighs consistency with the proposed feature qa[]. Also, I typically store membership hashes in plain hashes because
drive($car) if $is_working{$car};
is a bit cleaner than
drive($car) if $is_working->{$car};
so given my druthers, qk() would return a plain hash.
I’m not a fan of either proposal, though especially not of
qa()
, because of how little they offer, and how easily they are replicated by just chucking a few functions into List::Util:… and though
array
may not seem worth it, it does have the advantage over[]
that you can often leave off the parentheses in the function call, in which case it offers prefix notation rather than the built-in circumfix.Then old perls can easily run this code by just upgrading their List::Util.
A thought I’ve had a number of times, though, is that I’d like for simple slices to be allowed within declarations, so you could say things like this:
… and this would declare
%is_color
while assigning to it. And same thing for array slices.This feature generalises to many more situations than
qa()
andqk
, e.g.The problem is that we don’t have someone like Larry any more who a) tries to keep the design of the language as a whole in their head and b) has the legitimacy to say “no” to nice-looking but ultimately ill-fitting proposals for consistency reasons. (And I’m not saying it’s my proposal that Larry would keep; maybe mine is the crazy one, and he would go with something completely different.) This was of course precisely the problem with the RFC process that factored into why Perl 6 became such a huge undertaking – namely, almost every proposal was one-off solution focused on one corner of the language, and however reasonable they were individually, putting all of them together would have made a huge mess of the language.
I could live without either. I think qk() is more useful than qa(), given that qa saves you two non whitespace keystrokes:
my $aref = qa( a b c );
## vs
my $aref = [qw( a b c)];
And yes, Aristotle is correct; qk() is the better syntax given usage would be
my %h = qk( a b c )
And if you want a hash ref, then this should be sufficient:
my $h = { qk(a b c ) };
I'm not a fan of either proposal... because of how little they offer
In my opinion, qk() offers clearer code, and that's not a little thing. I will not comment further on qa() since it isn't my idea, although I personally like it.
they are replicated by just chucking a few functions into List::Util
While qa() and qk() could indeed be provided as a function in List::Util (or List::MoreUtils), Perl has plenty of core features that could have (should have?) been implemented as modules. For example, say. Perl also has lots of core features that offer relatively minimal benefit given a scope of "all Perl programs" (e.g. getprotoent).
Based on my CPAN search, I think qk() would get pretty heavy use.
I'd like for simple slices to be allowed within declarations, so you could say things like
our @is_color{qw(red blue green)} = (1) x 3;
I agree that being able to declare-and-initialize a hash using the slice syntax would be helpful. However, in this case I'd still use
our %is_color = map { $_ => 1 } qw(red blue green);
(or qk(), of course) instead of
our @is_color{qw(red blue green)} = (1) x 3;
because with the latter, you have to change two things every time you add or remove something from list: the actual list, and the literal 3;
I read Mark-Jason Dominus's post you linked to, and I will admit I am not a "Perl internals" guy. And maybe, per that same post, this is tunnel vision on my part since I would use this feature a lot. But I hope not, based on my CPAN search.
https://metacpan.org/pod/Syntax::Feature::Qwa