I remember back in a Paradigms of Programming class in uni, our professor who lived by Dijkstra's "Gotos considered harmful" mantra tried to get us all to write programs with a single exit point from each function. I argued that short-circuiting errors before going to the logic made sense both nesting-wise and maintainability-wise, but was ridiculed. I wish I'd known about coding styles in the Linux/BSD kernels back then so I could throw it back at him.
But the coding styles in the Linux kernel usually maintain a single exit point from the function. The format they generally have is:
syscall(params)
{
if (easy check on params fails)
goto done;
allocate data for work
if (further check on params and data fails)
goto cleanup;
perform real work
cleanup:
deallocate data
done:
set return value
}
There's often still a single exit, but it does use gotos to implement simple exceptions. The only times when there are multiple exits is before any data has been allocated and there's no cleanup to be done.
Every time you have to go into a nest, you have to maintain state in your head of what the condition is. So by the time you're nested 9 or 10 levels deep (I've seen this before), I can't remember what all the conditions are that got me there.
I remember reading the single-exit mantra was the result of hardware and speed optimization, rather than readability and maintainability optimization, but I could be remembering wrong.
Whenever I see code that asks for conditions that might vary according to the business rules, I ask myself if there's a better way.
Has anyone had any success with business rule engines? What's your approach to these kind of constraints?
I'm faced with code like this at work all the time (I mostly maintain corporate apps, and get requirements like "law XYZ asks for a minimum of 150.000 oddunits for ABC, we need to change all our apps to support that") Edit: or the far simpler, "we can't sell ABC to people under 21" or "people from Iran"
Alex Papadimoulis of the Daily WTF has an article on this -- it's his opinion that the simple, "bone-headed" approach is the best one for constraints like this, and homegrown business rule engines are dangerous:
IMO Alex doesn't give the best advice sometimes (not to wonder, he sees so much boneheadedness!!!), but he's quite right there... but I don't see the advice -against- business rule engines there.
I totally agree with your own advice about sticking to commercial business rule engines. In fact, I'll give Drools.net a try (any other suggestions?).
If any company -needs- a rules engine, it's the one I work for (there's a "developer" whose main reason for having work is that the business rules change a lot every freaking month)
PD: I like his linking to Yourdon's book :) - and I have a degree in "Information Systems" myself
You know, on considering, I think you're right -- he doesn't advise against rules engines, just the Greenspun's Rule version. (Greenspun's Rule is that "any sufficiently long-running project includes a slow, buggy, and ad-hoc implementation of half of Common Lisp.")
I don't have much experience with rules engines, and I don't know what your budget is like; so I can't really make recommendations. That doesn't stop me from trying, though...
You might be interested in Intersystems' Ensemble. If you're anything like Alex, you'll run screaming for the hills at this point, since it's built on MUMPS (it's an extension of Caché, "postmodern MUMPS," which comes with everything from a web server to a blindingly fast SQL frontend). If you're not -- if you realize that it all compiles down to object code anyways, and what's important is programmer skill and code maintainability, not the presence of curly braces and variable declarations -- you'll keep reading. (Actually, Caché's version of MUMPS has both curly braces and variable declarations, although both are optional.)
Ensemble was designed to translate messages between incompatible healthcare databases, applying business rules in the process. It can be used for more than that; part of the training I had in the product (see below for my full disclosure) was using it to implement a simple loan acceptance protocol.
I think that its "flowchart mode" of execution might be very well suited to frequently-changing business rules. There's a video at http://www.maddash.net/videos/intersystems/ensemble/vehr/ which demonstrates this -- look at 7:15 for the flowchart (each element in it is programmable as necessary), and at 2:15 for the related flow through processing modules when something comes in.
You may be able to get a proof-of-concept demonstration -- see http://www.intersystems.com/ensemble/pilot/index.html for details. Even if not, if this looks interesting, it wouldn't hurt to get in touch with Intersystems Sales.
Full disclosure: I work at Intersystems, but I'm not associated with Sales, and I really should be getting back to work. :)
Thanks for the suggestion. My first instinct is indeed to run for the hills :)
I'm watching the video and the first point is that you should fire the graphic designer :P (I suspect there isn't any, if a programmer did it, at least the screens are very clear - heck, maybe a graphics designer would murk it).
PD: after watching the video it's very clear that it's not what I need at all, but thanks for the suggestion.
Greenspun's rule is something I'm painfully aware of (the main enterprise app I work with has an ad-hoc implementation of... assembler :P ), but thanks for the reminder.
Thanks for the update, and for giving it a chance. I agree that the production values are pretty bad...
Good luck in your search. I've never heard of an ad-hoc implementation of assembly language before, but while we're talking about wanting to head for the hills... :)
Heh.. you don't say... it's built with Sun ONE's UDS FORTE development environment (on a language called TOOL), which stopped being supported years ago.
Fortunately I don't maintain that one very much, but yes, it's "run-for-the-hills" worthy - sadly, the current job offers job security and pays above average for my country.
I'm keeping an eye open, but I haven't found an adequate niche for me yet - my skills don't translate well to a remote environment, I'll probably try my hand at consulting.
This methodology is commonly taught for designing discrete logic in electronics. Make a Karnaugh map to show your true and false states, and pick to implement the minimized logic for true or false cases only.
I much prefer one return per mehtod/func. Of course there are cases where that isn't possible, but I feel that if it were structured the right way, the code would be easier to read.
What do you do if in between if(condition) and return, there are a bunch of actions that modify state outside the function that, if condition is true, you'd never ever want to do?
It's generally pretty easy to do if you write your code in a functional/tail recursive style. Instead of modifying your variables and returning, create new variables and pass them back into your function, in that way whatever you create will have to go back through all the sanity checks. Yes you can get infinite loops this way, but it alerts you to having coded your function incorrectly instead of returning the wrong data.
I'm always using this style because of much better readability.
If I need some shared cleanup code when the function returns, I'm using some of the C++ scope-exit trick. Or in most cases, some of the predefined scope-exit handlers do just what I want. Like:
std::auto_ptr or boost::scoped_ptr
boost::shared_ptr
boost::interprocess::scoped_lock (or I have my own mutex scopedlock class)
18 comments
[ 2.1 ms ] story [ 43.8 ms ] threadEvery time you have to go into a nest, you have to maintain state in your head of what the condition is. So by the time you're nested 9 or 10 levels deep (I've seen this before), I can't remember what all the conditions are that got me there.
I remember reading the single-exit mantra was the result of hardware and speed optimization, rather than readability and maintainability optimization, but I could be remembering wrong.
Has anyone had any success with business rule engines? What's your approach to these kind of constraints?
I'm faced with code like this at work all the time (I mostly maintain corporate apps, and get requirements like "law XYZ asks for a minimum of 150.000 oddunits for ABC, we need to change all our apps to support that") Edit: or the far simpler, "we can't sell ABC to people under 21" or "people from Iran"
http://thedailywtf.com/Articles/Programming-Sucks!-Or-At-Lea...
However, commercial business rule engines might work out better, provided they stick to XML or known formats of I/O in general.
IMO Alex doesn't give the best advice sometimes (not to wonder, he sees so much boneheadedness!!!), but he's quite right there... but I don't see the advice -against- business rule engines there.
I totally agree with your own advice about sticking to commercial business rule engines. In fact, I'll give Drools.net a try (any other suggestions?).
If any company -needs- a rules engine, it's the one I work for (there's a "developer" whose main reason for having work is that the business rules change a lot every freaking month)
PD: I like his linking to Yourdon's book :) - and I have a degree in "Information Systems" myself
I don't have much experience with rules engines, and I don't know what your budget is like; so I can't really make recommendations. That doesn't stop me from trying, though...
You might be interested in Intersystems' Ensemble. If you're anything like Alex, you'll run screaming for the hills at this point, since it's built on MUMPS (it's an extension of Caché, "postmodern MUMPS," which comes with everything from a web server to a blindingly fast SQL frontend). If you're not -- if you realize that it all compiles down to object code anyways, and what's important is programmer skill and code maintainability, not the presence of curly braces and variable declarations -- you'll keep reading. (Actually, Caché's version of MUMPS has both curly braces and variable declarations, although both are optional.)
Ensemble was designed to translate messages between incompatible healthcare databases, applying business rules in the process. It can be used for more than that; part of the training I had in the product (see below for my full disclosure) was using it to implement a simple loan acceptance protocol.
I think that its "flowchart mode" of execution might be very well suited to frequently-changing business rules. There's a video at http://www.maddash.net/videos/intersystems/ensemble/vehr/ which demonstrates this -- look at 7:15 for the flowchart (each element in it is programmable as necessary), and at 2:15 for the related flow through processing modules when something comes in.
You may be able to get a proof-of-concept demonstration -- see http://www.intersystems.com/ensemble/pilot/index.html for details. Even if not, if this looks interesting, it wouldn't hurt to get in touch with Intersystems Sales.
Full disclosure: I work at Intersystems, but I'm not associated with Sales, and I really should be getting back to work. :)
I'm watching the video and the first point is that you should fire the graphic designer :P (I suspect there isn't any, if a programmer did it, at least the screens are very clear - heck, maybe a graphics designer would murk it).
PD: after watching the video it's very clear that it's not what I need at all, but thanks for the suggestion.
Greenspun's rule is something I'm painfully aware of (the main enterprise app I work with has an ad-hoc implementation of... assembler :P ), but thanks for the reminder.
Good luck in your search. I've never heard of an ad-hoc implementation of assembly language before, but while we're talking about wanting to head for the hills... :)
Fortunately I don't maintain that one very much, but yes, it's "run-for-the-hills" worthy - sadly, the current job offers job security and pays above average for my country.
I'm keeping an eye open, but I haven't found an adequate niche for me yet - my skills don't translate well to a remote environment, I'll probably try my hand at consulting.
func(condition,resp){ val = true if(!condition){ return func(!condition,'new'); }) return resp } func(false,'whatever');
If I need some shared cleanup code when the function returns, I'm using some of the C++ scope-exit trick. Or in most cases, some of the predefined scope-exit handlers do just what I want. Like:
Worth another look. I've found this style much more readalbe and maintainable down the road and it prevents the "arrow code" in the link above.