Beefy Boxes and Bandwidth Generously Provided by pair Networks
Pathologically Eclectic Rubbish Lister
 
PerlMonks  

block-based programming...

by bronto (Priest)
on Apr 22, 2004 at 10:23 UTC ( [id://347291]=perlmeditation: print w/replies, xml ) Need Help??

Preface: I don't claim neither that I like this kind of programming, nor that I hate it. I would just like to read some opinions

I am in charge of writing a small simple application that we will use to update our DNS zones. No pressure, no timelines, no hurry, so I have some time to think about how to make it :-)

The application scheme is quite simple:

  • A - call the script, specifying which domain you want to update
  • B - do a cvs update of maps
  • C - edit the direct map
  • D - do a named-checkzone on the map file, go back to the C step if it fails parsing the map
  • E - show a cvs diff; if the user is not happy with the changes go back to C
  • F - same as C for the reverse map
  • G - same as D for the reverse map
  • H - same as E for the reverse map
  • J - do a cvs commit
  • K - do a cvs update on the DNS server
  • L - reload maps on the DNS server

Does it make sense to build this kind of application in a block-based way, like:

# cvs update, croak if it fails ... C: { # call the editor, croak if it fails ... redo C if $named_checkzone_failed ; # show cvs diff redo C unless $user_is_happy ; } # ...and so on

instead of, e.g., creating subroutines and making decisions on which to run? Does the block-based approach make sense at all? Which pro's and con's do you see for each approach?

Thanks in advance for your opinions

Update after Abigail's post: The pro I see in the block-based style is that it mimics the states graph (???wrong???) of the program; for example, it could be made clear from the source using labeled blocks and redos that once one enters the "EDIT_ZONE" block he could restart it any times he likes, but once he gets out, he'll never come back. On the con side, it seems to me that the source get more structured than it needs this way, and the approach seems unfeasible when the complexity of the states graph grows.

Ciao!
--bronto


The very nature of Perl to be like natural language--inconsistant and full of dwim and special cases--makes it impossible to know it all without simply memorizing the documentation (which is not complete or totally correct anyway).
--John M. Dlugosz

Replies are listed 'Best First'.
Re: block-based programming...
by dragonchild (Archbishop) on Apr 22, 2004 at 11:48 UTC
    I'm of two (or more) minds about this. Firstly, this is not some radical new style. This is a form of imperative programming without subroutines, similar to some older languages (like Apple Basic and the like). You are, essentially, programming with goto.

    Now, you did say that this is a small program. In a way, so long as your stuff is well-named and well laid out, it almost doesn't matter what way you program this. Heck, your program seems to be simple enough that, if the hooks were there, you could program this in Parrot! (I don't reccomend it, but it could be interesting ...)

    Now, you talk about a state machine. If you think about this being a state machine and you don't use CPAN code, you're (probably) reinventing the wheel. The standard distribution for handling state machines in Perl is POE*. It's a bit of a learning curve, but I would definitely suggest looking there if you consider this a state machine.

    As for my feelings on block-driven programming in general ... I guess I would say that it's a neat idea, so long as your codebase remains very very small. This doesn't scale in any meaningful way. *shrugs*

    *CGI::Application can be considered as another state machine distribution, but it is both domain-specific to web applications and doesn't really have built-in support.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      If you think about this being a state machine and you don't use CPAN code, you're (probably) reinventing the wheel. The standard distribution for handling state machines in Perl is POE*.
      But it's big and clunky and doesn't read like normal Perl code. For a simple FSM, there is absolutely no reason to use POE, as POE tends to make your app into a "POE application", transmogrifing it. Reinventing your own will result in something a lot more concise. POE is cool, but it is *much* more than just a state machine, and I really doubt everytime someone recommends it that they have actually used it for that purpose. Implementation of basic state machines WITHOUT goto's is basic CS1, and those that use goto's to do this are doing really shoddy programming.

      Gotos (and their P.C. cousins "abused exceptions") must die. They lead to poorly maintainable code that is both hard to read and trace.

      A simple state machine without gotos (pardon the syntax errors):

      $state = 'foo'; $running = 1; while($running) { if ($state eq 'foo') { # do stuff $nextstate = 'bar'; } elsif ($state eq 'bar') { # do stuff $nextstate = 'baz'; } elsif ($state eq 'baz') { # do stuff $running = 0; } $state = $nextstate; }
        Gotos (and their P.C. cousins "abused exceptions") must die. They lead to poorly maintainable code that is both hard to read and trace.
        There's no reason they lead to poorly maintainable code. They only lead to poorly maintainable code if they are misused. But it isn't too difficult to write spaghetti code using OO and no gotos - but that isn't a reason to say that OO must die. Perhaps you should read Knuth's paper "Structured programming with Go Tos", where he shows that if used appropriately, use of gotos is fine.

        Quoting from my own work, part of a C program I modified last week:

        __close1: if (close (soc) < 0) { if (errno == EINTR) { goto __close1; } perror ("close"); exit (1); }
        I don't think this is less maintainable than writing a loop.

        Abigail

Re: block-based programming...
by Abigail-II (Bishop) on Apr 22, 2004 at 10:52 UTC
    Which pro's and con's do you see for each approach?
    Why don't you tell us what you think the pros are? I can't form any judgement based on a single, not worked out example. If you want to harvest useful opinions, you have to give us something to work on. Give a description of what "block programming" is supposed to be/do. Are you by any chance trying to re-implement make?

    Abigail

Re: block-based programming...
by fruiture (Curate) on Apr 22, 2004 at 12:27 UTC

    You'll see the downsides of that approach as soon as you reach blocks F, G and H, which do the "same as .. with ..", which clearly looks like you want to have block C as a function, taking cartein arguments, being called from certain points of your main program with different parameters. I mean, you could write your block-based program and then refactor out all the redundant stuff that you find. Personally, i'd try to find the obvious things before and not write block-stuff at all.

    Perhaps writing a subroutine for each block may help. While you write each sub, try to make it as flexible as possible. Finally you only need a main script, keeping the main program's state variables and calling the subs accordng to the overall logic.

    You might want to think of an OO model for your data, not only refactoring to subs, but to classes and methods. The main program does then not just keep state "variables", but objects, which you might want to reuse somehwere else...

    I'm sure you won't need named blocks in the end, only "if", "while" and subroutines. No redo, but tail recursion.

    --
    http://fruiture.de
      I agree with your post up until the point about tail recursion. Tail recursion is *easily* eliminated by reducing the recursion to a simple loop. This is much friendlier on your stack, and much more efficient! Other forms of recursion are harder to eliminate, but the refactoring of tail recursion is well known.

      That being said, tail recursion often serves as as decent way to design an algorithm originally, but it should be eliminated when you get the chance.

      In his case, assume the program is a "C" program implemented as a state machine that will run for years. Tail recursion would chew up a tremendous amount of stack and lead to program termination. Refactored as a simple while loop, the program would run forever.

      This is one (not so rare) example where the reality of how a computer works oversteps the theory of basic "computer science". What appears to be logically equivalent is not once you put it on real hardware.

      edit: darn, it looks like I'm turning into a CS professor now... run away! run away!

        This is one (not so rare) example where the reality of how a computer works oversteps the theory of basic "computer science". What appears to be logically equivalent is not once you put it on real hardware.
        What has "real hardware" to do with it? All you need is a compiler that's able to detect tail recursion, and eliminate it, instead of putting the burden on the programmer. Yes, such compilers exist. Ironically, they are more common in environments that are more concerned with the 'theory of basic "computer science"' than in environments that use 'real hardware'.

        Abigail

Re: block-based programming...
by tilly (Archbishop) on Apr 22, 2004 at 21:35 UTC
    I have used a similar block-style before, so I'm going to say that it is sometimes justified. (Look for redo in the Carp::Heavy shipped in Perl 5.8.) I'd only think that it is justified if you have many conditions that you need to satisfy to leave the loop (my situation there) or else factor the code so that the odd flow of control is compactly presented in an understandable way.

    In your case that would mean that you should make A, B, C, etc into functions (hopefully well-named ones) and then write the flow of control in a small block. like this:
    EDIT_MAP: { attempt_edit($map); redo EDIT_MAP unless can_parse($map); redo EDIT_MAP unless user_confirms($map); attempt_edit($reverse_map); redo EDIT_MAP unless can_parse($reverse_map); redo EDIT_MAP unless user_confirms($reverse_map); } commit_edit($map, $reverse_map);
    Now the key flow issues are put somewhere where the redo logic is very easy to understand and is not intertwined with the grungy details of the steps. It would be easier, for instance, for someone who didn't know the code or technique to look at this and figure out how to add a "give up" option on those redos, or to have F-H by default fail back to F rather than C.

    (Yes, my experience suggests that if someone wants a change in this script, then they are quite likely to want to make them to the flow of control.)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://347291]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (4)
As of 2024-03-28 20:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found