Only with an explicit define can you make intent clear and maintain full control. If it were automatic, how would you access a buffer scoped to the procedure?
Consulting in Model-Based Development, Transformation, and Object-Oriented Best Practice http://www.cintegrity.com
This is just a boneheaded design decision on the part of Progress to scope variables to the procedure level. This type of thing leads to scope creep and breeds dangerous non-reentrant functions. Tsk tsk. It will never be fixed, so don't bother with the bug report, because there is way too much ancient Progress code that needs backwards compatibility. I'll increment the Progress fail counter, though, if that makes you feel better.
Someone got up on the wrong side of the bed this morning. What, exactly, is wrong with scoping to the IP or method?
It's harmful due to the fact that it is unexpected behavior, IMO. I've never coded in a language that didn't scope variable declarations to the looping block, so it violates the principle of least surprise, in my (subjective) view. But, then again, I haven't coded in very many older languages so maybe ABL is not a zebra in this regard (although C/C++ doesn't do this...). Of course, it's not a "show stopper" or anything like that. A compiler warning or something wouldn't hurt, I guess, to let you know that the compiler is moving the DEFINE statement. Or even a footnote in the "DEFINE VARIABLE statement" portion of the help manual.
It's annoying because it imposes additional restrictions on the programmer. If I wanted my variable definitions scoped to the procedure block, then I would move the DEFINE statement into the procedure block scope myself. I think there are plenty of times when you have multiple loops in the same procedure block that would benefit from tightly scoped variables. For instance, C and Java both have shorthand for defining your iterator variable within the loop:
//Loop 1for (int i=0; i}//Loop 2 - in the same procedure blockfor (int i=0; i}
My iterator variable, i, only really needs to live inside these loops because that's the only place that its lifetime makes sense. I want the compiler to complain loudly if I try to access i outside of those blocks!
I can see some point to auto-defining variables like that in the looping construct ... but the issue there seems to be more one of auto-definition than the scoping. If one assumes explicit defines ... which has a certain virtue too, then one has a different situation. E.g., in ABL, one might wish to say
do i = 1 to 10:
and have i autodefine and scope, but the price that one pays for that is that there is no explicit definition of i. One certainly couldn't expect to say:
define variable i as integer no-undo.
Since the variable definition is after the use. Moreover, it is in an iterating block and one does want to execute the define in every iteration. So, one needs:
define variable i as integer no-undo.
But, now the definition isn't in the block so how could it be scoped to the block. Whereas:
do i = 1 to 10:
Makes the scope really clear and also opens up the possibility that the scope is one loop or several depending on the need.
The key, really, is factoring code into small procedures or methods instead of having many lines of run on code.
FWIW, I stopped counting when I got to 50 languages in which I had written and that was probably 20 years ago. One thing that has taught me is that any claims of "everybody else does it this way" only apply if one has not experienced many variations. Clearly, there are some convenience short cuts in other languages that are not in ABL, but while convenient, I am not entirely sure they are good things because they make code less clear.
Thomas, Java and C# don't have auto define of variables (like Visual Basic has or had, not 100% about VB.NET because it's a toy and not a language).
for (int i=0; i
is an explicit variable definition, because it's prefixed with the type name. It's the same as
int i = 0;
for (i=0; i
with the difference, that in the first case i is scoped to the loop only, in the second case it's scoped to the method.
In the context of ABL, I would still count this as a form of autodefine since one does not have the full range of options available that one has with a DEFINE statement.
In the context of C# and Java I count that as a full variable definition: type and initial value. You can't add more (arrays are part of the type).
In the ABL, what else would be missing? Probably just NO-UNDO (kind of a sub-type to me), and the no longer that much relevant formatting options.
Something like this would be nice:
DO INT i=1 TO 10:
As a special variable declaration shortcut, kind of like how you can get a LOGICAL short-cut-defined off of the MESSAGE ... UPDATE statement to get user input.
It's better than doing something gross like
DO (DEF VAR INT i NO-UNDO INIT 0) TO 10:
which if anything this just highlights the annoyance of Progress' verbosity compared to other languages.
tamhas wrote:The key, really, is factoring code into small procedures or methods instead of having many lines of run on code.
I don't think that works when it makes more sense to have multiple looping constructs in the same procedure/method block. If you have to wrap every looping construct in a procedure block to get variable scoping working then you'll end up with spaghetti code.
tamhas wrote:FWIW, I stopped counting when I got to 50 languages in which I had written and that was probably 20 years ago. One thing that has taught me is that any claims of "everybody else does it this way" only apply if one has not experienced many variations. Clearly, there are some convenience short cuts in other languages that are not in ABL, but while convenient, I am not entirely sure they are good things because they make code less clear.
Totally agree. Variation between languages is what sets them apart, for better or worse, and makes life interesting. In my opinion, though, ABL made a lot of decisions that fall into the 'worse' category. But I'm sure many on here would disagree. To each his own. My last day coding in ABL is 04/26 though, so I won't be coming on here griping about it anymore.
I'm not sure I agree about the conciseness thing. I think
int i, j, k, l = 5;
is more concise and clear than
DEFINE VARIABLE i AS INTEGER NO-UNDO INITIAL 5.
DEFINE VARIABLE j AS INTEGER NO-UNDO INITIAL 5.
DEFINE VARIABLE k AS INTEGER NO-UNDO INITIAL 5.
DEFINE VARIABLE l AS INTEGER NO-UNDO INITIAL 5.
(My brain has to filter out too much garbage). Also,
is more concise IMO than
ASSIGN i = i + 1.
But again, to each his own.
P.S. Why is there no ABL syntax highlighting option? Pfft.
Whatever ... yes, I would consider NO-UNDO pretty important and the context is ABL, not the shorthand of C# and Java.
But, more to the point, the original and reinforced complain is not about DO i = 1 to 10, but about DO: DEFINE which has migrated to iterating blocks. I submit that REPEAT: DEFINE, FOR EACH: DEFINE, and interating forms of DO... : DEFINE are highly questionable constructs since one doesn't want a variable defined within an interaction unless it is a part of a smaller scope. And a plain DO: DEFINE sets up ambiguities about whether the variable is or is not defined based only on whether the block is executed. I see no good reason for this and it certainly runs counter to the whole structure of defining variables as a part of compilation.
SO, it seems like the only actual valid use case is for the loop variable in DO I = 1 to 10: I have trouble seeing that as a major oversight, especially since there are constructs where one wants to know the value of I outside the loop, e.g., to determine when the loop finished.
I don't get that! In that case you could still define the variable outside the block and get exactly what you want. The ability to scope variables to blocks would not mean that you could no longer scoped them to internal procedures/methods or the whole compile unit.
How would you distinguish the case where you want the value of i outside the loop, e.g., to return which element in a set provided the match?
I strongly disagree about the spaghetti code. Properly written methods in an OO language are often only a few lines long. Encapsulating a logical unit in its own method or IP makes code more readable, not less, as well as making it more maintainable.
And what do you mean no syntax highlighting option? Been one for years and years.
It's the same way how you'd distinguish between a method scoped variable and a class scoped variable. Aren't you one of the biggest fan of naming prefixes on earth? Just one sample.
But very limited scoped variables do not necessarily be marked, typically their definition is just very few lines above where they are used.
tamhas wrote:...since there are constructs where one wants to know the value of I outside the loop, e.g., to determine when the loop finished.
I would say that is stretching it. Most of the time you don't care. In the rare circumstance where you do care, you just define it outside the loop - e.g.
int i;for (i=0; i}//check i here
//check i here
More importantly is that this isn't a big issue to get worked up about, since nothing will be done about it due to backwards compatibility, or if something is done it will be kludgy. Even if syntax like
DO INT i=0 TO 10:
Were supported for scoping i to the loop, other cases are not handled, which is the main complaint. The only backwards-supporting capability would be to add yet another keyword, like
Which would just be yet another keyword to remember to tack onto 99% of your DO blocks, like remembering to put NO-UNDO onto 99% of your variable declarations or NO-LOCK onto 99% of your queries. So, in my opinion, boneheadedDesignDecision == TRUE.
Message was edited by: Abe Voelker to fix the mixing up of iterating/DO block stuff
I should add that if you're checking the iterator's value outside of the loop, you probably aren't using a FOR loop, but an indeterminate construct like WHILE or UNTIL, since you don't know when the loop will exit. So Progress, when/if you fix this, don't forget to tack on the new keyword to those types of loops as well.