RE: On elegant coding...
by footpad (Abbot) on Oct 12, 2000 at 20:09 UTC
|
The novice hesitates and then cautiously responds...
Many of your thoughts strike chords with my personal development style (paraphrasing):
- Planning the implementation using pseudocode comments before writing implementation code
- Minimizing globals
- Not using classes when old-fashioned structured programming will suffice
- Using white space to visually separate sections of code
- Reviewing it after the fact to make sure it's right
However, do you not hold a rather unique position in the community? You are held to a higher standard because your code is often seen as a standard to follow. As a leader, a columnist, and a mentor, you are one of the sources for "how it should be done."
Yes, your code should be elegant as time, energy, and editors will allow.
Yes, our code should as elegant as possible. But, does not the ability for elegance come with experience? I don't expect my daughter's watercolors to match the quality or the simplicity of master artists. So, too, the quality of my perl will not match yours for a period of years (if ever).
As possible is the key. As a leader and mentor, please don't forget that some do not yet have skills that match yours. They have the potential, just not the experience.
In the short time I've been slouching about, I've found perlmonks to be a community interested in helping folks improve their knowledge and skills, as opposed to other communities where knowledge and ideas are hoarded for some questionable tactical advantage. I personally appreciate that very much.
I would ask, though, that "elegance" be gently taught. After all, there are many that need to do their work very quickly, without the luxury of extended research, review, or analysis. A regrettable portion of our positions as programmers is that we're given too little time and resources to do our work properly.
This is, as you well know, why there are so many hacks in "finished" software. One quote that comes to mind, though I confess I cannot properly attribute it, is, "Software is never finished; it's just abandoned."
Also, elegance, to some degree is in the eye of the beholder. For example, I recognize that the standard is to use this:
if (cache_is_good()) {
show_cache_and_ext("current");
}
However, you will almost always find my code expresses it as:
if ( cacheIsGood() )
{
showCacheAndExit( "current" );
}
Does that make my code less elegant? To you, perhaps...however, I find it:
- Clearly denotes the block.
- Is easier to debug when I've forgotten enclosing braces or mismatched my parentheses.
- Is easier to read when viewed or printed with proportional spaced fonts.
- Clearly separates my function calls from built-ins.
That's just my personal preference. It works for me and I do not claim it works for anyone else.
I would hope that in your quest to improve the elegance of our code, you would recognize that and not vote me down because I format my tokens differently that you do.
In my opinion, good programmers continue to look for ways to improve their skills, even though previous efforts cannot (or will not) be revisited. They also recognize that "coding standards" should be guidelines and not straight-jackets. In my opinion, that's where the artistry comes in. | [reply] |
|
|
Seems to me that you're looking to heavily on the brushstrokes
and not enough on the theme when referring to the blocks.
(Disclaimer: This is not a rant on either footpad or
merlyn). What I note from merlyn is that one does not
simply reach a point where everything is elegant. This is
either comforting or a complete disappointment. I'm not sure
which, yet. I appreciate the sentiment; that much is clear.
footpad, the mistake I notice in your post is that you
give a higher credence to merlyn. Possibly more than other
monks, but he is, after all, 'just another perl hacker'. I
guess what I'm saying is that I agree with the overall concept
being discussed here: there is a good quality about treating
code as an art. (Here's the potential fire, but I've tried
my hardest to put the bucket of water right in front of you)
Just because someone is considered a "master" in their field
of art does not mean that their work is good. I think merlyn
is a good artist, but it is because what he does is important
to himself, not that it is important to you.
I don't have a cool quote from anyone famous, but I'm
certain that someone probably said something like this in the
past (Probably Howard Roark): You can't create art for
anyone but yourself. If it happens that your work reaches
others, then they can only take from it what they want to
take from it. This will be dependent on their personal path
that has led to this moment. It will be theirs only, and
even as the artist, you will not have any claim to ownership
of that which they get from your work.
ALL HAIL BRAK!!!
| [reply] |
|
|
PsychoSpunk,
Interesting. I wasn't necessarily speaking completely for myself, but through things I've observed. Merlyn is placed higher than many and for good reason. Also, his RN is extremely recognizable with respect to perl. He's written books; he's a columnist in a few different magazines, and he gives much to to community. How can people not listen when he speaks?
I wasn't disagreeing with him either. Code should as elegant as possible. It's easier for him (and perhaps even more important) for him to write the best code possible.
In the other community I'm involved with, I'm very careful to not judge the validity of a poster's question or the elegance of the solution they've reached. As I mentioned in an earlier post, I have found that I learn much from those who have less experience than I do.
And please don't misunderstand my intention; I appreciate everything he's given to the community and respect and appreciate those contributions highly. And I do pay attention...
What made me pause was the possibility that some would take his post as suggesting that elegance should be the only consideration for code. (FTR, I honestly don't think that's what he was actually trying to say.) I wanted to highlight the idea for discussion....just in case...
I hope no one thought I was trying to slam merlyn; I honestly wasn't. I respect him too much to even consider doing so. if that's how that post came across, I apologize.
-- f
| [reply] |
|
|
|
|
|
|
|
|
|
I was just scanning some of my code to see how I do it and I notice that if the block contains a single statement, I almost always code like this:
showCacheAndExit("current") if cacheIsGood();
That way it reads the same way I would say it in English. But that's just me and I am FAR from considering myself an experienced Perl dude.
-It's snowing in Mammoth!!! | [reply] [d/l] |
RE: On elegant coding...
by Trimbach (Curate) on Oct 12, 2000 at 19:26 UTC
|
In my experience with my own modest Perl
skills I have gradually moved from the "hack-hack-hack-does it work? Yeah!"
style of coding to a more aesthetic appreciation for
the artistic aspects programming.
When I'm working on a project I try to figure out an elegant
solution to the problem, that is, a solution that requires the least
amount of coding, but the implementation itself isn't always very elegant.
Now, I certainly know when I've written something sloppy, and although most the time
I have to go with it due to time constraints, but it doesn't keep me from feeling
dirty somehow. I certainly appreciate
programming elegance even if I'm not capable of it.
I aspire for the day when my programs become elegant both in form and in
function, because I think Merlyn's right: the more artistic a
program is the easier it is to maintain, the easier it is to read,
the better it works.
But for now I am content to color with crayons. I will leave the oil paints
to the Gurus.
Gary Blackburn
Trained Killer | [reply] |
RE: On elegant coding...
by runrig (Abbot) on Oct 12, 2000 at 22:04 UTC
|
I enjoy at least trying to redo these things (whether or not the result is more or less maintainable/elegant is another question). My effort (which I know is probably something like what you already tried): {
## main loop
show_cache_and_exit("current") if cache_is_good();
unless (cache_is_stale()) {
## cache is dead
update_cache(),show_cache_and_exit("current") if i_am_the_writer();
## we cannot do anything about a bad cache, so retry
close_cache();
sleep 5;
redo;
}
## If I'm not the writer then show old cache
show_cache_and_exit("stale") unless i_am_the_writer();
## If I cannot fork it's up to me
update_cache(),show_cache_and_exit("current") unless i_can_fork();
show_cache_and_exit("stale") if i_am_the_parent();
## child does:
be_a_child();
update_cache();
exit 0;
}
| [reply] [d/l] |
|
|
| [reply] |
|
|
Excessive !?!?! There's 3 if's and 3 unless's. I'd say they're perfectly
balanced :-) Really, though, I was just trying to get rid of all the
nested blocks, which is sometimes easier to do (in this case anyway, I believe) only after
seeing the code WITH all the nested blocks.
Update:oops, meant to reply to merlyn, and replied to myself instead :-)
| [reply] |
RE (tilly) 1: On elegant coding...
by tilly (Archbishop) on Oct 12, 2000 at 21:50 UTC
|
First of all I agree with the various thoughts and points you make. So let me just comment on the example.
I see what you mean. Aesthetically I don't like the exit condition being woven through the logic, what happens if some day you want to have this cache updating be part of a longer-running script? Also reading the logic through I saw a lot of combinations of actions being taken, but it was not at all obvious why some of them were. And the nesting is pretty darned deep.
I used an indicator variable to do the actions and restructured. Comments?
{
## main loop
my $cache = 'current'; # Hope for the best
if (not cache_is_good()) {
$cache = 'stale'; # Changed expectations
if (i_am_the_writer()) {
if (cache_is_stale()) {
unless (start_background_update()) {
$cache = 'current'; # Gotta do it foreground
}
}
else {
$cache = 'current'; # Have nothing to show, gotta do it
}
}
elsif(not cache_is_stale()) {
# Stuck, gotta retry
close_cache();
sleep 5;
redo;
}
update_cache() if $cache eq 'current'; # Make it so
}
show_cache($cache);
}
sub start_background_update {
# NOTE: Uses implicit returns.
if (i_can_fork()) {
unless (i_am_the_parent()) {
be_a_child();
update_cache();
exit 0;
}
}
}
UPDATE
Yeah, I know. I was using implicit returns without a comment. At the time it made sense, but that is the kind of thing I rethink and mention shortly after... | [reply] [d/l] |
|
|
I'm not a real big fan of state variables, and in fact, for this example,
I had tossed around doing some status checks similar to what you've done,
but rejected it after a couple of false starts.
The problem with state variables is that they introduce some additional
coupling:
- What if the variable gets set to a value other than the legal values (or not initialized)? You could branch more often or less often than you think.
- What if you refactor and now need a finer-grain distinction (stale vs real stale)?
How do you track down which values to check, or does it now need some disjunctive
tests?
- Tracing the program flow now also requires a data simulation as well.
Yeah, again, just reporting on where I've been burned. But I think I've got
enough scars to not just be a guy in a diner on this one. You know, the guy
at the end of the counter who has an opinion on everything even though
he's not been there? {grin}
-- Randal L. Schwartz, Perl hacker
| [reply] |
|
|
I am not the greatest fan of state variables either. But the state variable always was initialized, information on what values were legal appeared fewer times within fewer lines of code than the original, and I was only resorting to it to remove something else that I disliked even more. And I note that it is an idea that you tossed around as well. :-)
To tell the truth, were this my problem with my stuff I would look at the logic and reconsider whether I need to be fancy and fork. Since my stuff likes to run in crons and long-running jobs, I would be able to say "no" and get drastic simplifications. Down to "until it is safe to read, become writer and be sure it is fixed, once safe to read then read".
But there is an obvious solution that doesn't involve state, and that is to make your loop into a real function, and then return out of the function rather than call a function that exits. Combine with runrig's organizational skills... :-)
As for the diner, yeah, that hits home. And you know it. So let me explain.
It has to do with my learning style. I found out many years and subjects ago that I learn best by going out and finding out everything that I can, trying to integrate that into a solid understanding, and then articulating my understanding in the presence of people who know better than I do. If they correct, then analyze the corrections, integrate *that* into my understanding. Wash, rinse, and repeat until satisfied.
Which boils down to one thing. I am a lot like that guy at the end of the counter except in one huge detail. I learn!
Besides which, I have been more places than you might think...
| [reply] |
RE: On elegant coding...
by princepawn (Parson) on Oct 12, 2000 at 21:10 UTC
|
Well, this code has one inconsistency. In one case, you aggregate an action with an exit, while in another you dont. E.g.,
show_cache_and_exit($STATE);
aggregates two actions, while
update_cache();
exit 0;
would have been written as
update_cache_and_exit(0)
if it were consistent with the aggregation methodology used for show_cache()
Alternatively, and in the favor of fine granularity,
show_cache_and_exit($STATE);
Could be written as
show_cache();
exit($STATE);
| [reply] [d/l] [select] |
|
|
Yeah, I played around a little with factoring. You're right, I could have combined those.
But the inelegance was that there are two different reasons we could be updating
the cache in the parent... one because we couldn't fork, and the other because
the cache was so old we dared not show a stale cache. And I was trying to figure
out how to have both of those fall out to the same place in the program without
freaking out with state variables or contorted conditions, and just didn't get there.
-- Randal L. Schwartz, Perl hacker
| [reply] |
RE: On elegant coding...
by princepawn (Parson) on Oct 12, 2000 at 21:21 UTC
|
Being a former logic programming daemon, let's take a completely different take on writing this. Among other things, this different take shows that you should use constants instead of strings to avoid spelling errors.
## main loop
if (cache_is_good()) {
show_cache_and_exit("current");
}
# INSTEAD BECOMES
cache(GOOD) && show_cache && exit('current');
if (cache_is_stale()) {
if (i_am_the_writer()) {
if (i_can_fork()) {
if (i_am_the_parent()) {
show_cache_and_exit("stale");
}
## child does:
be_a_child();
update_cache();
exit 0;
}
## cannot fork, so it's up to me
update_cache();
show_cache_and_exit("current");
}
## I'm not the writer, so show old cache
show_cache_and_exit("stale");
}
#### BECOMES
(cache(BAD) && sleep(5) && redo)
||
(cache(STALE) &&
(!i_am_writer && show_cache && exit(STALE))
||
(
i_am_writer &&
(forkable() && (
(parent && show_cache && exit(STALE))
||
(child && update_cache && exit(0)
))
|| (update_cache && exit(CURRENT)
)
Prince "--'s here I come! Where is the preview button for comments? Why is this TEXTAREA so small? I cant see anything?" PAWN | [reply] [d/l] [select] |
|
|
I shun this coding style, because it either implies an additional dependency which
doesn't exist,
or introduces a dependency which shouldn't be there.
If you use
a && b && c
where you mean
if (a) { b; c; }
and b returns false, you're hosed.
Therefore, as an element of "elegance", I don't introduce or require any more
dependencies than I can safely justify.
In practical terms, I'd reject your code during a code review, demanding a rewrite
on the grounds of maintainability.
-- Randal L. Schwartz, Perl hacker | [reply] [d/l] [select] |
|
|
But I think it is perfectly OK to generate expectations of functions and develop code whose logic is dependent on it.
| [reply] |
|
|