Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Control Structures

by artist (Parson)
on May 09, 2005 at 21:21 UTC ( #455340=perlmeditation: print w/replies, xml ) Need Help??

While programming, we work with control structures defined in particular language. In Perl, we have if, while, foreach, do, unless etc... Even though we can mimic, we are missing some originals like 'case'-'when', 'yield', etc. by design in Perl 5. I believe that, we think within the boundaries of control structures. With more control structures, we can become more flexible in programming. After all, TMTOWTDI is what make Perl interesting.

What type of new control structures you would like to have in Perl? What would be your consideration for the design of control structures ? Would that make your task easier? Can you mimic them with current version (5.6+)?


Replies are listed 'Best First'.
Re: Control Structures
by TimToady (Parson) on May 10, 2005 at 02:43 UTC
    I'm always vaguely amused when people start trying to redesign Perl 6 from scratch...

    What type of new control structures you would like to have in Perl?

    I would like to have the new control structures that are discussed in Synopsis 4.

    What would be your consideration for the design of control structures ?

    That would be Apocalypse 4.

    Would that make your task easier?

    Hmm, well, so far it seems to have made my task considerably harder... :-)

    Can you mimic them with current version (5.6+)?

    Yes, most of them can be emulated with Damian's various Perl6::* modules, but they'll work much better when they're built in.

      I'm curious where you think the ideas for perl 6 came from? Any chance they might have picked up on the desires of the perl community and used those? Since perl 6 is no where near set in stone I would think now is the perfect time to voice opinions on what might be usefull in future versions of perl.

      Modified language ever so slightly. Larry wall or not, just because there are plans for Perl6 doesn't mean that opening up discusion on control structures and what people would like changed is "amuzing." Maybe that statment wasn't meant as a slight (maybe i shouldn't respond in the middle of the night ;) ). Either way I think that Larry of all people should be supporting any type of community discussion on the matter even if its not in the "proper" channels mentioned in the following replies. Either way its just my two cents.

      Eric Hodges
        TimToedady is Larry Wall. Perl6 is the community rewrite of perl. There have been many RFCs and much discussion, and Perl6 has already been designed. artist is late to the game, and hasn't bothered to do an ounce of research.
Re: Control Structures
by Joost (Canon) on May 09, 2005 at 21:52 UTC
      I'm not sure how much perl6 will allow you to mess with the parser, but source-filters in perl5 suck.

      As I understand it Perl 6 will give you pretty much complete access to the parser. The Perl 6 grammar will be represented with the new regexp system so you can tweak it to your hearts content.

      Actually I think this might be a first for Perl. While other languages like Lisp and Pop-11 allow you to write your own syntax with macros, etc. I can't recall a language that has an explicit representation of the grammar that you can tweak directly at runtime. Nice.

Re: Control Structures
by eric256 (Parson) on May 09, 2005 at 23:14 UTC

    A switch or case statment. Although after programming for a while with out them I seem to have lost the urge to use them and can't come up with a case where i needed them! ;)

    A clearer control default values like my $var ||= $default without the 0 value gotcha.

    Makeing elsif elseif...just because it continues to make me look twice at my code.

    It is hard to streach outside the box though, I find my mind jumping to available solutions instead of wishing for changes.

    Eric Hodges
      A clearer control default values like my $var ||= $default without the 0 value gotcha.

      The newest development versions of perl have a "defined-or" operator //, so the //= operator does that. There's even a patch you can apply to perl-5.8 to have that operator. The only sad part is that we'll have to wait a while until it's widely available for general use.

      The advantage to case statements is that each element is not mutually exclusive, as you have with a single if/elsif/else structure

      So, in pseudocode, you might do something like:

      switch (value) { case 'needs_slight_cleaning': &clean_up_values; case 'good value': &do_whatever_you_need_to; break; case 'totally unrelated'; &do_something_else; break; default: &do_some_default_thing; }

      Note how there is no 'break' between the first two cases, so something that matches the first case will run '&clean_up_values' and '&do_whatever_you_need_to';

      It's not an absolutely necessary control structure, but there are some times when it sure does come in handy. (most times when you have a giant if/elseif/else tree, where you're repeating large blocks of it).

      Update: I forgot to answer the questions as they were asked:

      What type of new control structures you would like to have in Perl?
      I'm fine with the current structures
      What would be your consideration for the design of control structures ?
      Anything that helps me get my work done
      Would that make your task easier?
      switch/case statements, for the reason stated above.
      Can you mimic them with current version (5.6+)?
      Yes. With goto.
        You certainly don't need goto to do switch/case. One auxiliary function will give you the functionality you outlined above, and more. (I've included the ability to check regexen; you could do other things if you were inclined.) The syntax reads pretty well, IMO. Writing these things has become something of a hobby for me. :-)

        Caution: Contents may have been coded under pressure.
