Improved autobox-ing. I'm loving it :o)
print (0..9)->grep { $_ > 5 }
->map { $_ * 2 }
->join(' - ');
# prints: 12 - 14 - 16 - 18
Isn’t that nice ? It is now possible with
use autobox::Core;
use PerlX::MethodCallWithBlock;
…
autobox::Core enables to call methods on Perl native types (scalars, lists, hashes, etc). It has the advantage that you can call functions like grep() and map() in the order in which they are executed. But it has the annoying requirement of passing coderefs to map() and grep(), which makes them look ugly:
use autobox::Core;
print (0..9)->grep( sub { $_ > 5 } )
->map( sub { $_ * 2 } )
->joint(' - ');
PerlX::MethodCallWithBlock enables to pass blocks instead of coderefs, as arguments to functions. It’s magic! And it solves autobox’s biggest syntax hindrance (in my opinion).
I love these two modules. I will propose both authors to document the fact that they work well together.
~ ~ ~
Also, autobox::Core enables you to very easily create your own methods for Perl’s native types. For example in this article about “checking that a list contains”, i use it to create a contains() list-method :
do_something if @ingredients->contains('flour');
Now, i’m imagine that people will come up with ideas of methods for List, Hashes, Scalars, etc, that would take a block as an argument.
There’s probably a lot to copy from Ruby, who uses block-passing a lot. I’ve read an example in a book, i’ll share it here if i manage to make it work.
I'm a big fan of PerlX::MethodCallWithBlock. I'd love to see it become part of standard Perl syntax. Another module it works very nicely with is XML::LibXML::NodeList.
Ick. I've never felt the appeal of autobox. If you want to write things in that order, Ruby already exists, and it even lets you use "." instead of "->". It's cool that Perl can be contorted to do this, but I'm glad I never see it used in real code.
I'm with educated_foo. There may be times when autobox is useful, but this sure doesn't look like one of them :-(.
You'd probably like the Perl 6 solution:
However, I'd put all of these options into the category of fun and elegant code that I wouldn't release to production.
Surprisingly Nick, i don't like this Perl6 solution very much. I find it not clear; cryptic.
(^10) is not as self-explanatory as (1..10)
I like self-explanatory code: if i don't have to think or to remember, to understand the code, then that's perfect.
And i don't like the use of * instead of $_ :
it is very confusing, especially when the * sign is also used in the same block.
I managed t answer to Nick, but i don't manage to answer to Ron. It says "Your comment has been received and held for approval by the blog owner."
I thought i was the blog owner?
Ron, do you have examples of when autobox is useful? I'm not trying to prove you wrong or anything; just to learn more about good practices.
One more thought about these constructs ( ->map, ->grep ) :
Naively, i would say that in production code it's better to use "Perl5-ish" code only, so that any Perl programmer immediately understands what you're doing. It's better for maintainability, better for asking for help, better for showing the code to others, because "it's the language that everyone knows and speaks". I agree, that's really important.
But in home-code, code that i use for myself only, it feels (to me) good to express things more freely, with constructs that feel more "natural", that are easier to read.
I think there's something i really enjoy about experimenting and learning about languages.
I imagine that today's odd/unusual constructs could become tomorrow's best practices. And realize that i am probably fooling myself.
Still, i am left with a practical question:
Fortunately, TIMTOWTDI!
**to which extent would you allow yourself to use useful but
new/unusual idioms** in code that you will not be the only one to
read/write ? (for example for a CPAN module that you want other people
to use)
Would you use
Method::Signatures ?
I'm guessing it's more or less "best practice" for code that doesn't
need speed optimizing?
English ?
Scalar::Does ?
or Safe::Isa, instead of ref
boolean ?
It's really not perlish, but sometimes a lot clearer than 1, 0, "" or undef.
invoker ?
I find that it makes my code clearer: less clogged with $self everywhere.
Once you've started it's really hard to stop, and then few people can
actually read your code...
Unless maybe if you write a POD page called "The syntax in this
project is..." which explains which modules are used, how they modify
the syntax, and maybe even when to best use the idioms. It doesn't
sound good for a big project. But maybe acceptable / profitable for
smaller projects?
What are your thoughts on this matter?
~ ~ ~
trying to post this message without the links...
I'm sort of on the educated_foo side here. Terseness is a good attribute for code, but not at the expense of readability or exposure of business rules IMHO. This (exposure of business rules) is why I am not allergic to 'unless'. Sometimes that's the best way to expose the business rule.
I'm a believer in clarity first. So if terseness and succinctness support that goal, then great, however I think some folks like to push Perl syntax to the edge of readability for the sole purpose of terseness (and perhaps to show off a bit).
The beard test. "If the guy in the next cubicle scratches his beard when he looks at it, recode it." ;-)
Do you feel that
@list->map { do_something_with($_) }
is less clear than
map { do_something_with($_) } @list
?
If so, is it only because you are more used to it, or are there any other reason?
In terms of clarity I prefer the first one because i don't have to jump to the end of the line, to know which list is being processed.
Another advantage is that when i chain them, they are in the order in which they are processed. I would agree that chaining them does not always make the code clear, though.
The reason why i would use the second one in production code, is in my answer of May 8 at 1:16 AM: "so that any Perl programmer immediately understands", because "it’s the language that everyone knows and speaks". If someone looks at my code and thinks "why does 'map' not behave as usual?", that's wasted time and energy. And that could make them feel that they are not in control: "the person who wrote this program uses strange idioms, it's not Perl!"
I think that this reason outweights the benefits of the first syntax, especially in production code.
So far, several of you have expressed that the code i posted is not clear, but none of you has explained why. Please help me understand.
I wonder whether it's the syntax that you don't like, or my (crapy) example.
Plenty of people would call the “clear and obvious” Perl 5 solution cryptic. You call the Perl 6 solution cryptic. They are wrong and so are you.
I hired into my job a smart friend who had mostly done Java up to that point, and let him loose on a very complicated piece of Perl code I didn’t have the time to finish nor explain. He had to figure it out almost entirely by himself. He said at first he thought my copious use of
map
andgrep
cryptic and obfuscatory, so he rewrote the code with loops in order to understand it. But little by little he got comfortable with the more functional approach, still trying to understand what I had been doing, and started using them himself. By the end he had written much of the code back to how it was.He started out finding
map
andgrep
cryptic and ended up finding them clarifying the code.Intuition is a matter of recognition.
There are some ineffable measures of obviousness and it’s not all just subjective – but it’s far less straightforward than judging something by whether it looks obvious to you. The real question is how learnable it is, and after being learned, how effortlessly it can be read without slowing down to think.
I’m not closely familiar with Perl 6, but on that count it looks pretty good to me from afar.
ARG!
Unfortunately PerlX::MethodCallWithBlock seems to have a nasty bug:
when i have a compilation error, it changes the line indicated for this error (for example, it will tell me that an error is on line 8, while it's on line 10). Because of this i won't use it. Has anyone any idea how to fix it? I looked into the code but was overwhelmed by Devel::Declare language.
To reproduce the bug:
Aristotle, I agree with most of what you are saying. However, I think that using the same symbol (the * symbol) in the same block to express two very different things, is confusing.
I am referring to this 'map':
(^10).map(* * 2)
I think it's bad practice ( i would be happy to be proven wrong :-) ).
Just to be clear: I am not saying anything negative about Perl6 (i barely know the language). I like its TIMTOWTDI-ism, and most of the syntax i've seen. I just don't like this * symbol in a 'map'.
To answer to the rest of your message, although i agree that we can learn new syntax quite fast, i still think that it is harder for a programmer to have to use a "different Perl language" for every new project they are involved in ("because now there's a better syntax"). For the same reason, i don't use three keyboards (french, english, dvorak) : although my brain can learn to use each of them, having to change constantly makes me much slower. I have tried.
On a broader note, i like code to look as "english" as possible. Parsing english and inferring a meaning from it, is something that my brain masters; i try to turn this into an advantage. For anyone who speaks english, the meaning of this line will be immediately clear :
add_water if @ingredients->contains('flour')
It is clear because it's the right verb. And it is easy to parse because you can read it as english:
"add water if the list of ingredients contains flour"
In comparison, this line is "more" obscure:
add_water if first { $_ eq 'flour' } @ingredients;
"add water if you find a first element in...(going to the end of the line) the ingredients list, whose name is equal to flour"
You can get used to it, that's true. But why not use something clearer?
Because my hands know how to use an english keyboard, that's what i'd rather use. Because my brain knows how to read and understand english, i try to "code in english". Because most Perl programmers code everyday in Perl-core (without all the latest CPAN bells and whistles), it makes sense to code without too much "new syntax".
To sum up, i'm kind of torn in-between those two sides: i agree with you that people can adapt to new (clearer) syntax; and i agree that sticking to "old" syntax has benefits, especially in code that will be read/shared by many people.
Myself, I like to use syntax that i find more readable. If it looks like english, other programmers should be able to understand my code quite easily and fast.
To broaden the discussion even further, as some new syntax become accepted by bigger parts of the community, the language evolves. That's why books like "Perl Best Practices" and "Modern Perl" are written.
I guess, in every community you need people who are more adventurous. They produce less and sometimes they provoke accidents, but slowly they make the community's practice evolve and improve. And you also need people who are more "present oriented" or "pragmatic": they are the ones who will use the tools they know, to sustain the community's needs.
The question that i asked earlier (message of May 8 at 2:58), was:
which of these modules would you say is now accepted as "best practice" by "most" of the community?
(see the list of modules on this earlier message)
I'm aware of the vagueness of the question...
~ ~ ~
Please don't hesitate to bash me, prove me wrong, indicate where my understanding (and experience!) is limited.
Please share your own experience and opinions. I would like to learn to code in a cooperation-friendly style, so that people can participate easily. For example if i ever release code on CPAN. I'm sure other people have similar questions: "can i use this new syntax or will noonw understand me?" (i have this question when i use the CPAN module called 'invoker' for example: it enables to write
$->function;
instead of
$self->function;
I’m confused… you are making this argument in the comments of an entry in which you advocate the use of autobox + PerlX::MethodCallWithBlock.
My purpose here is both to share information and to learn. I said "hey, that's possible! Isn't that cool?", and then i listened.
Some people said that they didn't like this syntax but they didn't explain why. I tried to guess and found some possible reasons; it made sense so i shared my thoughts.
Although i like using "better new" syntax, I might not want to produce code on CPAN that others will struggle to read. I would like to use some amount of "newer" syntax, while making it simple to spot and integrate for others (or for myself in one year).
A possibly good solution could be to document the syntax that i use. I could use Syntax::Collector to create a MyProject::Syntax::Bundle module for each project, and very briefly document the modified syntax there. Plus make it clear both in the doc and code that this is where you want to look first, before diving into the code.
How does this sound?
I hadn't criticized the syntax, but I do have an example of something I'm not fond of and a reason why. I find the use of chained method calls without parens to be jarring.
This is, of course, all a matter of personal taste and I could get used to it, but it's not the way that Perl or most languages work. I can think of an exception: LiveScript; although I'm sure there are more.
Which compiles to JavaScript:
Note that LiveScript only uses functional versions of
map
andfilter
(thinkgrep
), so it doesn't have a corresponding OO example.I would strongly prefer your example if it used parens like so:
Even though you can't natively do that in Perl 5 (or Perl 6 unless you swap the method call operator
->
with.
), it still seems Perlish to me, unlike the original example. This is all subjective though!You said that “(^10) is not as self-explanatory”.
It’s not without understanding the fundamentals of Perl 6, like the operators, which are needed in order to program in the language or understand the code. Just like Perl 5 or Ruby has their own prerequisite fundamentals.
Which makes these examples all equivalent:
This ultimately makes for cleaner code when you’re not constantly doing things like
$a..$b-1
but requires knowledge of operators unavailable to most languages, just like Perl 5 has operators like=~
andx
which aren’t generally known to programmers of other languages.Thanks for your comments Nick :-)
Indeed, these bare blocks after a method call are disconcerting. I didn't even notice that. I guess that's why I compensated by putting them on distinct line: it's the only way i could make it look ok. I'm almost happy enough with the result.
I think it's not only a subjective matter. The fact that it's not seen anywhere else means that our eyes are not used to it, they don't immediately know how to interpret it. The fact that there is a given programming culture is not subjective. It is an objective fact that we have to take into account. Also, to be comfortable to read a language must have a kind of homogeneity: it must obey its own rules. At least some of the basic ones ;-)
In this case I would also prefer what you said:
->grep({ $_ > 5 })
or even just
->grep( $_ > 5 ).
Would that be do-able with Devel::Declare, by replacing parenthesis by braces, for grep?
About the range in Perl6, i only just understood that
^
means "but not the last element". I like it. I think that(0..^10)
is clearer than(^10)
though, because it makes it obvious that it's a range (you don't have to think). On the other hand (^10) is faster to write. Sometimes it's hard to find the boundary between what is subjective and what is not.I like the 'x' operator, because it looks like the multiplication sign.
I'm not in love with =~ in terms of self-explanatory-ness. I like Ruby's
/regex/.match($string)
but in a strange way i still like
$string =~ /regex/
better. Why is that...? I guess i got used to it? Plus, it looks almost like ==, with ~ meaning "kind of equal". And it's intuitive, to put options at the end of the regex, and of the line. In short, i'm unclear about how i feel about this one.
Hey Nick. Ruby has methods who take a block as an argument. An example here with the Tree class (search for "Tree" in the page):
http://nickknowlson.com/blog/2011/12/04/seven-languages-week-1-day-2/
The result is :
a_tree.visit_all { |node| do_something_with(node) }
which will perform an operation on all nodes of the tree. I like it.
I still don't love how it looks. But i prefer writing the list name first, and then the maps and greps in the order in which they are executed.
As a point of information, you mentioned Method::Signatures for code that doesn't need speed optimizing implying that the signatures would slow things down.
Method::Signatures has little overhead itself. With the exception of the type checks, all code is inlined. The required argument checks are little more than things like "@_ > 2". Code like "method foo($this, $that)" is essentially free.
Type checks have the cost of type checks. Mouse type checks for the basic types are exceptionally fast and basically free. Moose type checks are another matter. We're working on making Method::Signatures smarter about whether it uses Moose or Mouse types. We're also working on a "make go fast" switch to disable checks in production.
What is slow is MooseX::Method::Signatures. At last benchmark even a method which takes no arguments is something like 5000 times slower than a normal method call.
Thank you for the explanation and sorry for the mistake, i won't make it again.
I've also read that you might make type checks disable-able, maybe with an environment variable. Thus enabling to develop with type-checks, without slowing down the program in production.