Re: Control Structures
by tlm (Prior) on May 09, 2005 at 23:52 UTC

    In my code I often find structures of the form

    while ( 1 ) { # yadda yadda last if some_condition(); # yadda yadda }
    which suggests to me that a better, more general design for a loop would put the test between "pre" and "post" blocks:
    loop { # begin of enclosing block # pre-test code } while ( some_condition() ) { # post-test code; }
    Both pre- and post-blocks get executed repeatedly until the test in the middle fails, at which point control passes to immediately after the post-block. The pre-block (together with the loop keyword) would be optional; omitting it results in the standard while-loop. Likewise, the post-block is optional; omitting produces the standard do-while loop.

    But Perl already gives a pretty close approximation for the attractive low price of an end-of-block redo:

    { # pre-code last if some_condition(); # post-code redo; }

    the lowliest monk

      Or you could use the "for-do-do" loop:
      for(; do { # pre-test code some_condition() }; do { post-code }) {}
      which is really just a while-do:
      while (do { # pre-test code some_condition() }) { # post-code }
      This started out as a joke, but that construct is sensible enough not to be a joke. Bare-block-redo is still better, though.

      Caution: Contents may have been coded under pressure.

      Dijkstra aparently coined this: "looping n and a half times" (or the "loop and a half" as some people have shortened it) back in 1973. Knuth mentioned it as one of the main reasons for using goto in his 1974 "Structured Programming with go to Statements" (which does not seem to be available online unless you are an ACM member) ...

      A: S; if B then goto Z fi; T; goto A; Z:

      He mentions several alternatives that he finds inferor to the goto version for various reasons, and credits Ole-JOhan Dahl as proposing a syntax he really like -- which frankly I think kicks ass, and plan on writting as a P6 macro (I think macro is the right word)...

      loop; S while !B: T; repeat;
      or in the more practical perlish way...
      loop { S; } while (!B) { T; }

        On the off-chance that this does work, the code is so tricky you probably shouldn't even think about using it in real life.

        I'm willing to accept the possibility that:

        S while (!B): T;

        is pronounced: "use T as the continue block for a while() loop that controls S." If it doesn't, then it seems like the colon should be a semicolon, and you're looping over S until B returns TRUE, then calling T.. which a loop-and-a-half doesn't do.

        I'm also willing to consider the possibility that the whole statement is somehow the conditional that controls the loop() statement, and could thus be written like so:

        loop (; S while (!B): T ; repeat) { }

        but I'm damned if I can see how the conditional in the while() loop drops through to control the loop() statement, and I have no idea why you'd want to call repeat as the loop() statement's continuation routine.

        The best way I know to express the loop-and-a-half is:

        while (1) { S; # make a calculation last if (B); # drop out when the result is right T; # adjust the parameters for another try }

        which is, at very least, easier to read. The fact that expressing the idea requires a last statement goes right to the heart of the fight that made Dijkstra's Use of Go To Considered Harmful so infamous.

        According to the key theory of structured programming (I don't reall who did the proof and don't have my references with me right now), you can write any program with nested function calls (where each function has a single entry point and a single exit point), while() loops, and if() statements. The problem is that some forms of logic are extremely ugly when written using only those tools.

        We've solved those aesthetic problems by adding things like the next, last and redo statements, continue blocks, else and elsif() statements, the capacity to exit a function from more than one place, and so on. Technically, those tools exceed the 'minimal effective system' necessary to write programs, but they don't violate the spirit of structured programming, and they make the code a heck of a lot easier to read.

        Er, perhaps I'm badly missunderstanding you, but wouldn't:
        loop;  S  while !B:  T;  repeat;
        Translate fairly literally in to:
        { S while !B; T; redo; }
        In which case I don't understand your "practical perlish way".
Re: Control Structures
by mstone (Deacon) on May 10, 2005 at 19:38 UTC

    Well, speaking with tongue partially in cheek, I suppose Fredekin gates and generalized Toffoli gates would be nice. The code versions of each look roughly like so:

    sub fredekin { my ($a,$b,$c) = @_; return (($a) ? ($a,$c,$b) : ($a,$b,$c)); } sub toffoli { my @list = @_; my $tail = pop @list; my $state = 0; for my $i (@list) { $state++ if ($i); } if ($state == @list) { $tail = ($tail) ? 0 : 1; } return ((@list, $tail)); }

    The Fredekin gate takes a three-item list as input and returns a three-item list as output. If the first item is FALSE, the return list is identical to the input list. If the first item is TRUE, the last two items of the return list are swapped.

    The Toffoli gate is more general, and does roughly the opposite. It takes an N-item list as input, and returns an N-item list as output. If any item from 0 to N-1 is FALSE, the output list is indentical to the input list. If all the items from 0 to N-1 are TRUE, the logic value of the final item is flipped.. TRUE is replaced by FALSE, or FALSE is replaced by TRUE.

    Both gates are universal, meaning you can build a complete Turing machine using nothing but arrays of either kind of gate. The Fredekin gate also maintains perfect energy balance, meaning it never changes the number of TRUE or FALSE statements. The Toffoli gate can change the number of TRUE and FALSE statements, which makes it slightly more powerful than the Fredekin gate (i.e.: you'll need fewer gates and scratch inputs to solve a problem with Toffoli gates), but as a consequence, the Toffoli gate runs 'hotter'. Changing in the number of TRUE and FALSE statements involves energy transfer, and ultimately, that energy will be released as heat.

    Both can serve as basic building blocks for quantum and/or reversible computation.

    And as an aside, the Fredekin gate is actually kind of useful in everyday programming. It's good for situations where you need to choose between two options based on the value of a third item. And situations like that show up frequently when you try to arrange code for logical correctness.

Re: Control Structures
by perrin (Chancellor) on May 10, 2005 at 01:24 UTC
    I'd like to see fewer control structures in perl. Both unless() and the trailing if() are constant sources of bugs in the code I see.

      Really? Thats an interesting observation, I always found both forms useful for minimizing noise and making flow control clearer. I find unless in either form (block or modifier) somewhat more inclined to be difficult, but the modifier if, and even unless coupled with loop control statements like next and last to be very useful in enhancing readability.

      When I see something like

      next if $condition;

      it allows me to think "condition is prohibited past this line", i dont have to worry that later on the block ends and !$condition is still possible. For instance in the following code if at a later I point I forget the importance of the next, or possible restructure code so it doesnt fire sometimes something bad could happen after the block and I might not even notice it until debugging. The statement form doesnt allow such complexity so it sort of red-flags itself as being "mess with this and the code below has to be completely reconsidered."

      if ($condition) { ... next; } # !$condition stuff can happen here

        Trailing if() leads to this incredibly hard to debug problem, which I have seen in production code. So, you could say that trailing if() would be fine except that perl has bugs with it, but that doesn't really help the current situation.
      Both unless() and the trailing if() are constant sources of bugs in the code I see.

      Depends in my experience. With newbie developers or with people new to Perl it can cause problems. Appropriately used I find they produce clearer and more readable code.

      Personally I'd rather train up the developer than simplify the language.

        New developers will create bugs regardless of language. It is a gift, without which they would never learn.

        The problem with unless() is just that people get lost in double negatives and precedence. The problem with trailing if() is this.
      You should stop looking at buggy code :)
        You should stop looking at buggy code :)

        Bah. If only I had the option :-/

      As others have posted, I don't find problems with this either. Although as soon as these sort of statements start becoming complicated, you're much better off using a proper if () {} block.

      I find trailing if/unless can greatly improve the readability of code (which hopefully helps to reduce bugs).
Re: Control Structures
by jonadab (Parson) on May 10, 2005 at 11:14 UTC
    I'm looking forward to given/when, and lazy evaluation may prove useful, but for the most part control flow is not really the area where Perl5 lacks.
Re: Control Structures
by Eimi Metamorphoumai (Deacon) on May 10, 2005 at 16:33 UTC
    It's hard to look outside the box, almost by definition. So what follows aren't really suggestions for syntax, they're more questions for "what's a good idiom for?"

    The "loop-and-a-half" problem has already been mentioned, but I still haven't seen anything that looks really good (though the redo aproach may be the cleanest).

    Something I've frequently found myself doing is wanting a three-way control structure for greater than, less than, and equal to. Sure there are ways to do it, but none of them really feel clean.

    given fork { case $_ < 0 { #error } case $_ > 0 { #parent } default { #child } }
    if ((my $pid = fork()) < 0){ #error } elsif ($pid > 0) { #parent } else { $child }
    But it's the sort of thing that feels like it could be simpler. A better example might be checking two values against each other, where it feels unnatural to need a separate variable or compare them multiple times.
    if (get_the_boundary_x() < the_user_provided_x()){ draw_color("green"); elsif (get_the_boundary_x() == the_user_provided_x()){ draw_color("blue"); else { draw_color("red"); }
    (Yes, I know that can be done with draw_color(qw( blue red green )[get_the_boundary_x() <=> the_user_provided_x()], but that's just ugly and confusing).

    merlyn's looking for a good idiom: return this if this is true shows another question without a (really good, or at least completely natural) answer. The suggested if (my $ret = thatroutine()){return $ret} feels ugly due to the synthetic variable $ret.

      First, fork doesn't return -1 on error. It returns undef. Perhaps you were thinking of something else. (I hope that's not the way you've been coding your fork checking all this time!)

      Second, as one odd way to solve your problem, you could execute one of a few code blocks:

      ( sub { print "A is less than B" }, sub { print "A is equal to B" }, sub { print "A is greater than B"} )[($a <=> $b) + 1)]->();
      OK, maybe not much better, but at least you don't compute it more than once.

      -- Randal L. Schwartz, Perl hacker
      Be sure to read my standard disclaimer if this is a reply.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://455340]
Approved by blokhead
Front-paged by ghenry
[hippo]: Understood. I'll have to go through the code and see if it's doing anything fancy with ties, dual-vars or non-scalars. In the end, it's probably a bug though.
[Corion]: Aaah - you should be able to do this with overload, but I would hit somebody really hard if they constructed objects that are true but the empty string, and you not knowing about the domain knowledge where this makes sense
[Eily]: you could tie a variable into not having the same value each time, if you like to make people who try to debug your code facepalm
[Corion]: perl -wle 'package o; use overload q("") => sub {warn "str"; ""}, bool => sub{warn "bool"; 1}; package main; my $o={}; bless $o => o; print "Yay" if ($o && !length($o))'
[Corion]: But people writing such code should document the objects they construct and why it makes sense for an object to be invisible as string while being true in a boolean context
[hippo]: That's equal parts clever and horrendous.
[Eily]: the overload version wouldn't return true with "$x" && !length $x though, I guess
[hippo]: The more I look at this code, the more $x is a plain old scalar and the more this condition will never be true. I'm calling it a bug at this point.

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (14)
As of 2017-07-27 13:38 GMT
Find Nodes?
    Voting Booth?
    I came, I saw, I ...

    Results (413 votes). Check out past polls.