Lazarus

Free Pascal => General => Topic started by: 440bx on August 15, 2018, 06:20:25 am

Title: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 06:20:25 am
Hello,

I've probably been using C too long.  In C, I can break out early in a case statement.  it is often convenient.

I thought this was possible in Pascal too but, FPC is not happy when it finds a "break" in a case. 

Question is: how do I break out early of a case ?

example:
Code: Pascal  [Select][+][-]
  1. case I of
  2.   somevalue :
  3.   begin
  4.     statement ...
  5.     statement...
  6.  
  7.     if someconditionhere then break;  //  FPC is not happy with this
  8.  
  9.     statement ...
  10.     statement....
  11.   end;
  12.  
  13.   <more cases here>
  14. end; { case }
  15.  
Title: Re: how to break out early of a case ?
Post by: guest48180 on August 15, 2018, 06:35:37 am
if (RadioButton.Checked) then
    Exit;
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 07:00:23 am
if (RadioButton.Checked) then
    Exit;

Exit would break out of the enclosing function/procedure, not just the case statement. 
Title: Re: how to break out early of a case ?
Post by: Cyrax on August 15, 2018, 08:12:27 am
There is no need for breaking from case of clause in Pascal. If one case statement is true, then rest aren't executed like they are in C/C++.
Title: Re: how to break out early of a case ?
Post by: Thaddy on August 15, 2018, 08:52:03 am
There's a big difference with a case in Pascal and a case in C:
C behaves like a fall-through (evaluates everything else too, unless break) where Pascal exits when a condition is satisfied.
Hence in C you need to break, whereas in Pascal, you don't need break,
This can be very confusing.
With a fall-through I mean that C evaluates the next cases too, unless break is called.
A pseudo code example would look like this:
Code: Pascal  [Select][+][-]
  1.  case avalue of
  2.  0..9:executeAvalue; // if case 0 to nine is satisfied... Execute something...
  3.  3: Execute third value; // will be executed in C, provided break is [b]not[/b] called,  but not in Pascal. This will never execute in Pascal.
  4. end;

See the point? There is not a one to one relationship between a C (or C++) case and a Pascal case.
In the above case (no break in C) we would solve that with if then without else..
Title: Re: how to break out early of a case ?
Post by: Zoran on August 15, 2018, 12:09:32 pm
Invert the condition and use one more begin-end pair:

Code: Pascal  [Select][+][-]
  1. case I of
  2.   somevalue :
  3.   begin
  4.     statement ...
  5.     statement...
  6.  
  7.     if not someconditionhere then begin // the opossite condition
  8.  
  9.       statement ...
  10.       statement....
  11.     end;
  12.   end;
  13.  
  14.   <more cases here>
  15. end; { case }
  16.  

Or you can put a label under end {case} and use goto.
Title: Re: how to break out early of a case ?
Post by: Zoran on August 15, 2018, 12:20:18 pm
You can also use "repeat - until True" instead of "begin - end":

Code: Pascal  [Select][+][-]
  1. case I of
  2.   somevalue :
  3.   repeat //begin
  4.     statement ...
  5.     statement...
  6.  
  7.     if someconditionhere then break;  //Now, FPC IS happy with this
  8.  
  9.     statement ...
  10.     statement....
  11.   until True; // end;
  12.  
  13.   <more cases here>
  14. end; { case }
  15.  

"repeat - until True" makes a block which executes only once (so, just like simple "begin - end", only now you can use Break.
Yes, a hack. :)
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 01:00:16 pm
Invert the condition and use one more begin-end pair:

Code: Pascal  [Select][+][-]
  1. case I of
  2.   somevalue :
  3.   begin
  4.     statement ...
  5.     statement...
  6.  
  7.     if not someconditionhere then begin // the opossite condition
  8.  
  9.       statement ...
  10.       statement....
  11.     end;
  12.   end;
  13.  
  14.   <more cases here>
  15. end; { case }
  16.  

Or you can put a label under end {case} and use goto.

I ended up using an if statement as the one you suggested  (like most everyone, I'm not fond of goto.)

It's unfortunate there is no way of exiting a case block early.  An if statement solves the problem but, if there are multiple conditions which would cause the case to break early then that would result in the nesting of multiple if statements to implement what is really very simple, linear not hierarchical, logic.

Allowing break in case statements would be a nice and simple addition to the language which would allow the implementation of simpler and cleaner logic in  some cases.

Zoran, thank you for your help.
Title: Re: how to break out early of a case ?
Post by: Cyrax on August 15, 2018, 01:08:58 pm
Again, there is no need for break in Pascal case of clause statements. If one statement is true, the rest are ignored.
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 01:28:50 pm
Again, there is no need for break in Pascal case of clause statements. If one statement is true, the rest are ignored.
Cyrax, yes, that is a fact.  The point is to break out early as the example I posted showed.
Title: Re: how to break out early of a case ?
Post by: taazz on August 15, 2018, 01:35:25 pm
Again, there is no need for break in Pascal case of clause statements. If one statement is true, the rest are ignored.
Cyrax, yes, that is a fact.  The point is to break out early as the example I posted showed.
Code: Pascal  [Select][+][-]
  1.     case I of
  2.       somevalue :
  3.       begin
  4.         statement ...
  5.         statement...
  6.      
  7.         if not someconditionhere then begin  //  FPC is happy with this
  8.      
  9.           statement ...
  10.           statement....
  11.         end;
  12.       end;
  13.      
  14.       <more cases here>
  15.     end; { case }
  16.  
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 02:08:04 pm
Hi Taaz,

Of course, it can be done by wrapping the code fragments in if statements, the downside of that becomes more obvious when there are multiple conditions that would cause the case to be exited early.  In those cases, there will be as many nested ifs as there are conditions that would cause an early break.   That is not as clean as simply breaking out of the case, which keeps the code linear (nested ifs free.)

Being able to use "break" would yield simpler code (no nested ifs.)


Title: Re: how to break out early of a case ?
Post by: lucamar on August 15, 2018, 02:11:17 pm
It's unfortunate there is no way of exiting a case block early.  An if statement solves the problem but, if there are multiple conditions which would cause the case to break early then that would result in the nesting of multiple if statements to implement what is really very simple, linear not hierarchical, logic.

In this simple example inverting the condition is probably the best solution but if you find yourself in a situation in which "there are multiple conditions which would cause the case to break early" I would suggest moving the body of that selection to a separate function or procedure where you can use Exit to your heart's content :) :

Code: Pascal  [Select][+][-]
  1. procedure Whatever;
  2.  
  3.   procedure DoSomething;
  4.   begin
  5.     statement ...
  6.     statement...
  7.  
  8.     if somecondition then
  9.       exit
  10.     if othercondition then begin
  11.       statement ...
  12.       statement ...
  13.       statement ...
  14.       Exit; {Early exit here too!}
  15.     end;
  16.     statement ...
  17.     statement....
  18.   end;
  19. begin
  20. {...}
  21. case I of
  22.   somevalue :
  23.       DoSomething
  24.   <more cases here>
  25. end; { case }
  26. {...}
  27.  
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 02:29:31 pm
In this simple example inverting the condition is probably the best solution but if you find yourself in a situation in which "there are multiple conditions which would cause the case to break early" I would suggest moving the body of that selection to a separate function or procedure where you can use Exit to your heart's content :) :
Absolutely, that's one solution and it is reasonable.  That said, any solution that requires a form of nesting is not as clean and simple as a solution that is fully linear which is what being able to break out early of a case would enable.

Unfortunately, the answer to my question is: there is currently no way to break out early of a case statement.   

I'll keep that in mind when porting C code to Pascal.  The resulting code will not be as simple as in C (which is unusual, usually Pascal code is cleaner than C.)
Title: Re: how to break out early of a case ?
Post by: taazz on August 15, 2018, 03:07:14 pm
In this simple example inverting the condition is probably the best solution but if you find yourself in a situation in which "there are multiple conditions which would cause the case to break early" I would suggest moving the body of that selection to a separate function or procedure where you can use Exit to your heart's content :) :
Absolutely, that's one solution and it is reasonable.  That said, any solution that requires a form of nesting is not as clean and simple as a solution that is fully linear which is what being able to break out early of a case would enable.

Unfortunately, the answer to my question is: there is currently no way to break out early of a case statement.   

I'll keep that in mind when porting C code to Pascal.  The resulting code will not be as simple as in C (which is unusual, usually Pascal code is cleaner than C.)
emulate it.

Code: Pascal  [Select][+][-]
  1. procedure Test(Choice:Byte);
  2. label AfterCase;
  3. begin
  4.   case Choice of
  5.     1..9 : begin
  6.              process;
  7.              process;
  8.              process;
  9.              process;
  10.              process;
  11.              process;
  12.              If SomeCondition then goto AfterCase;
  13.              process;
  14.              process;
  15.              process;
  16.              process;
  17.              process;
  18.              process;
  19.            end;
  20.     10..19 :begin
  21.              process;
  22.              process;
  23.              process;
  24.              process;
  25.              process;
  26.              process;
  27.              process;
  28.              process;
  29.              process;
  30.              If SomeCondition then goto AfterCase;
  31.              process;
  32.              process;
  33.              process;
  34.            end;
  35.     20..29 :begin
  36.              process;
  37.              process;
  38.              process;
  39.              process;
  40.              process;
  41.              process;
  42.            end;
  43.     end;
  44. AfterCase:
  45.   Process;
  46.   Process;
  47.   Process;
  48.   Process;
  49.   Process;
  50.   Process;
  51. end;
  52.  
Title: Re: how to break out early of a case ?
Post by: wp on August 15, 2018, 03:21:57 pm
Why not put the code of that case into a procedure?
Code: Pascal  [Select][+][-]
  1. procedure DoSomething;
  2. begin
  3.   statement1;
  4.   statement2;
  5.   if SomeCondition then exit;
  6.   statement3;
  7.   statement4;
  8. end;
  9.  
  10. begin
  11.   case I of
  12.     somevalue:
  13.       DoSomething;
  14.     ...
  15.     <other cases>
  16.   end;
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 03:54:29 pm
emulate it.

Why not put the code of that case into a procedure?

Absolutely.  Those are both reasonable workarounds.     There is no shortage of ways to implement the logic.  It's just unfortunate that the simplest and cleanest solution, which is to break out of the case, simply isn't available.  Oh well...

Thank you for the suggestions, I appreciate them.

Allowing "break" in a case statement would be a nice addition to the language and, best of all, it isn't hard to implement.  May be a feature the developers might be willing to consider.
Title: Re: how to break out early of a case ?
Post by: taazz on August 15, 2018, 04:21:00 pm
emulate it.

Why not put the code of that case into a procedure?

Absolutely.  Those are both reasonable workarounds.     There is no shortage of ways to implement the logic.  It's just unfortunate that the simplest and cleanest solution, which is to break out of the case, simply isn't available.  Oh well...

Thank you for the suggestions, I appreciate them.

Allowing "break" in a case statement would be a nice addition to the language and, best of all, it isn't hard to implement.  May be a feature the developers might be willing to consider.
No, the choices presented here are as easy and straight forward as a call to break. I see no reason to change break.
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 04:36:12 pm
No, the choices presented here are as easy and straight forward as a call to break. I see no reason to change break.
Those choices are easy and straightforward but, they break linear flow. Those choices could be used in any loop too.  Following your logic, there is no need for "break" in a loop either.

It's about controlling logical flow.  Not having to nest into ifs or call function/procedures when a linear solution is possible.

At this point, it looks like we should probably agree to disagree.

Bottom line is simple: there is no way in Pascal to break out early of a case statement.  When that is needed, which is quite common, C provides a better, simpler, cleaner solution.
Title: Re: how to break out early of a case ?
Post by: taazz on August 15, 2018, 04:41:38 pm
No, the choices presented here are as easy and straight forward as a call to break. I see no reason to change break.
Those choices are easy and straightforward but, they break linear flow. Those choices could be used in any loop too.  Following your logic, there is no need for "break" in a loop either.
true break is just a familiar construct nothing more.
It's about controlling logical flow.  Not having to nest into ifs or call function/procedures when a linear solution is possible.
there are proper flow control constructs no need to extend a hack to a flow control construct.
At this point, it looks like we should probably agree to disagree.
on all accounts I guess.
Bottom line is simple: there is no way in Pascal to break out early of a case statement.  When that is needed, which is quite common, C provides a better, simpler, cleaner solution.
There are. you simple choose to reject them.
Title: Re: how to break out early of a case ?
Post by: engkin on August 15, 2018, 04:47:07 pm
To add to the list, with a sense of humor. Let us use exceptions:
Code: Pascal  [Select][+][-]
  1. type
  2.   ECaseExit=class(Exception)
  3.   end;
  4. ...
  5.   try case I of
  6.     somevalue :
  7.     begin
  8.       statement ...
  9.       statement...
  10.  
  11.       if someconditionhere then raise ECaseExit.Create('Take me out.');
  12.  
  13.       statement ...
  14.       statement....
  15.     end;
  16.  
  17.     <more cases here>
  18.   end; { case } except On ECaseExit do; end;

I think using break inside a case would break some code that was supposed to exit the loop:
Code: Pascal  [Select][+][-]
  1.   while somecondition do
  2.      case I of
  3.        val1: if shouldLeaveLoop then
  4.          Break
  5.        val2: ....
  6.      end;
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 04:52:55 pm
I think using break inside a case would break some code that was supposed to exit the loop:
Code: Pascal  [Select][+][-]
  1.   while somecondition do
  2.      case I of
  3.        val1: if shouldLeaveLoop then
  4.          Break
  5.        val2: ....
  6.      end;
Now, that is a reasonable objection and, your example shows that it is, at least somewhat likely, that it would.   That's unfortunate since it is a good reason not to implement break in case statements.

I got a chuckle out of using an exception to break out of a case. :)  Good one.

Title: Re: how to break out early of a case ?
Post by: ASBzone on August 15, 2018, 09:43:51 pm
Allowing "break" in a case statement would be a nice addition to the language and, best of all, it isn't hard to implement.  May be a feature the developers might be willing to consider.

What did you think of using Repeat/Until in place of Begin/End, which would facilitate Break?
Title: Re: how to break out early of a case ?
Post by: Thaddy on August 15, 2018, 10:00:28 pm
< >:D >:D> I still think he means the break in the case of a C case. Which I already answered...
Title: Re: how to break out early of a case ?
Post by: 440bx on August 15, 2018, 11:57:55 pm
What did you think of using Repeat/Until in place of Begin/End, which would facilitate Break?
I like it.  It's a nice trick to implement a scope that can be broken out of.   Sort of a begin/end pair that can be broken out of.  Can be very handy,  Of course, it needs to be commented as to its real purpose so the programmer who sees it knows why it's being done.

Used correctly, it can also be used to replace some try/finally pairs resulting in much better assembly code.

Thanks.
Title: Re: how to break out early of a case ?
Post by: mangakissa on August 16, 2018, 08:23:01 am
if shouldLeaveLoop is a condition, why not reverse the code
Code: Pascal  [Select][+][-]
  1. while somecondition do
  2.   if shouldLeaveLoop then
  3.   begin
  4.      case I of
  5.        val1: ....
  6.        val2: ....
  7.      end;
  8.   end;
  9.  
I'm agreed with Thaddy (not always :D). 
You said you don't want to use goto, but break is doing the same.
Title: Re: how to break out early of a case ?
Post by: 440bx on August 16, 2018, 08:32:02 am
I'm agreed with Thaddy (not always :D). 
You said you don't want to use goto, but break is doing the same.
There is a big difference between break and a goto.  The destination of a break is preset by where it occurs.  A goto can go anywhere.  Break and goto do the same thing only for people who are better at babbling than programming.
Title: Re: how to break out early of a case ?
Post by: lucamar on August 16, 2018, 02:22:25 pm
Break and goto do the same thing only for people who are better at babbling than programming.

You're wrong, you know. Break, Continue, Exit, etc. are just specialized Gotos; the fact that their destination is preselected doesn't negate this.

One could almost say that they were created to allow you to sneak a "goto" while avoiding that culpable feeling that real gotos produce  :)
Title: Re: how to break out early of a case ?
Post by: 440bx on August 16, 2018, 03:40:26 pm
One could almost say that they were created to allow you to sneak a "goto" while avoiding that culpable feeling that real gotos produce  :)
No.  They were created because they solve the inherent problem of the goto statement, which is that the destination is, unlike with break, continue, etc, not predetermined.

Title: Re: how to break out early of a case ?
Post by: Thaddy on August 16, 2018, 04:47:17 pm
Break and goto do the same thing only for people who are better at babbling than programming.

You're wrong, you know. Break, Continue, Exit, etc. are just specialized Gotos; the fact that their destination is preselected doesn't negate this.

One could almost say that they were created to allow you to sneak a "goto" while avoiding that culpable feeling that real gotos produce  :)
Actually you are a bit incomplete in  your explanation: those goto's are assembler jumps generated by the compiler. As such they have little to do with an actual high level language construct.
But your answer is more correct than that babbler. 8-)
Title: Re: how to break out early of a case ?
Post by: 440bx on August 16, 2018, 04:53:58 pm
Actually you are a bit incomplete in  your explanation: those goto's are assembler jumps generated by the compiler. As such they have little to do with an actual high level language construct.
But your answer is more correct than that babbler. 8-)
The two of you should get a room, share your gotos.
Title: Re: how to break out early of a case ?
Post by: lucamar on August 16, 2018, 05:49:11 pm
The two of you should get a room, share your gotos.

Nah, all my actual GOTOs (from my BASIC days) are rather old and crumbling, although I've got quite a lot of them preserved in (cassette) tapes.  :D

Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 05:58:39 pm
Safe to say there have been _many_ discussions regarding C's fall-through / break functionality for its switch/case statement.

For a long time I have considered this functionality superfluous and I cannot recall one time that I missed it in any project.

However, now that I have come to the point of implementing a switch equivalent in the SharpBASIC compiler, I must admit that I look differently at the fall-through and break features, although IMO 'break' is not necessarily the best statement to leave a switch because it is also strongly associated with 'breaking' loops while a switch doesn't 'loop'. Likewise, 'fall-through' is also not the best term IMO.

So logically, while trying to implement a switch statement with maximum flexibility, I came up with the following solution, which IMO is much more intuitive and safe; unless the programmer specifically specifies a fall-through case with 'also', no fall-through will occur. The keyword 'done' is used to leave the 'when' block. It adds a total of three keywords to control a switch in SharpBASIC: 'also', 'done' and 'other'. Best of all: it works!
Code: Text  [Select][+][-]
  1.   var n: int = 0;
  2.  
  3.   when n
  4.     is 0 do
  5.       n = 1;
  6.     is 10 do
  7.       n = 0;
  8.     also is 1 do         ' "fall-through"
  9.       n = 2;
  10.       done;              ' leave 'when' block
  11.     also is 2 do         ' "fall-through"
  12.       n = 3;
  13.     is other do
  14.       n = -1;
  15.     end;
  16.  
  17.   print n;               ' 2
Title: Re: how to break out early of a case ?
Post by: lucamar on May 02, 2020, 06:30:51 pm
unless the programmer specifically specifies a fall-through case with 'also', no fall-through will occur. The keyword 'done' is used to leave the 'when' block. It adds a total of three keywords to control a switch in SharpBASIC: 'also', 'done' and 'other'. Best of all: it works!
Code: Text  [Select][+][-]
  1.   var n: int = 0;
  2.  
  3.   when n
  4.     is 0 do
  5.       n = 1;
  6.     is 10 do
  7.       n = 0;
  8.     also is 1 do         ' "fall-through"
  9.       n = 2;
  10.       done;              ' leave 'when' block
  11.     also is 2 do         ' "fall-through"
  12.       n = 3;
  13.     is other do
  14.       n = -1;
  15.     end;
  16.  
  17.   print n;               ' 2

The "done" thing (if it is like the break 440bx wanted) is nice, and "is Other" is equivalent to "else" and "otherwise" in Pascal, isn't it? But that "also" ... I mean, that:
Code: Text  [Select][+][-]
  1.     is 10 do
  2.       n = 0;
  3.     also is 1 do         ' "fall-through"
looks a tiny absurd: when "n" is 10, it can't be 1, can it? So, what is it supposed to do?

Granted, you go about changing "n" and that might have some bearing with it but it looks a litle confussing (at least to me).
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 06:40:55 pm
The "done" thing (if it is like the break 440bx wanted) is nice, and "is Other" is equivalent to "else" and "otherwise" in Pascal, isn't it? But that "also" ... I mean, that:
Code: Text  [Select][+][-]
  1.     is 10 do
  2.       n = 0;
  3.     also is 1 do         ' "fall-through"
looks a tiny absurd: when "n" is 10, it can't be 1, can it? So, what is it supposed to do?

Granted, you go about changing "n" and that might have some bearing with it but it looks a litle confussing (at least to me).

I simply inserted the rubbish 'is 10' test to demonstrate that even though a match was found earlier, the 'when' block continues to execute and tests the new definition of n (1) against every 'also is' it finds. So it is not really a "fall through" but more like a "test again" if the programmer specifies a 'also is' test.

C's default fall-through has been a source of many difficult to find bugs. I know because I read about it; I actually never used C myself, not once!

The "done" thing (if it is like the break 440bx wanted) is nice, and "is Other" is equivalent to "else" and "otherwise" in Pascal, isn't it?

Yes, "is other" is equivalent to "else", and it is shorter than "otherwise". Preferably SharpBASIC uses keywords no longer than 6-7 characters.
Title: Re: how to break out early of a case ?
Post by: 440bx on May 02, 2020, 06:41:53 pm
<...> unless the programmer specifically specifies a fall-through case with 'also', no fall-through will occur.
That's the way to do it.  C making the fall-through the default is yet another design atrocity in that language.

A few things for you to consider:
Code: Pascal  [Select][+][-]
  1.   when n
  2.    is     0 do n =  1;
  3.    is    10 do n =  0;  fall;    ' or "fall-through"
  4.    is     1 do n =  2;  fall;
  5.    is     2 do n =  3;
  6.    is other do n = -1;
  7.  end;
  8.  
I'd rather have the fall-through made explicit instead of implicit by the use of a keyword ("also") that doesn't clearly denote it is meant to fall through.

Also (pun intended),  do you really need the "is" ? ... it seems this would be even more readable (possibly also using a single "is" just after the "n" variable being tested):
Code: Pascal  [Select][+][-]
  1.   when n [is]
  2.     0  do n =  1;
  3.    10  do n =  0;  fall;    ' or "fall-through"
  4.     1  do n =  2;  fall;
  5.     2  do n =  3;
  6.  other do n = -1;
  7.  end;
  8.  

Also (again!) one advantage I see to having an explicit "fall" is that the "fall" can then be made conditional, for instance:
Code: Pascal  [Select][+][-]
  1.   when n
  2.     0  do n =  1;
  3.    10  do n =  0;  if <somecondition> then fall;    ' or "fall-through"
  4.     1  do n =  2;  if <somecondition> then fall;
  5.     2  do n =  3;
  6.  other do n = -1;
  7.  end;
  8.  
now falling through has to be explicitly and clearly stated by the use of a keyword that clearly identifies its purpose and in addition to that, falling or not falling is completely under programmer control. I like that part :)

ETA:
Added [is] after the "n" variable to see how the thing would look with it.




Title: Re: how to break out early of a case ?
Post by: lucamar on May 02, 2020, 06:56:41 pm
So it is not really a "fall through" but more like a "test again" if the programmer specifies a 'also is' test.

I think I got it, but just to test my understanding: the equivalent in (current) Pascal would then be someting like,
Code: Pascal  [Select][+][-]
  1.   case n of
  2.   0: n = 1;
  3.   10: begin
  4.       n = 0;
  5.       case n of
  6.       1: n = 2;
  7.       2: n = 3;
  8.       end;
  9.     end;
  10.   else n = -1;
  11.   end;
Is that right?
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 07:07:44 pm
I'd rather have the fall-through made explicit instead of implicit by the use of a keyword ("also") that doesn't clearly denote it is meant to fall through.

As I explained in my previous post, it is not really a fall-through, but more like a 'test again' if 'also' is _explicitly_ included. One can skip several 'is' tests and still "fall through" the next "also is".

Quote
Also (pun intended),  do you really need the "is" ?

Please note that the numbers used in the example are not just treated as ordinal constants. Each test is parsed as an expression. So the 'is' keyword - which is similar to 'else' or 'else if' in an IF block - effectively delimits the previous block. It actually replaces the 'end' keyword that would normally mark the end of a 'do..end' block, similar to Pascal's 'begin..end'. IS eliminates any ambiguity syntax-wise and it also makes it more readable IMO (compare IS with C's CASE).

Quote
Also (again!) one advantage I see to having an explicit "fall" is that the "fall" can then be made conditional

You definitely have a point there. I like the word "fall" because it is short. However, the compiler parsing (nested) IF blocks does not automatically communicate with outer 'when' blocks. If the 'fall' instruction would be inside an IF block then that block must somehow tell the 'when' block what to do. It would require a fall-flag to be passed to every control structure (not just Ifs) that can appear inside a when-block.
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 07:13:15 pm
So it is not really a "fall through" but more like a "test again" if the programmer specifies a 'also is' test.

I think I got it, but just to test my understanding: the equivalent in (current) Pascal would then be someting like,
Code: Pascal  [Select][+][-]
  1.   case n of
  2.   0: n = 1;
  3.   10: begin
  4.       n = 0;
  5.       case n of
  6.       1: n = 2;
  7.       2: n = 3;
  8.       end;
  9.     end;
  10.   else n = -1;
  11.   end;
Is that right?

No because the additional "also is" tests do not depend on the "is 10" test. Because n already had a match in the "is 0' test, the "is 10" test is simply being skipped, not just because n <> 10, but because it is not a "also is" test.
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 07:25:49 pm
A conditional 'fall' can be replaced by an opposite conditional 'leave' for which a jump address is already passed:
Code: Text  [Select][+][-]
  1.   when n
  2.     is 0 do
  3.       n = 1;
  4.     also is 1 do
  5.       n = 2;
  6.       if n == 2 do ' (sic)
  7.         leave;
  8.       end;
  9.     also is 2 do
  10.       n = 3;
  11.     end;

EDIT: 'done' has been replaced by 'leave'.
Title: Re: how to break out early of a case ?
Post by: 440bx on May 02, 2020, 08:09:35 pm
EDIT: 'done' has been replaced by 'leave'.
I like "leave" better.  I think it makes the intention clearer.
Title: Re: how to break out early of a case ?
Post by: JdeHaan on May 02, 2020, 08:12:04 pm
I know the discussion is about breaking out of a case statement, but it got my mind spinning. What about a case expression, like this:

Code: Pascal  [Select][+][-]
  1. function Fibonacci(n: Integer): Integer;
  2. begin
  3.   Result := case n of
  4.     <1.  : 0;
  5.      1, 2: 1;
  6.      else Fibonacci(n-1) + Fibonacci(n-2);
  7. end;
  8.  

'else' should be mandatory, to determine the end of the expression.

Sorry for the off-topic...
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 08:18:47 pm
I know the discussion is about breaking out of a case statement, but it got my mind spinning. What about a case expression, like this:

Code: Pascal  [Select][+][-]
  1. function Fibonacci(n: Integer): Integer;
  2. begin
  3.   Result := case n of
  4.     <1.  : 0;
  5.      1, 2: 1;
  6.      else Fibonacci(n-1) + Fibonacci(n-2);
  7. end;
  8.  

'else' should be mandatory, to determine the end of the expression.

Sorry for the off-topic...

That is another reason why a test should start with a keyword like 'is' or 'case'. It is really similar to the IF structure in this respect. In addition, in the case of SharpBASIC, it allows for syntax like:
Code: Pascal  [Select][+][-]
  1. when n is 1 to 10 do
  2.   print n;
  3. end;
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 10:11:56 pm
Am I correct in assuming that if fall-through is not the default like in C, a break / leave statement is not necessary?
Code: Text  [Select][+][-]
  1.   when n
  2.     is 0 do
  3.       n = 1;
  4.       fall;
  5.     is 1 do
  6.       n = 2;
  7.     is 2 do
  8.       n = 3;
  9.     is 3 do
  10.       n = 4;
  11.     end;
Title: Re: how to break out early of a case ?
Post by: 440bx on May 02, 2020, 10:45:08 pm
fall through _is_ the default in C, which can be a very annoying characteristic.
Title: Re: how to break out early of a case ?
Post by: marcov on May 02, 2020, 10:54:44 pm
Am I correct in assuming that if fall-through is not the default like in C, a break / leave statement is not necessary?
Code: Text  [Select][+][-]
  1.   when n
  2.     is 0 do
  3.       n = 1;
  4.       fall;
  5.     is 1 do
  6.       n = 2;
  7.     is 2 do
  8.       n = 3;
  9.     is 3 do
  10.       n = 4;
  11.     end;

Yeah. Since break is the default. But allowing to change the case condition and then fall through is messy IMHO. Be sure to document that very thoroughly. It will be hard to write optimizers for such case blocks (since the fallthrough and the modification of the case criterium are extra constraints to weigh in that)
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 11:03:43 pm
I just changed the construct by replacing 'leave' with 'fall' and removing 'also':
Code: Text  [Select][+][-]
  1. var n: int = 0;
  2.  
  3. main do
  4.  
  5.   when n
  6.     is 0 do
  7.       n = 1;
  8.       fall;
  9.     is 1 do
  10.       n = 2;
  11.       fall;
  12.     is 2 do
  13.       n = 3;
  14.       fall;
  15.     is 3 do
  16.       n = 4;
  17.     end;
  18.  
  19.   print n;      ' 4
  20.  
  21. end;

But leaving out one 'fall' in a matching case will break the chain and exit the when-block.

But allowing to change the case condition and then fall through is messy IMHO.
I agree. That wouldn't be an option.
Title: Re: how to break out early of a case ?
Post by: marcov on May 02, 2020, 11:13:58 pm
I would leave out fallthrough entirely. Not worth the effort, mostly theoretical cases only.
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 11:17:03 pm
I would leave out fallthrough entirely. Not worth the effort, mostly theoretical cases only.

I actually have one routine in the compiler where it would come in handy. Generally, most of the time fall-through isn't needed, but in some cases it can prevent code duplication, or one would have to resort to loop constructs, which isn't ideal.

In any case, the current implementation works. And the explicit 'fall' statement makes the switch-block much less bug prone. Not easy to overlook anyway.
Title: Re: how to break out early of a case ?
Post by: munair on May 02, 2020, 11:30:58 pm
fall through _is_ the default in C, which can be a very annoying characteristic.

Misunderstanding. I meant to say that if fall-through is not the default... (I know in C it is the default).
Title: Re: how to break out early of a case ?
Post by: 440bx on May 03, 2020, 12:02:05 am
Code: Text  [Select][+][-]
  1. var n: int = 0;
  2.  
  3. main do
  4.  
  5.   when n
  6.     is 0 do
  7.       n = 1;
  8.       fall;
  9.     is 1 do
  10.       n = 2;
  11.       fall;
  12.     is 2 do
  13.       n = 3;
  14.       fall;
  15.     is 3 do
  16.       n = 4;
  17.     end;
  18.  
  19.   print n;      ' 4
  20.  
  21. end;
That looks good.
Title: Re: how to break out early of a case ?
Post by: Thaddy on May 03, 2020, 07:00:40 am
fall through _is_ the default in C, which can be a very annoying characteristic.
I would leave out fallthrough entirely. Not worth the effort, mostly theoretical cases only.
I disagree here. We used to use that a lot in C libraries where some of the options needed preparatory setup before any option later in the switch could be executed, example: partial information need to be completed first. E.g. when a ticker was not yet in the cache, so its static information needed to be retrieved first before processing trade information.
I seem to remember I once suggested to use continue for that, so you get basically kind of a reverse syntax to C, but introducing at least the option to fall-through.
It was refused, not only as a feature, but also because it was not needed if the case labels would allow overlap anyway, shame on me  :-[ . (I was thinking about re-labeling, or case labels as true sets)
I still think that the mutual exclusiveness for case labels is a limitation rather than a feature.
Although similar solutions can be written such a feature is not only notational candy, but a clear grouping of related code. It enhances readability. {$modeswitch OverlappedCaseLabels}?
Title: Re: how to break out early of a case ?
Post by: munair on May 03, 2020, 09:49:15 am
Here is a practical example that could be part of a compiler to check a literal against a type. I currently use similar code but not as efficient due to the lack of fall-through functionality.

Code: Text  [Select][+][-]
  1.   // suppose type = tuint and literal = "0"
  2.  
  3.   if litype == litint do  
  4.     when val(literal)
  5.       is -128 to 0 do
  6.         when type is tint8, tint16, tint32, tint do
  7.           return true;
  8.         end;
  9.         fall;
  10.       is -32768 to 0 do
  11.         when tid is tint16, tint32, tint do
  12.           return true;
  13.         end;
  14.         fall;
  15.       is -2147483648 to 0 do
  16.         when type is tint32, tint do
  17.           return true;
  18.         end;
  19.         fall;
  20.       is 0 to 127 do
  21.         when type is tint8, tuint8, tint16, tuint16, tint32, tuint32, tint, tuint do
  22.           return true;
  23.         end;
  24.       // ..
  25.     end;
  26.   end;

Without fall-through the compiler would raise a type mismatch because it would bail-out with the first case that matches "0".
Title: Re: how to break out early of a case ?
Post by: BrunoK on May 03, 2020, 10:11:58 pm
How is all that code / discussion related to freepascal / object pascal ?

Title: Re: how to break out early of a case ?
Post by: munair on May 04, 2020, 08:41:46 am
How is all that code / discussion related to freepascal / object pascal ?
It has been a feature request more than once.
Title: Re: how to break out early of a case ?
Post by: BrunoK on May 04, 2020, 01:48:54 pm
Frankly, what's the problem with :
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. var
  3.   n: integer = 0;
  4. begin
  5.   repeat
  6.     case n of
  7.       0: n := 1;
  8.       1: n := 2;
  9.       2: n := 3;
  10.       3: begin
  11.         n := 4;
  12.         break;
  13.       end;
  14.     end;
  15.   until False;
  16.   writeln(n); // 4
  17.   // or
  18.   n := 0;
  19.   if n=0 then n := 1;
  20.   if n=1 then n := 2;
  21.   if n=2 then n := 3;
  22.   if n=3 then n := 4;
  23.   writeln(n); // 4
  24.   readln;
  25. end.
Title: Re: how to break out early of a case ?
Post by: Thaddy on May 04, 2020, 02:39:39 pm
The first is inefficient, the second can written better with sets.
Title: Re: how to break out early of a case ?
Post by: BrunoK on May 04, 2020, 02:50:45 pm
The first is inefficient, the second can written better with sets.
Ok for the first, except it addresses breaking out early.

Publish working code for the second that matches the n : integer or keep out.
Title: Re: how to break out early of a case ?
Post by: munair on May 04, 2020, 02:55:51 pm
Yes, like Thaddy said, a loop is inefficient. It requires a conditional jump and a full case walk-over each time, whereas a fall instruction is just one unconditional jump. In addition, a fall can be made conditional:
Code: Text  [Select][+][-]
  1. var n: int = 0;
  2.  
  3. main
  4. do
  5.   when true is 1 > n do  
  6.     when n
  7.       is 0 do
  8.         n = 1;
  9.         if n > 0 do
  10.           fall;
  11.         end;
  12.       is 1 do
  13.         n = 2;
  14.     end;
  15.   end;
  16. end;

EDIT: Indeed, sets can somewhat replace case ranges. In SharpBASIC a when/case block is meant to fulfill both IF and IN constructs.
Title: Re: how to break out early of a case ?
Post by: BrunoK on May 04, 2020, 03:29:52 pm
Yes, like Thaddy said, a loop is inefficient. It requires a conditional jump and a full case walk-over each time, whereas a fall instruction is just one unconditional jump. I addition, each fall can be made conditional:
Code: Text  [Select][+][-]
  1. var n: int = 0;
  2.  
  3. main
  4. do
  5.   when true is 1 > n do  
  6.     when n
  7.       is 0 do
  8.         n = 1;
  9.         if n > 0 do
  10.           fall;
  11.         end;
  12.       is 1 do
  13.         n = 2;
  14.     end;
  15.   end;
  16. end;
Please, can you comment what this bit of code is supposed to do or where documentation can be found on such instructions.
Title: Re: how to break out early of a case ?
Post by: munair on May 04, 2020, 03:44:24 pm
Please, can you comment what this bit of code is supposed to do or where documentation can be found on such instructions.

This code is just a simple example of fall-through. I used it as a test to see if the fall instruction works in SharpBASIC. I started its compiler project about half a year ago. The compiler is far from finished so there is no documentation published yet. But you can follow the latest development on the website sharpbasic.com.

My presence on this forum among other things is to exchange ideas. Pascal as a programming language is great to use as example, but it has its flaws, which I try to avoid in SharpBASIC.
Title: Re: how to break out early of a case ?
Post by: BrunoK on May 04, 2020, 04:07:31 pm
Code: Pascal  [Select][+][-]
  1.  when true is 1 > n do  
Just explain what this line means please.
Title: Re: how to break out early of a case ?
Post by: munair on May 04, 2020, 05:05:46 pm
Code: Pascal  [Select][+][-]
  1.  when true is 1 > n do  
Just explain what this line means please.

It means:

Code: Pascal  [Select][+][-]
  1. if n < 1 then

In SharpBASIC one would usually write:

Code: Pascal  [Select][+][-]
  1. if n < 1 do

As I said earlier, the when-switch should fulfill both Ifs and INs for most variable types, including boolean and string. In simple cases like above IF is preferred, but for range checks I prefer:

Code: Text  [Select][+][-]
  1. when n is 1 to 10 do
  2. // ..
  3. end;

which produces (for 32 bits):

Code: ASM  [Select][+][-]
  1. ; when n is <expr> to <expr> do  
  2.         movsx   eax, byte [_C5]
  3.         push    eax
  4.         movsx   eax, byte [_C4]
  5.         push    eax
  6. ; load n
  7.         mov     eax, dword [_I26]
  8.         pop     edx
  9.         cmp     eax, edx
  10.         jg      ._L1
  11.         pop     edx
  12.         cmp     eax, edx
  13.         jl      ._L1
  14. ._L2:
  15.         ; ...
  16. ._L1:
  17.   ; ...

instead of old school

Code: Text  [Select][+][-]
  1. if n >= 1 and n <= 10 do
  2. // ..
  3. end;

which produces:

Code: ASM  [Select][+][-]
  1. ; load n
  2.         mov     eax, dword [_I26]
  3.         push    eax
  4.         movsx   eax, byte [_C3]
  5.         pop     edx
  6.         cmp     edx, eax
  7.         cmovge  eax, [_sb_true]
  8.         cmovl   eax, [_sb_false]
  9.         push    eax
  10. ; load n
  11.         mov     eax, dword [_I26]
  12.         push    eax
  13.         movsx   eax, byte [_C4]
  14.         pop     edx
  15.         cmp     edx, eax
  16.         cmovle  eax, [_sb_true]
  17.         cmovg   eax, [_sb_false]
  18.         pop     edx
  19.         and     eax, edx
  20. ; convert to boolean
  21.         call    _sb_cbool
  22.  
  23.         test    eax, -1
  24.         je      ._L0
  25.   ; ...
  26. ._L0:
  27.   ; ...
Title: Re: how to break out early of a case ?
Post by: munair on May 04, 2020, 05:06:14 pm
While n < 1 do.
Rpn
IF
Title: Re: how to break out early of a case ?
Post by: munair on November 23, 2021, 10:35:50 am
Code: Text  [Select][+][-]
  1. var n: int = 0;
  2.  
  3. main do
  4.  
  5.   when n
  6.     is 0 do
  7.       n = 1;
  8.       fall;
  9.     is 1 do
  10.       n = 2;
  11.       fall;
  12.     is 2 do
  13.       n = 3;
  14.       fall;
  15.     is 3 do
  16.       n = 4;
  17.     end;
  18.  
  19.   print n;      ' 4
  20.  
  21. end;
That looks good.

Documentation and development stages are now on a dedicated forum (https://sharpbasic.com/forum). I thank everyone here on the FP forum for their input and ideas.
Title: Re: how to break out early of a case ?
Post by: 440bx on November 23, 2021, 04:58:47 pm
I thank everyone here on the FP forum for their input and ideas.
And thank you for implementing ideas that are mostly the removal of a wrinkle/deficiency in an existing concept that has proven useful.

I'm totally overwhelmed with things to do as it is but, I'll definitely check SharpBasic out.
Title: Re: how to break out early of a case ?
Post by: munair on November 27, 2021, 01:04:21 pm
Reading the history of FPC again and its struggles to be TP and Delphi compatible, after some 5 months effectively working on the SharpBASIC compiler (SBC), I realize how constraining it must be to model a new compiler after an existing language. Honestly, I admire the FPC and also FBC (FreeBASIC) developers for honouring the restrictions/limitations and also the ambiguities of the original language for maximum compatibility. It certainly wouldn't be my cup of tea! I say this as a frequent FPC and FBC user; even a trivial thing such as declaring and initializing multiple variables can be hard/impossible to implement:

Code: Pascal  [Select][+][-]
  1. var a,b,c:integer = 0; // error


See here one of the many reasons for SBC. It stems from a desire coming from years of experience with FBC and FPC and could be considered sort of an amalgamation of the two. Using Crenshaw's approach, i.e. without producing a ST, development of the compiler so far has been prosperous, considering I haven't been able to work on it for one-and-a-half year. Here's an example with pointers: https://sharpbasic.com/forum/viewtopic.php?p=27#p27 (https://sharpbasic.com/forum/viewtopic.php?p=27#p27), which tells you that the Crenshaw model, isn't necessarily for toy compilers.  ;)
Title: Re: how to break out early of a case ?
Post by: 440bx on November 27, 2021, 01:33:18 pm
years of experience with FBC and FPC and could be considered sort of an amalgamation of the two.
Curious... what programming language are you using to write the SharpBasic compiler ?

The other question I have is, do you have plans to making it opensource ?
Title: Re: how to break out early of a case ?
Post by: marcov on November 27, 2021, 01:49:32 pm
Reading the history of FPC again and its struggles to be TP and Delphi compatible, after some 5 months effectively working on the SharpBASIC compiler (SBC), I realize how constraining it must be to model a new compiler after an existing language

Well, if you phrase it as "struggles", the mind seems to be already made up before you get to the end of the sentence :-)

I think it is more a difference in goals. Making a widely used suitable for production compiler is simply hard and iterative.  There is also a significant difference in general between a one person team, and a multi person team. With multiple people there are also multiple opinions, directions etc.

On the other hand, compatibility also provides an Occam's razor to test random ideas and alternate directions with. Often it is also very clear which feature to tackle next (from the compatibility list) and that is very rewarding since that often makes a whole new class of applications and reusing existing code possible.  I can still remember how I excited I felt in the beta period before 2.0 when more and more Delphi code ran without significant modifications

Specially in the early days this keeps language experimentation and the associated discussions to a minimum, and keep your focus on a certain target (like self-compiling/bootstrapping the compiler)

Quote
Honestly, I admire the FPC and also FBC (FreeBASIC) developers for honouring the restrictions/limitations and also the ambiguities of the original language for maximum compatibility. It certainly wouldn't be my cup of tea! I say this as a frequent FPC and FBC user; even a trivial thing such as declaring and initializing multiple variables can be hard/impossible to implement:

If such micro syntax is the biggest problem in a language, you'd be very happy. Keep in mind that the Pascal parsing model was ingrained into the fabric of the project. Problems with foreign language constructs not matching the model might have been considered a small or even negligible sacrifice.

Anyway don't fall for the micro syntax trap, it takes more to make a good language than simply implementing a superset of all shorthands of all languages.

In the end what attracts users is visible ability/proof to complete certain tasks, like sizeable programs in the project's target domain.  It is not simply a matter of bullet lists of micro syntax comparisons.
Title: Re: how to break out early of a case ?
Post by: munair on November 27, 2021, 02:23:12 pm
Well, if you phrase it as "struggles", the mind seems to be already made up before you get to the end of the sentence :-)
Wikipedia:
Quote
The principal problems were that adding processors meant rewriting the code generator, and that the register allocation was based on the principle of always keeping three free registers between building blocks, which was inflexible and difficult to maintain.

For these reasons, the 1.1.x series branched off from the 1.0.x main branch in December 1999. At first, changes were mostly clean-ups and rewrite-redesigns to all parts of the compiler. The code generator and register allocator were also rewritten. Any remaining missing Delphi compatibility was added.

I think it is more a difference in goals. Making a widely used suitable for production compiler is simply hard and iterative.  There is also a significant difference in general between a one person team, and a multi person team. With multiple people there are also multiple opinions, directions etc.
I agree that it is a long way to a compiler successfully targeting multiple platforms. If I read correctly, initial development of the FPC compiler 1993-1995 was also done by one person and the compiler took off after Florian Klämpfl released it. A firm codebase is essential, otherwise, with different opinions, a compiler may actually never be born.

Quote
Honestly, I admire the FPC and also FBC (FreeBASIC) developers for honouring the restrictions/limitations and also the ambiguities of the original language for maximum compatibility. It certainly wouldn't be my cup of tea! I say this as a frequent FPC and FBC user; even a trivial thing such as declaring and initializing multiple variables can be hard/impossible to implement:

If such micro syntax is the biggest problem in a language, you'd be very happy. Keep in mind that the Pascal parsing model was ingrained into the fabric of the project. Foreign constructs not matching the model might not have been considered a small or even negligible sacrifice.

Anyway don't fall for the micro syntax trap, it takes more to make a good language than simply implementing a superset of all shorthands of all languages.

This is not about deliberate micro-syntax (although less verbosity might be a goal), but rather about flexibility. I've seen feature request both for FPC and FBC that appear difficult to implement because of the compiler's design. When starting development of a new compiler, implementing such features right at the start is much easier. SBC is not the first project started due to a new language design wish.

In the end what attracts users is visible ability/proof to complete certain tasks, like sizeable programs in the project's target domain.  It is not simply a matter of bullet lists of micro syntax comparisons.
Agreed.
Title: Re: how to break out early of a case ?
Post by: munair on November 27, 2021, 02:28:55 pm
years of experience with FBC and FPC and could be considered sort of an amalgamation of the two.
Curious... what programming language are you using to write the SharpBasic compiler ?

The other question I have is, do you have plans to making it opensource ?

Initial/current development is in FreeBASIC, simply because I'm a bit more comfortable with the language (I did extensive programming with QuickBASIC in the 80-90s). But I could have chosen FPC as well. It doesn't matter all that much because eventually, when SBC is up to it, I want it to be self-hosting, which is also a great way to extensively test the compiler.

However, key routines such as copying strings, allocating memory etc are all in assembly and part of the sys library. For this reason SBC supported inline ASM early, because it makes it convenient to write these routines. For example:
Code: [Select]
sub _sb_copystr(p_src: ptr, p_dest: ptr, length: uint)
do
  asm do
    push     esi
    push     edi
    push     eax
    push     ecx

    mov      esi, [ebp + 16]              ; source
    mov      edi, [ebp + 12]              ; destination
    mov      ecx, [ebp + 8]               ; length

    cmp      ecx, 0                       ; larger than 0 ?
    jle      .out

    .copy:
    mov      al,[esi]                     ; from source
    mov      [edi],al                     ; to destination
    inc      esi                          ; increment pointers
    inc      edi
    dec      ecx                          ; decrement counter

    cmp      ecx, 0                       ; done?
    jne      .copy

    .out:
    pop      ecx
    pop      eax
    pop      edi
    pop      esi
  end;
end;

I haven't made up my mind yet about the compiler being open-source. If successful, it might be as an example of a compiler without producing a ST.
Title: Re: how to break out early of a case ?
Post by: marcov on November 27, 2021, 03:31:52 pm
Well, if you phrase it as "struggles", the mind seems to be already made up before you get to the end of the sentence :-)
Wikipedia:
Quote
The principal problems were that adding processors meant rewriting the code generator, and that the register allocation was based on the principle of always keeping three free registers between building blocks, which was inflexible and difficult to maintain.

For these reasons, the 1.1.x series branched off from the 1.0.x main branch in December 1999. At first, changes were mostly clean-ups and rewrite-redesigns to all parts of the compiler. The code generator and register allocator were also rewritten. Any remaining missing Delphi compatibility was added.

Most of this was compiler architectural and backend rewrite, not due to language compatibility issues. The old backend was hard to stabilize after changes (either bugfixes, new features, new optimizations etc), since there were certain assumptions in the initial codegenerator (a minimum of scratch registers) that were constantly violated with changing codepaths. One could argue the 1.0.x series was only meta-stable.

Due to the length of this rewriting (1999-2003, and another 2  (2003-2005) to bring it to production), the newer language features were implemented in parallel, quite some only in the later stages (the betas, 2003-2005) of the development, after the main rewrite.

I agree that it is a long way to a compiler successfully targeting multiple platforms. If I read correctly, initial development of the FPC compiler 1993-1995 was also done by one person and the compiler took off after Florian Klämpfl released it. A firm codebase is essential, otherwise, with different opinions, a compiler may actually never be born.

All true.

This is not about deliberate micro-syntax (although less verbosity might be a goal), but rather about flexibility.

This is. You more or less explain it here:

I've seen feature request both for FPC and FBC that appear difficult to implement because of the compiler's design. When starting development of a new compiler, implementing such features right at the start is much easier. SBC is not the first project started due to a new language design wish.

It is expanding existing Pascal syntax with unplanned features stolen from other languages, and they were never on the table on the start when a good portion of those languages didn't even exist. Even C++ was still on shaky ground in 93-94.

Agreed.

So what domain do you think Sharpbasic will excel in? What is the big picture?
Title: Re: how to break out early of a case ?
Post by: munair on November 27, 2021, 03:54:18 pm
So what domain do you think Sharpbasic will excel in? What is the big picture?
I haven't given it too much thought yet. The first goal is a stable compiler that generates at least 32 and 64 bit programs for Linux and Windows (GUI bindings might be added at that point). Possibly, programmers that see a use for the compiler on other architectures might want to add the specific code generation to the compiler. There's also the question of OOP with classes or only interfaces as Go has. But SBC will certainly support structures/types (NASM supports them natively). Right now I'm working on strings, then arrays, then types, all needed to make the compiler self-hosting.
Title: Re: how to break out early of a case ?
Post by: 440bx on November 27, 2021, 09:08:42 pm
Anyway don't fall for the micro syntax trap, it takes more to make a good language than simply implementing a superset of all shorthands of all languages.

In the end what attracts users is visible ability/proof to complete certain tasks, like sizeable programs in the project's target domain.  It is not simply a matter of bullet lists of micro syntax comparisons.
But, in a way, it is.  Designing a language and writing a compiler for it are no simple tasks and, it is not realistic to think that its design and implementation will be perfect the first go round.  Like most everything, getting to something that is a real pleasure to use is an iterative process consisting of a long string of mostly small improvements - what you deride as "micro-syntax".

Being consistently against small improvements that add polish to a compiler/grammar, which I'm afraid, you tend to be, only yields a result that is not well polished around the edges.  The multiple variable initialization is a good example.



@munair,

So far, from what you've stated, I believe your philosophy towards designing and writing the compiler is in the right direction.  Making the compiler eventually self hosting is a good way to ensure the language packs a real punch.  I'm rooting for you. :)
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 27, 2021, 10:41:17 pm
In the end what attracts users ... is not simply a matter of bullet lists of micro syntax comparisons.
But, in a way, it is.

With regret, I join this thread to say I agree. You only have to look at the popular distinction between "curly bracket languages" and "Everything else".

Only last week I mentioned Pascal and got an extreme negative reaction from an ex-commercial programmer. The fact that she now makes a living mucking-out stables shouldn't be held against her :-)

MarkMLl


Title: Re: how to break out early of a case ?
Post by: munair on November 28, 2021, 08:27:09 am
With regret, I join this thread to say I agree. You only have to look at the popular distinction between "curly bracket languages" and "Everything else".

Two years ago I did do some programming in Go. The extra shift key required to type a curly bracket quickly becomes an annoyance if you're not used to it. Using actual keywords instead also allows for distinction between declaration blocks and execution blocks. After all, if there's one thing computers are good at, it is repeating things to make life easier for us:

C:
Code: [Select]
#include <gtk/gtkaboutdialog.h>
#include <gtk/gtkaccelgroup.h>
#include <gtk/gtkaccessible.h>
#include <gtk/gtkactionable.h>
... (some 250 includes in gtk.h only)

SharpBASIC:
Code: [Select]
incl is
  "gtk/gtkaboutdialog.h";
  "gtk/gtkaccelgroup.h";
  "gtk/gtkaccessible.h";
  "gtk/gtkactionable.h";
  ...
end;

The is..end; counterpart is do..end; (in Pascal begin..end). These keywords actually say something as opposed to {..}. I've never written a program in C or C++ (other than a "hello world" in C++ 3.0 DOS). These languages are way too symbolic for my taste.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 28, 2021, 10:03:52 am
These languages are way too symbolic for my taste.

I don't care for the use of "symbolic" in this context, since it's already used for things like algebra solvers which can do symbolic manipulation. I'd far prefer "concise"... "pithy" even.

"The C way" is definitely good for some things, but it's an inescapable fact that C has held C++ back and that C++ has, despite its wide adoption, severe flaws.

My choice would be for a core language like a tidied-up Pascal, but with abstractions that allowed all the higher-level cruft to be defined as privileged libraries and a front-end that allowed e.g. braces ** to be expanded to begin/end if that's what the user wanted.

I'm not sure whether that's doable, since keeping the default frontend similar to today's Object Pascal might demand an extensible syntax more flexible than anybody's managed so far. But I don't think that the current situation, where even trivial language tweaks can only be handled by modifying the core compiler with all that that implies, is tenable.

That is my personal opinion, no disrespect of the core team is intended.

MarkMLl

** Or, as I've written before, brackets https://forum.lazarus.freepascal.org/index.php/topic,53139.msg392670.html#msg392670
Title: Re: how to break out early of a case ?
Post by: marcov on November 28, 2021, 06:41:07 pm
Getting to something that is a real pleasure to use is an iterative process consisting of a long string of mostly small improvements - what you deride as "micro-syntax".

And that is not true IMHO. Those micro improvements are mostly for language comparison and hardly improve user productivity, if at all. But at the same time all the cruft makes adding real features harder and often has a long trail of little unforeseen consequences to disambiguate and fix.

A good example is the recently suggested named parameters that clashes with e.g. overloading (and that is only the most obvious clash)

Quote
Being consistently against small improvements that add polish to a compiler/grammar, which I'm afraid, you tend to be, only yields a result that is not well polished around the edges. 

That is a matter of taste. If you value detail syntax over real features, it might seem that way. But that is not an universal truth.

Neither is mine probably, but I hope I at least explained my view on it.
Title: Re: how to break out early of a case ?
Post by: munair on November 28, 2021, 10:28:18 pm
That is a matter of taste. If you value detail syntax over real features, it might seem that way. But that is not an universal truth.

Detailed syntax is not an antithesis to real features.

Missing 'detailed features' adds to little annoyances over time. If there's room/logic to implement them, then why not? Taking grouped initialization as example, I remember having to set several iterators/counters to zero statement by statement after declaration, which is probably the first reason for the grouped initialization feature request. Admittedly, it doesn't carry as much weight as 'real features', but that's no reason to ignore those 'detailed features'. Polishing a compiler just adds that little extra that allows for an ever better programming experience.

Earlier in this thread you also opted for leaving out explicit fall through, which I would say is more than a detailed feature.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 09:29:45 am
Missing 'detailed features' adds to little annoyances over time. If there's room/logic to implement them, then why not? Taking grouped initialization as example, I remember having to set several iterators/counters to zero statement by statement after declaration, which is probably the first reason for the grouped initialization feature request.

Initialisation where there is a list of expressions assigned to a list of lvalues can be very useful. However experience shows that the C-style multiple assignment a = b = 0 is nothing but trouble, particularly where there is automatic type coercion.

Noting Marco's comment about named parameters: that is something which, broadly speaking, I favour /but/ I suspect that the extra verbosity would prevent most people from using it in the situations where it would be really useful. I think that allowing multiple record field assignment (not just initialisation) would be less contentious and would provide an equivalent facility.

An alternative would be to allow a Smalltalk-style keyword sequence as function invocation, but while I use it in some of my own scripting I've never really tried to work out whether it is compatible with "Pascal as we know it".

MarkMLl
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 11:18:28 am
That is a matter of taste. If you value detail syntax over real features, it might seem that way. But that is not an universal truth.

Detailed syntax is not an antithesis to real features.

Missing 'detailed features' adds to little annoyances over time.

Only in the eyes of people that want to see any missing shorthand from any other language as such. As said they are more about giving a false sense of familiarity than about productivity.

Quote
If there's room/logic to implement them, then why not?

As said (with example) there is often crosstalk (unintended consequences) between features in a large, organically grown languages as Object Pascal is. As an additional bump, FPC is also a multi dialect compiler, which adds yet another dimension of possible complications.

Quite often the consequences are not visible up front.  But once it is in, you are stuck with it due to the backwards compatibility policies. Also the compromises are often sometimes liked by none because the syntax of the originating language is too ambiguous and has to be changed, or the application is limited.

Besides feature clashes there is also error generation to contend with. FPC prides itself on high quality error messages, and not just vague endless rows of "syntax error, unexpected token <x>". This is also quite often overlooked by the initial design of such quick and dirty language extensions.

Moreover, the person initiating the support often moves on leaving the core developers to pick up the pieces.

If you want language experimentation, by all means do it in a new dialect as Sharpbasic, plan it up front, and you can still remove failed experiments in the early versions.

Quote
Taking grouped initialization as example,
 
I remember having to set several iterators/counters to zero statement by statement after declaration, which is probably the first reason for the grouped initialization feature request.

I don't believe that any occasional verbosity automatically warrants a language expansion.  Moreover because FPC is not a language experimentation project in the first place.

Quote
Admittedly, it doesn't carry as much weight as 'real features', but that's no reason to ignore those 'detailed features'. Polishing a compiler just adds that little extra that allows for an ever better programming experience.

Mostly I get the impression that the only ones that care are the handful pressing for extensions, and more from an angle of  language design rather than pure users.

Moreover I'm even more wary if those same suggesters also dodge bearing real responsibility as core developers, but are only gaslighting the forums.

Quote
Earlier in this thread you also opted for leaving out explicit fall through, which I would say is more than a detailed feature.

IIRC I opted for using gotos and then having the optimizer eliminate that, for the rare cases that fall through is necessary. Since that is a solution without any syntax extensions, it is quite in line with my opinion stated above.


Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 11:47:34 am
I agree with most of what you've said, in particular that most of those demanding extra sugar are neither long-term FPC users nor people with the skillset that would allow them to contribute at the core level.

Only in the eyes of people that want to see any missing shorthand from any other language as such. As said they are more about giving a false sense of familiarity than about productivity.

However as a counterexample I'd like to highlight the ALGOL-style conditional expression that Wirth unaccountably omitted from Pascal:

Code: [Select]
  a := if b then c else d;

since this can't easily be emulated using a function and is such a common idiom in mainstream ALGOL derivatives- by which I of course mean C etc.- that its absence is embarrassing.

Apart from that I think that your comments about the difficulty of adding and testing facilities add weight to my contention that today's Pascal family (i.e. Object Pascal plus the other variants supported by FPC) has put on far too much weight and needs to be slimmed down to a core language flexible enough to support syntactic extensions.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 12:37:51 pm
Code: [Select]
  a := if b then c else d;

Like:

Code: Pascal  [Select][+][-]
  1. if b then a := c else a := d;

?
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 12:50:19 pm
However as a counterexample I'd like to highlight the ALGOL-style conditional expression that Wirth unaccountably omitted from Pascal:

Code: [Select]
  a := if b then c else d;

since this can't easily be emulated using a function and is such a common idiom in mainstream ALGOL derivatives- by which I of course mean C etc.- that its absence is embarrassing.

Hasn't there has been talk about this, ifthen() becoming an intrinsic and only evaluating the sides as necessary.   (because objective pascal needed it???)

I don't know if it happened in the end. Maybe Pascaldragon knows.
Title: Re: how to break out early of a case ?
Post by: winni on November 29, 2021, 12:53:01 pm
Hi!

Code: Pascal  [Select][+][-]
  1. a := math.ifthen (b,c,d);
  2.  
  3.  

Winni
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 01:33:37 pm
Code: Pascal  [Select][+][-]
  1. a := math.ifthen (b,c,d);
  2.  

Not equivalent since it evaluates the parameters unconditionally.

Noting also @SymbolicFrank's comment: I agree, but I can assure you that if translating a large amount of code from just about any ALGOL-derivative other than the Pascal clade the absence gets to be a right pain. And one so basic that it's difficult to explain when asking questions of the original authors.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 01:37:39 pm
Hasn't there has been talk about this, ifthen() becoming an intrinsic and only evaluating the sides as necessary.   (because objective pascal needed it???)

I don't know if it happened in the end. Maybe Pascaldragon knows.

I think he's looked at it but never seen it as pressing. However I highlighted this more as something which was a weird omission from the language than as an immediate "must-have": IMO it should really have been implemented at about the time that Pascal functions gained the ability to return values larger than simple numbers.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 03:39:37 pm
I don't believe that any occasional verbosity automatically warrants a language expansion.  Moreover because FPC is not a language experimentation project in the first place.
The multiple initialization feature isn't about eliminating some verbosity.  It is about giving the language a mechanism that allows it to show that 2 or more variables should have the same initial state.

The fact that a data type can be associated with multiple variables isn't about eliminating verbosity, it is about giving the language a mechanism that enables it to declare that two or more variables should be of the same type.  This would be error prone if each variable had to be declared individually and, it would make maintenance more difficult.  For those same reasons, the language should support multiple variable initialization.  I believe you know this but, instead you pretend it's about "verbosity" and FPC not being an "experimentation project".

In addition to that, the missing multiple variable initialization shows there is an inconsistency in the grammar and that the grammar is gratuitously intransitive where it could easily be.  Specifically,
Code: Pascal  [Select][+][-]
  1. var
  2.   VariableA                       : integer = 0;  { no problem initializing this }
  3.   VariableB, VariableC, VariableD : integer;      { no problem with that either - and it _not_ about eliminating verbosity }
but, when you try
Code: Pascal  [Select][+][-]
  1. VariableB, VariableC, VariableD : integer = 0;   { sorry, no can do }
A language that allows the first two, should also allow the third simply for symmetry, consistency and completeness (of course, why would anyone care about those things.)
Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 04:14:48 pm
Code: Pascal  [Select][+][-]
  1. function iif(a: Boolean; b, c: Variant): Variant; inline;
  2. begin
  3.   if a then Result := b else Result := c;
  4. end;

Or use generics, if you don't want the conversions.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 04:40:36 pm
Code: Pascal  [Select][+][-]
  1. function iif(a: Boolean; b, c: Variant): Variant; inline;
  2. begin
  3.   if a then Result := b else Result := c;
  4. end;

Or use generics, if you don't want the conversions.

As I've said twice already, that doesn't work because when called all parameters are evaluated (with potential side-effects) before the condition is evaluated. I'm sorry, but this one really is an old chestnut.

I don't know- and am not going to investigate- whether ALGOL W had this form of conditional. If for some reason it didn't then it might explain why it didn't get into Pascal.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 05:03:13 pm
I don't believe that any occasional verbosity automatically warrants a language expansion.  Moreover because FPC is not a language experimentation project in the first place.
The multiple initialization feature isn't about eliminating some verbosity.  It is about giving the language a mechanism that allows it to show that 2 or more variables should have the same initial state.

Please explain this in more detail. What are concrete practical benefits of doing this, other than for some fuzzy feeling of orthogonality/symmetry for the grammar police? What checks or features do you envision on top of this?

Quote
This would be error prone if each variable had to be declared individually and, it would make maintenance more difficult.

More syntactical features also make a language harder to read, and thus increase failure rate, specially for rarely used constructs like this. So I would wager it is is a net loss.

Quote
For those same reasons, the language should support multiple variable initialization.  I believe you know this but, instead you pretend it's about "verbosity" and FPC not being an "experimentation project".

No. I mean those things seriously, and am not just pulling reasoning out of a sleeve to delay the adding. If wanted to that,  I'd be like the others and keep silent, letting threads like this just rage till they run out of fuel, and the status quo persists, with some baseless extra anti-core sentiment added.

I'm actually here and investing the time to discuss with you. Please respect that.

Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 05:05:39 pm

As I've said twice already, that doesn't work because when called all parameters are evaluated (with potential side-effects) before the condition is evaluated. I'm sorry, but this one really is an old chestnut.

I don't know- and am not going to investigate- whether ALGOL W had this form of conditional. If for some reason it didn't then it might explain why it didn't get into Pascal.

Wirth notoriously liked orthogonal and simple grammars. Like 440bx but in a more subtractive than additive way.

I think the feature simply didn't make the cut to be considered "base algol subset".

Keep in mind that in those days there were years between language definition and implementation. (68-70 in Pascal's case)
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 05:38:52 pm
I'm actually here and investing the time to discuss with you. Please respect that.
I respect that but...

Please explain this in more detail. What are concrete practical benefits of doing this, other than for some fuzzy feeling of orthogonality/symmetry for the grammar police? What checks or features do you envision on top of this?

More syntactical features also make a language harder to read, and thus increase failure rate, specially for rarely used constructs like this. So I would wager it is is a net loss.
The answer to your concerns is very simple and, it is shown quite well by the fact that the language allows defining a group of variables of the same type.  i.e,
Code: Pascal  [Select][+][-]
  1. var
  2.   VariableA, VariableB : TSOME_TYPE;
With that mechanism the language ensures that 2 or more variables that must be of the same type are declared in a group all having the same type.  If that feature wasn't there and 2 or more variables had to be of same type then, ensuring they are of the same type would require the programmer to inspect every variable declaration to ensure they are of the same type and, if during maintenance, a programmer changes the type of one variable, it would not be obvious that the types of the other variables also has to be changed.  That is a problem.

Exactly the same reasons apply to group initialization.  Group initialization is _not_ about mere syntax and embellishments. It's about the language having a mechanism to declare that two or more variables should have the same initial state thereby relieving the programmer from having to inspect and update the initial state of multiple variables which makes it a distinct possibility that one or more will be omitted by mistake.

Hundreds of little things like that are what makes language definition standards useful.  Unless I am mistaken, the extended Pascal standard includes group variable initialization.  Also, symmetry, which you seem to deride, isn't just something "nice to have", when implementing symmetry is not possible it often indicates a problem in the design.


Title: Re: how to break out early of a case ?
Post by: munair on November 29, 2021, 05:45:34 pm
In addition to that, the missing multiple variable initialization shows there is an inconsistency in the grammar and that the grammar is gratuitously intransitive where it could easily be.  Specifically,
Code: Pascal  [Select][+][-]
  1. var
  2.   VariableA                       : integer = 0;  { no problem initializing this }
  3.   VariableB, VariableC, VariableD : integer;      { no problem with that either - and it _not_ about eliminating verbosity }
but, when you try
Code: Pascal  [Select][+][-]
  1. VariableB, VariableC, VariableD : integer = 0;   { sorry, no can do }
A language that allows the first two, should also allow the third simply for symmetry, consistency and completeness (of course, why would anyone care about those things.)
I second that. In the case of Pascal, it is either because of language compatibility issues (the compiler modeled after an existing language), or it is a compiler design flaw. I don't think that targeting multiple architectures would make it more/too difficult to support such basic feature. I remember QuickBASIC had some limitations with declaring variables, simply because the developers didn't think of it or they felt it was not significant enough to them to implement. But that's talking of a 1980s compiler.
Title: Re: how to break out early of a case ?
Post by: munair on November 29, 2021, 05:46:53 pm
Keep in mind that in those days there were years between language definition and implementation. (68-70 in Pascal's case)

Very true.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 06:02:02 pm
Wirth notoriously liked orthogonal and simple grammars. Like 440bx but in a more subtractive than additive way.

How can that statement possibly be reconciled with the const, type and var divisions, which could easily have been wrapped in some sort of block structure? I use the COBOL term advisedly, since by the late 60s people- particularly in the ALGOL community- were already beginning to realise what a mess that sort of thing was.

Quote
I think the feature simply didn't make the cut to be considered "base algol subset".

Keep in mind that in those days there were years between language definition and implementation. (68-70 in Pascal's case)

The bulk of the work was done between May and October 1969, but the details were still fairly fluid in '70.

The problem is that Wirth was still using recursive ascent which was notoriously difficult to maintain... have you looked at the source of his early compilers?

The syntactic differences between Pascal and ALGOL were intended to break backwards compatibility: Wirth felt that he'd been stabbed in the back (possibly by McCarthy) and wanted nothing more to do with it. But the difficulty of actually working with his codebase of the time limited his options, in which case it's surprising that he removed a facility which was presumably not broken.

Unless of course it was a casualty of e.g. an attempted fix of e.g. the dangling else issue, which he'd had to walk back in a hurry when he hit his time constraints.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 07:17:12 pm
Code: Pascal  [Select][+][-]
  1. procedure Bla;
  2. var
  3.   i:Integer;
  4. begin
  5.   i := 0;
  6.   ..
  7. end;  

In the above code, initializing i to 0 is superfluous, as all variables are zeroed by default. The same goes for the function result. But it makes it much more readable.
Title: Re: how to break out early of a case ?
Post by: JuhaManninen on November 29, 2021, 07:23:00 pm
Code: Pascal  [Select][+][-]
  1. procedure Bla;
  2. var
  3.   i:Integer;
  4. begin
  5.   i := 0;
  6.   ..
  7. end;  

In the above code, initializing i to 0 is superfluous, as all variables are zeroed by default. The same goes for the function result. But it makes it much more readable.
No, actually local variables of a function/procedure are not zeroed. Members of a class are zeroed.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 07:25:17 pm
Code: Pascal  [Select][+][-]
  1. procedure Bla;
  2. var
  3.   i:Integer;
  4. begin
  5.   i := 0;
  6.   ..
  7. end;  

In the above code, initializing i to 0 is superfluous, as all variables are zeroed by default. The same goes for the function result. But it makes it much more readable.

Manual section 4.4:

By default, simple variables in Pascal are not initialized after their declaration. Any assumption that they contain 0 or any other default value is erroneous: They can contain rubbish.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on November 29, 2021, 07:35:03 pm
No, actually local variables of a function/procedure are not zeroed. Members of a class are zeroed.

And with languages that do not zero variables upon declaration, multi-initialization is not merely a detail.
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 08:39:31 pm
With that mechanism the language ensures that 2 or more variables that must be of the same type are declared in a group all having the same type.  If that feature wasn't there and 2 or more variables had to be of same type then, ensuring they are of the same type would require the programmer to inspect every variable declaration to ensure they are of the same type and, if during maintenance, a programmer changes the type of one variable, it would not be obvious that the types of the other variables also has to be changed.  That is a problem.

Which perfectly demonstrates that allowing one shorthand only needs to the next.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 08:54:55 pm
No, actually local variables of a function/procedure are not zeroed. Members of a class are zeroed.

Aren't managed variables like strings initialised to guaranteed minimum length as well?

MarkMLl
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 09:02:38 pm
No, actually local variables of a function/procedure are not zeroed. Members of a class are zeroed.

Aren't managed variables like strings initialised to guaranteed minimum length as well?

Afaik that is considered an implementation detail, and to keep compatible with shortstrings, it was advised (*) not to abuse that, so that string code could be swapped betwixt them.

(*) in FPC 1.x and early FPC 2.x times. Haven't heard much about it since though.
Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 09:05:45 pm
Ah, so I got it wrong, but I never noticed because I initialize everything. Interesting.
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 09:06:27 pm
Wirth notoriously liked orthogonal and simple grammars. Like 440bx but in a more subtractive than additive way.

How can that statement possibly be reconciled with the const, type and var divisions, which could easily have been wrapped in some sort of block structure?

How is that related to removing rarely used features or not?  It is not that if you instinctively think, "no, this one not?", that you are forced reevaluate each and every feature ad nauseam on the penalty of death ?


Quote
The bulk of the work was done between May and October 1969, but the details were still fairly fluid in '70.

But probably also recycled ideas from the Algol-W period

Quote
The problem is that Wirth was still using recursive ascent which was notoriously difficult to maintain... have you looked at the source of his early compilers?

No.

Quote
Unless of course it was a casualty of e.g. an attempted fix of e.g. the dangling else issue, which he'd had to walk back in a hurry when he hit his time constraints.

That does make some sense. That dangling else made him wary of the construct. But still, probably if he considered it crucial he'd found a solution
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 09:13:38 pm
Aren't managed variables like strings initialised to guaranteed minimum length as well?

Afaik that is considered an implementation detail, and to keep compatible with shortstrings, it was advised not to abuse that, so that string code could be swapped betwixt them.

Thanks for that. This is a bit like chess: if somebody had said that (non-shortstring) strings were minified (? :-) I'd have cited this

Code: Pascal  [Select][+][-]
  1. type
  2.   TPortDescription= record
  3.                       baseName: string;   (* This field is mandatory, e.g. ttyS *)
  4.                       idVendor: string;   (* Not tested if blank                *)
  5.                       idProduct: string;  (* Not tested if blank                *)
  6.                       driverName: string; (* Non-presence is ignored if blank   *)
  7.                       manufacturer: string; (* Non-presence is ignored if blank *)
  8.                       product: string;    (* Non-presence is ignored if blank   *)
  9.                       serial: string      (* Non-presence is ignored if blank   *)
  10.                     end;
  11. ...
  12. const
  13.   descriptionTemplate: TPortDescription= (
  14.                          baseName: 'ttyUSB';
  15.                          idVendor: '';
  16.                          idProduct: '';
  17.                          driverName: 'usb-serial/drivers/pl2303';
  18.                          manufacturer: 'Prolific Technology Inc.';
  19.                          product: 'USB-Serial Controller';
  20.                          serial: '';
  21.                        );
  22.  

and asked why those fields had to be explicitly initialised if their state was known.

MarkMLl

Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 09:20:08 pm
Well, I have been using TP, BP, Delphi and FPC for 35 years, and I learned something new today. But I think I do have an excellent grasp of it.

I have been programming in C and C++ almost as long, and while C isn't that hard in small bites, it can become really hard when you get to identifiers like this:

Code: [Select]
**(*((*SomeIdentifier)*)*)
And I doubt you can truly understand C++. Too much special cases and most of what you can do, you really shouldn't.
Title: Re: how to break out early of a case ?
Post by: SymbolicFrank on November 29, 2021, 09:24:46 pm
and asked why those fields had to be explicitly initialised if their state was known.

MarkMLl
To make it easier to understand?
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 09:30:47 pm
And I doubt you can truly understand C++. Too much special cases and most of what you can do, you really shouldn't.

Agreed. I'm slowly reading through the Rust documentation: they appear to be making a genuine attempt at asking what's really needed in the core language to support more complex stuff built on top.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 09:37:59 pm
Which perfectly demonstrates that allowing one shorthand only needs to the next.
That reply of yours is an excellent example of your tendency to present useful features as if they were programming platitudes.

Being able to specify a group of variables to be of the same type by making it a single declaration is hardly a simple shorthand.  Without that "shorthand", there is no indication that those variables must be of the same type (an important fact to be aware of when maintaining the program.) It's the same thing with group variable initialization.

As I stated previously, I believe you know this.
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 10:03:48 pm
Being able to specify a group of variables to be of the same type by making it a single declaration is hardly a simple shorthand.   

So what else is there to a group of variables then?

What is the concrete difference between

Code: [Select]
var a,b: integer;

vs

Code: [Select]
var a: integer;
     b: integer;

?

The "group of variables" only exists to justify the need for the initialization shorthand. It isn't a real thing.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 10:19:47 pm
The "group of variables" only exists to justify the need for the initialization shorthand. It isn't a real thing.

Everybody, please. I'm certainly not here to try to knock the core team, but neither am I here to dismiss every possible enhancement: particularly when well-argued.

(Or, as in an advert many years ago, "I come to bury C, sir, not to praise it" :-)

The "group of variables" is basically what is discussed in the FP Reference Guide, section 3.5, where it explicitly says

The referenced type should be introduced later in the same Type block. No other block may come between the definition of the pointer type and the referenced type.

So there's already strong precedent for considering types or variables declared together being in some way related to a greater extent than if they'd been declared separately. I believe that that was subsequently tightened up in Modula-2.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 10:22:29 pm
What is the concrete difference between

Code: Pascal  [Select][+][-]
  1. var a,b: integer;
  2.  

vs

Code: Pascal  [Select][+][-]
  1. var a: integer;
  2.      b: integer;
  3.  

?
The concrete difference between those two is that, if used properly, the first construct informs the programmer that the two variables in the declaration should be of the same type. 

The second declaration doesn't do that.  If there is a need for the two variables to be of the same type then, it's very likely that in a maintenance cycle, a programmer will decide to change the type of the first variable and omit changing the type of the second because the impression is that they are  two totally unrelated variables or vice-versa, of course.

That's the concrete difference.
Title: Re: how to break out early of a case ?
Post by: munair on November 29, 2021, 10:23:40 pm
Code: Pascal  [Select][+][-]
  1. var
  2.   a, b, c: integer = 0;
  3.   s1, s2: string;
  4. begin
  5. //..
  6. end;

vs

Code: Pascal  [Select][+][-]
  1. var
  2.   a: integer = 0;
  3.   b: integer = 0;
  4.   c: integer = 0;
  5.   s1: string;
  6.   s2: string;
  7. begin
  8. //..
  9. end;

Who would prefer the second syntax if the first were available?
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 10:28:01 pm
Who would prefer the second syntax if the first were available?
Not to mention that there is no good reason not to have the first option.  It's the natural option.
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 10:43:54 pm
The "group of variables" is basically what is discussed in the FP Reference Guide, section 3.5, where it explicitly says

The referenced type should be introduced later in the same Type block. No other block may come between the definition of the pointer type and the referenced type.

So there's already strong precedent for considering types or variables declared together being in some way related to a greater extent than if they'd been declared separately. I believe that that was subsequently tightened up in Modula-2.

Weak argumentation to lift an exception to limit the impact of forward type referencing to a general rule even for other blocks. IMHO not even remotely plausible, it is like arguing that all semi-colons should go away because the dangling else was fixed by omitting it.

Modula2 also added  "opague" forward references, which were looser, as the final type of the pointer was  only defined in the implementation, being primarily meant as handle type.  I don't remember what the rules were for non opague types.


Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 10:48:08 pm
Code: Pascal  [Select][+][-]
  1. var
  2.   a, b, c: integer = 0;
  3.   s1, s2: string;
  4. begin
  5. //..
  6. end;

Or even

Code: Pascal  [Select][+][-]
  1. var
  2.   a, b, c: integer = (0, 0, 0);
  3.   s1, s2: string;
  4. begin
  5. //..
  6. end;

I grant that this is less concise than some form of multiple or replicated assignment, but it does guarantee that the three variables can be considered to be the same type by the compiler.

And it appears to be that level of robustness that Rust is targetting...

MarkMLl
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 10:51:12 pm
Modula2 also added  "opague" forward references, which were looser,

I'd hardly call that looser. It was still a robust type check, albeit of a type which was not accessible external to the module.

MarkMLl

p.s. s/gue/que/g :-)
Title: Re: how to break out early of a case ?
Post by: munair on November 29, 2021, 10:51:41 pm
Who would prefer the second syntax if the first were available?
Not to mention that there is no good reason not to have the first option.  It's the natural option.

I remember having this discussion before and if memory serves me well, the grouped initialization syntax cannot be easily implemented in FPC. While I haven't looked at the FPC source myself, I can imagine that if the parser feeds the syntax directly to a AST structure, the initialization may be a problem.

SharpBASIC doesn't do it like that. It collects all declarations with type and initialization (immediate) and adds them to the symbol table. After the stack frame is setup the symbol table (with initializations) is read out and the variables are initialized.

The following code:
Code: Text  [Select][+][-]
  1. sub test()
  2.   dim a, b, c: int = 0;
  3.   dim s1, s2: str;
  4. do
  5.  
  6. end;

would generate:
Code: ASM  [Select][+][-]
  1.         push    ebp
  2.         mov     ebp, esp
  3.         sub     esp, 20
  4. ; load immediate
  5.         movsx   eax, byte [_sb_refn]
  6. ; save a
  7.         mov     [ebp - 4], eax
  8. ; save b
  9.         mov     [ebp - 8], eax
  10. ; save c
  11.         mov     [ebp - 12], eax
  12. ; create local variable string 's1'
  13.         call    _sb_create_strdesc
  14. ; save descriptor address
  15.         mov     [ebp - 16], eax
  16.  
  17. ; create local variable string 's2'
  18.         call    _sb_create_strdesc
  19. ; save descriptor address
  20.         mov     [ebp - 20], eax

With or without grouped initialization, the output would be the same, but we have the compiler to do the dirty work for us, as it should be.  ;)
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 10:57:15 pm
Code: Pascal  [Select][+][-]
  1. var
  2.   a, b, c: integer = (0, 0, 0);
  3.   s1, s2: string;
  4. begin
  5. //..
  6. end;
I would not be in favor of that one because transpositions are a common error.  That construction makes it too easy to inadvertently swap initial values.



ETA:

I remember having this discussion before and if memory serves me well, the grouped initialization syntax cannot be easily implemented in FPC. While I haven't looked at the FPC source myself, I can imagine that if the parser feeds the syntax directly to a AST structure, the initialization may be a problem.
Yes, I remember that too.  From memory and, if I understood what was said at the time, apparently there is some code that is shared with other portions of the compiler that would not welcome the minor changes required in parsing an initial value. 

SharpBASIC doesn't do it like that. It collects all declarations with type and initialization (immediate) and adds them to the symbol table. After the stack frame is setup the symbol table (with initializations) is read out and the variables are initialized.
That's the best way of doing it.  Much more powerful and flexible.
Title: Re: how to break out early of a case ?
Post by: marcov on November 29, 2021, 10:58:49 pm
The concrete difference between those two is that, if used properly, the first construct informs the programmer that the two variables in the declaration should be of the same type. 

But it can also be viewed as an coincidental side-effect of an old shorthand. Without concrete reasons, and only statements of intent the difference can't be easily settled.

To be honest: I don't know either way, both thoughts have occurred to me over the years.

But whatever the reason is, that kind of undocumented intent is IMHO a very shaky ground to build some grand consistency argument on top of.

What's next, reviewing of all block structures to omit vowels other than "e" and "i", because those are the ones that are in begin and end, for consistency's sake ?   (I'm glad that all those years of watching absurd British comedy finally pay off, making it easy to find ridiculous comparisons)
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 11:08:37 pm
What's next, reviewing of all block structures to omit vowels other than "e" and "i",

No, i should be removed on account of its similarity to 1. And e should be removed in homage to Georges Perec's magnum opus.

Seriously chaps... there's no need to be silly

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on November 29, 2021, 11:12:06 pm
But whatever the reason is, that kind of undocumented intent is IMHO a very shaky ground to build some grand consistency argument on top of.
Undocumented ?... it seems to me that one of the things programmers have to learn early in the process of learning to program is to group things that belong together (like declarations and sequences of code) and keeping separate, things that don't belong together (which usually goes by the more academically polished word "modularization".)

I have not seen any programming manuals document that the reader has to have the ability of recognizing individual glyphs, grouping them, associating a semantic meaning to specific groups in order to be able to read the fine manual.  Should I open a bug against the FPC documentation to have this undocumented requirement be brought to light ?


Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 29, 2021, 11:27:17 pm
I have not seen any programming manuals document that the reader has to have the ability of recognizing individual glyphs, grouping them, associating a semantic meaning to specific groups in order to be able to read the fine manual.

https://en.wikipedia.org/wiki/Principia_Mathematica

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Bart on November 29, 2021, 11:58:26 pm
Should I open a bug against the FPC documentation to have this undocumented requirement be brought to light ?
You most definitively must!

Bart
Title: Re: how to break out early of a case ?
Post by: MarkMLl on November 30, 2021, 12:15:57 am
Should I open a bug against the FPC documentation to have this undocumented requirement be brought to light ?
You most definitively must!

I've managed to write an appendix in a technical manual on the assumed difference between "male" and "female" in terms of connectors, but I think your suggestion would be straining credibility.

Seriously: I think it's a valid point, but I don't think that much can be done that isn't at least attempted by (E)BNF and "railroad diagrams".

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on November 30, 2021, 02:47:35 pm
What's next, reviewing of all block structures to omit vowels other than "e" and "i", because those are the ones that are in begin and end, for consistency's sake ?

Toying with keywords is not the same as completing a syntax that is already supported for 80% and that, by contrast, wouldn't break any compatibility. So it's not a fair comparison IMO.
Title: Re: how to break out early of a case ?
Post by: marcov on November 30, 2021, 02:49:54 pm
I have not seen any programming manuals document that the reader has to have the ability of recognizing individual glyphs, grouping them, associating a semantic meaning to specific groups in order to be able to read the fine manual.  Should I open a bug against the FPC documentation to have this undocumented requirement be brought to light ?

No, try to file at Webster's site. It is a disgrace if it isn't in there.
Title: Re: how to break out early of a case ?
Post by: PascalDragon on November 30, 2021, 11:23:16 pm
However as a counterexample I'd like to highlight the ALGOL-style conditional expression that Wirth unaccountably omitted from Pascal:

Code: [Select]
  a := if b then c else d;

since this can't easily be emulated using a function and is such a common idiom in mainstream ALGOL derivatives- by which I of course mean C etc.- that its absence is embarrassing.

Hasn't there has been talk about this, ifthen() becoming an intrinsic and only evaluating the sides as necessary.   (because objective pascal needed it???)

I don't know if it happened in the end. Maybe Pascaldragon knows.

I had added it back in January 2016 (see announcement mail (https://lists.freepascal.org/pipermail/fpc-pascal/2016-January/046376.html) of which the thread continued into February (https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046385.html)). After some heated discussions both on the public mailing list as well as the core one I removed it again in February (https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046617.html). The addition of the intrinsic is still part of the version history (https://gitlab.com/freepascal.org/fpc/source/-/commit/ed94ca4b24c3eb979383ae4f846da4047b70ac00) and I think somewhere I still have a patch for the ifthenelse - expression (alternatively it would be easily derivable from the provided commit for the IfThen intrinsic).
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 01, 2021, 08:46:13 am
I had added it back in January 2016 (see announcement mail (https://lists.freepascal.org/pipermail/fpc-pascal/2016-January/046376.html) of which the thread continued into February (https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046385.html)). After some heated discussions both on the public mailing list as well as the core one I removed it again in February (https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046617.html). The addition of the intrinsic is still part of the version history (https://gitlab.com/freepascal.org/fpc/source/-/commit/ed94ca4b24c3eb979383ae4f846da4047b70ac00) and I think somewhere I still have a patch for the ifthenelse - expression (alternatively it would be easily derivable from the provided commit for the IfThen intrinsic).

Thanks for that. Hence https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046626.html followed by https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046641.html

I don't like flame wars, and from my POV the core team has made their position clear. But it still rankles that a fundamental element that apparently gave ALGOL no problems was omitted from Pascal.

I also don't like C-style usage of ? since it breaks the otherwise-close correspondence between operators and procedure/function invocation, which in all cases evaluate parameters before applying the operation.

But that's my personal opinion.

Code: Text  [Select][+][-]
  1. fn main() {
  2.     let condition = true;
  3.     let number = if condition { 5 } else { 6 };
  4.  
  5.     println!("The value of number is: {}", number);
  6. }
  7.  

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 10:07:31 am
Code: Text  [Select][+][-]
  1. fn main() {
  2.     let condition = true;
  3.     let number = if condition { 5 } else { 6 };
  4.  
  5.     println!("The value of number is: {}", number);
  6. }
  7.  

MarkMLl

In my opinion this code is messy because the keywords IF and ELSE in such languages are both statements and operators. The syntax is trivial at best because it can be written in statements with the same effort and same amount of code. The only benefit is that the code is crammed on a single line. What benefit is that? There's absolutely no need here to make things more complicated for the compiler. A wise decision that Pascal didn't adopt it.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 01, 2021, 10:10:41 am
In my opinion this code is messy because the keywords IF and ELSE in such languages are both statements and operators. The syntax is trivial at best because it can be written in statements with the same effort and same amount of code. The only benefit is that the code is crammed on a single line. What benefit is that? There's absolutely no need here to make things more complicated for the compiler. A wise decision that Pascal didn't adopt it.

I'll leave you to argue that with the Rust community, and with the ALGOL-60 designers before them. I'm done with arguing.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 10:35:30 am
In my opinion this code is messy because the keywords IF and ELSE in such languages are both statements and operators. The syntax is trivial at best because it can be written in statements with the same effort and same amount of code. The only benefit is that the code is crammed on a single line. What benefit is that? There's absolutely no need here to make things more complicated for the compiler. A wise decision that Pascal didn't adopt it.

I'll leave you to argue that with the Rust community, and with the ALGOL-60 designers before them. I'm done with arguing.

MarkMLl

Actually, discussions like these can be inspiring. I might just think of supporting a similar shorthand for the compiler I'm working on. But it should be clear and different from other statements, perhaps something like:
Code: Text  [Select][+][-]
  1. by condition { number = 5, 6 };

The benefit of such construct is that due to a different keyword (by) it could be processed separately and never reach the expression parser. Just a spontaneous idea.
Title: Re: how to break out early of a case ?
Post by: Warfley on December 01, 2021, 12:30:46 pm
But that's my personal opinion.

Code: Text  [Select][+][-]
  1. fn main() {
  2.     let condition = true;
  3.     let number = if condition { 5 } else { 6 };
  4.  
  5.     println!("The value of number is: {}", number);
  6. }
  7.  

MarkMLl
I really like the python version:
Code: Pascal  [Select][+][-]
  1. myvar = 6 if condition else 7
By switching the if and value around, you can basically set a focus, the value you put first is so to say the "important" value. Also it allows really great chaining:
Code: Pascal  [Select][+][-]
  1. myvar = a if condition1 \
  2.    else b if condition2 \
  3.    else c if condition3 \
  4.    else d
It's (if used with linebreaks) a very clear syntax that is easy to read and understand and also resembles how you would write conditionals in mathematical notation

Compared to for example the condition first syntax:
Code: Pascal  [Select][+][-]
  1. myvar = if Condition1 then a
  2.    else if condition2 then b
  3.    else if condition3 then c
  4.    else d;
Here you have always to read the conditions (which might be long expressions themselves) before you get to the value.

It's a question on what a reader might focus, the thing you write first is the thing that the programmer will read and understand at first, while for the second part they might even need to scroll. I think that  it is more important to see what are the possible values, over what are the possible conditions.

Actually, discussions like these can be inspiring. I might just think of supporting a similar shorthand for the compiler I'm working on. But it should be clear and different from other statements, perhaps something like:
Code: Text  [Select][+][-]
  1. by condition { number = 5, 6 };

The benefit of such construct is that due to a different keyword (by) it could be processed separately and never reach the expression parser. Just a spontaneous idea.
Personally, just from looking at it, I think this solution isn't very great, because if I didn't have the context on what it is supposed to mean, I wouldn't understand it. Languages should be expressive on their own, reading a program  should be as easy as possible.

Also I think it would be really cluttered if it was chained
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 01, 2021, 01:02:46 pm
I think the major parsing problem is if the ALGOL-style expression is used (for some crazy reason) when a function result is discarded, e.g.

Code: Pascal  [Select][+][-]
  1. { success := } if someSyscall(...) = 0 then true else false
  2.  

...which will, of course, be rejected by the compiler albeit for the wrong reason. But this is hardly the first time that the compiler's produced not quite the right error message.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 01, 2021, 01:43:47 pm
I don't like flame wars, and from my POV the core team has made their position clear. But it still rankles that a fundamental element that apparently gave ALGOL no problems was omitted from Pascal.

I myself am still a fan of the ifthenelse - expression... Maybe I should start an Oxygene compatibility mode and then allow the use of that expression through a modeswitch.  :P

I also don't like C-style usage of ? since it breaks the otherwise-close correspondence between operators and procedure/function invocation, which in all cases evaluate parameters before applying the operation.

The "not evaluating both branches" is one of the main uses when I use that ternary operator in C(++). It was also the base idea behind the IfThen() intrinsic and would also be the applied to the ifthenelse - expression.

I really like the python version:
Code: Pascal  [Select][+][-]
  1. myvar = 6 if condition else 7

I don't like that order as it would require annoying changes in the parser. An initial keyword (in this case if) makes it much easier for the parser to handle (which is also why I'd prefer something like lambda x as x * 2 instead of Oxygene's x => x * 2 for a shorter lambda expression syntax and also why I prefer FPC's specialize keyword).
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 01:48:09 pm
Also I think it would be really cluttered if it was chained

You're right about the cluttering when chained, which would basically be sort of a definition switch statement. In SharpBASIC = if implemented = it could be something along the line of:

Code: Text  [Select][+][-]
  1. let number on condition is
  2.   5 else 6;
  3. end;
  4.  

or chained:

Code: Text  [Select][+][-]
  1. let number
  2.   on condition1 is
  3.     5;
  4.   on condition2 is
  5.     x else y;
  6.   // ...
  7.   end;

Food for thought...
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 01:51:36 pm
An initial keyword (in this case if) makes it much easier for the parser to handle

Very true.
Title: Re: how to break out early of a case ?
Post by: Warfley on December 01, 2021, 02:00:46 pm
I don't like that order as it would require annoying changes in the parser. An initial keyword (in this case if) makes it much easier for the parser to handle (which is also why I'd prefer something like lambda x as x * 2 instead of Oxygene's x => x * 2 for a shorter lambda expression syntax and also why I prefer FPC's specialize keyword).
Completely understandable, but I would still argue that the value then condition structure is better readable, so this could be done with an initial keyword. Just from the top of my head, something like:
Code: Pascal  [Select][+][-]
  1. myvar := choose
  2.   a if condition1,
  3.   b if condition2,
  4.   c if condition3,
  5.   d otherwise
  6. end // probably can be ommited through otherwise terminating the statement
Which is heaviely inspired by the mathematical notation of conditional functions as in
https://i.stack.imgur.com/xDCSl.png
Title: Re: how to break out early of a case ?
Post by: Warfley on December 01, 2021, 02:07:38 pm
Code: Text  [Select][+][-]
  1. let number by condition is
  2.   5 else 6;
  3. end;
  4.  

or chained:

Code: Text  [Select][+][-]
  1. let number
  2.   by condition1 is
  3.     5 else 6;
  4.   by condition2 is
  5.     x else y;
  6.   // ...
  7.   end;

Food for thought...
This looks really good, but i would replace "is" with "be" as this sounds much more natural when reading
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 01, 2021, 02:12:03 pm
The "not evaluating both branches" is one of the main uses when I use that ternary operator in C(++). It was also the base idea behind the IfThen() intrinsic and would also be the applied to the ifthenelse - expression.

My argument for spelling it out if-then-else is that it emphasises the control transfer aspect, something I feel an intrinsic IfThen() fails to do.

I did initially have problems accepting the idea because of the way that a number of Object Pascal constructs have Modula-2 explicit end, but have come to believe that it is, ultimately, the cleanest way of doing it.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: dseligo on December 01, 2021, 02:42:20 pm
I myself am still a fan of the ifthenelse - expression...

I like this form too. But I think at least if (maybe also then and else) should be changed to something else to avoid confusion and make better distinction between operator and statement.
Maybe: whenreturnotherwise

This is also fine: whenthenelse
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 02:44:51 pm
Code: Text  [Select][+][-]
  1. let number by condition is
  2.   5 else 6;
  3. end;
  4.  

or chained:

Code: Text  [Select][+][-]
  1. let number
  2.   by condition1 is
  3.     5 else 6;
  4.   by condition2 is
  5.     x else y;
  6.   // ...
  7.   end;

Food for thought...
This looks really good, but i would replace "is" with "be" as this sounds much more natural when reading

That went also through my mind, but in SharpBASIC there are two types of blocks: is..end for definitions and do..end (like Pascal's begin..end for execution. Additionally, a let statement like this would already introduce two new keywords, which is good enough as it would be referred to as the let..by statement.
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 02:46:51 pm
I like this form too. But I think at least if (maybe also then and else) should be changed to something else to avoid confusion and make better distinction between operator and statement.

I expressed the same thought. Let if be if.
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 04:21:46 pm
Making the else clause optional one could do

Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is white;
  3.   on water == hot is red else blue;
  4. end;
Title: Re: how to break out early of a case ?
Post by: dseligo on December 01, 2021, 05:55:23 pm
Making the else clause optional one could do

Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is white;
  3.   on water == hot is red else blue;
  4. end;

And how would you solve conflicts?
E.g.:
Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is white;
  3.   on air == hot is red else blue;
  4. end;

What would the result be if water is frozen and air is hot?
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 07:47:24 pm
Making the else clause optional one could do

Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is white;
  3.   on water == hot is red else blue;
  4. end;

And how would you solve conflicts?
E.g.:
Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is white;
  3.   on air == hot is red else blue;
  4. end;

What would the result be if water is frozen and air is hot?

It may look like a conflict, but it really is a definition switch statement:

Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is
  3.     white;                   // exits the let statement if true (no implicit fall-through)
  4.   on air == hot is
  5.     red else blue;           // if no condition is met color = blue
  6. end;

So testing is not on 'water' (the conditions) but on color against any condition. I just meant to demonstrate that if the else clause is optional this let construct can be equal to an if statement.
Title: Re: how to break out early of a case ?
Post by: dseligo on December 01, 2021, 09:01:17 pm
It may look like a conflict, but it really is a definition switch statement:

By 'switch' you mean like switch in C? Case in Pascal?

Quote
Code: Text  [Select][+][-]
  1. let color
  2.   on water == frozen is
  3.     white;                   // exits the let statement if true (no implicit fall-through)
  4.   on air == hot is
  5.     red else blue;           // if no condition is met color = blue
  6. end;

So testing is not on 'water' (the conditions) but on color against any condition. I just meant to demonstrate that if the else clause is optional this let construct can be equal to an if statement.

I think this is confusing. What if we do it like below, what would be value in 'color'?

Code: Text  [Select][+][-]
  1. let color
  2.   on air == hot is
  3.     red else blue;           // if no condition is met color = blue
  4.   on water == frozen is
  5.     white;                   // exits the let statement if true (no implicit fall-through)
  6. end;
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 10:53:48 pm
I think this is confusing. What if we do it like below, what would be value in 'color'?

Code: Text  [Select][+][-]
  1. let color
  2.   on air == hot is
  3.     red else blue;           // if no condition is met color = blue
  4.   on water == frozen is
  5.     white;                   // exits the let statement if true (no implicit fall-through)
  6. end;

Color would be blue because the second branch would never be reached. In such case the compiler could give a warning for unreachable code, or the compiler could complain about an else-less branch after an else-branch.
Title: Re: how to break out early of a case ?
Post by: munair on December 01, 2021, 10:56:47 pm
I just implemented a basic version of the let-statement (only one branch). Works like a charm and I kind'a like it.
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 02, 2021, 09:34:02 am
I like this form too. But I think at least if (maybe also then and else) should be changed to something else to avoid confusion and make better distinction between operator and statement.
Maybe: whenreturnotherwise

This is also fine: whenthenelse

The point is that the expression is most likely to be introduced due to an Oxygene language mode. And for that it must be if. So why introduce yet another keyword and syntax?
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 02, 2021, 09:40:26 am
The point is that the expression is most likely to be introduced due to an Oxygene language mode. And for that it must be if. So why introduce yet another keyword and syntax?

Would there also be a mode directive to enable that individually, or would there be too much risk of incompatibility with other syntax?

Going back to the thread where it was withdrawn, I note that a whole lot of the troublesome cases were actually in variable initialisation or function declaration. Allowing that the whole idea is that this generates conditional code and that variable/parameter initialisation/default is by definition resolved at compilation time, is that usage even valid?

MarkMLl

Title: Re: how to break out early of a case ?
Post by: munair on December 02, 2021, 11:09:05 am
I think this is confusing. What if we do it like below, what would be value in 'color'?

Code: Text  [Select][+][-]
  1. let color
  2.   on air == hot is
  3.     red else blue;           // if no condition is met color = blue
  4.   on water == frozen is
  5.     white;                   // exits the let statement if true (no implicit fall-through)
  6. end;

You were right about the confusion in that in my hastiness I initially gave a bad example with multiple else branches. Of course, the very first else would leave the statement. So when chained/switched, only the last branch should be allowed to include else similar to if.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 02, 2021, 11:17:55 am
You were right about the confusion in that in my hastiness I initially gave a bad example with multiple else branches. Of course, the very first else would leave the statement. So when chained/switched, only the last branch should be allowed to include else similar to if.

This is not the place to discuss the design of some other language, but from the POV of my experience with multiple ALGOL-based languages- and modern BASIC aspires to that heritage- that syntax... is severely deficient.

The thread was originally about implementing a particular type of control flow using Pascal as provided by FPC, and quite frankly I think we've got quite enough problems of our own around here. I'm also concerned about the consequences if either a Pascal newbie or somebody intent to criticise the language were to read this stuff.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on December 02, 2021, 11:47:09 am
This is not the place to discuss the design of some other language, but from the POV of my experience with multiple ALGOL-based languages-

You yourself chose "with regret" to join this thread with a comment that continued off-topic:

With regret, I join this thread to say I agree. You only have to look at the popular distinction between "curly bracket languages" and "Everything else".
MarkMLl

So why can languages such as C, C++, Algol, Python etc be discussed but not a compiler I'm working on that can benefit from these discussions? I haven't heard the OP or any moderator complaining about the thread so far, on the contrary, and in my experience they're quite lenient about going off-topic.

Quote
and modern BASIC aspires to that heritage- that syntax... is severely deficient.

Programmers tend to be misguided by the reputation that comes with the name BASIC. The SharpBASIC syntax is most definitely NOT deficient (some proposed the language should have another name).

Quote
I'm also concerned about the consequences if either a Pascal newbie or somebody intent to criticise the language were to read this stuff.

Your concern is duly noted.

If you want to discuss a specific language feature as you have in your recent posts undisturbed, then please start your own thread.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 02, 2021, 12:09:17 pm
All fair points. My apologies.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 02, 2021, 02:01:52 pm
The point is that the expression is most likely to be introduced due to an Oxygene language mode. And for that it must be if. So why introduce yet another keyword and syntax?

Would there also be a mode directive to enable that individually, or would there be too much risk of incompatibility with other syntax?

Yes and there shouldn't be any risk of incompatibility due to if being a keyword recognized by all language modes.

Going back to the thread where it was withdrawn, I note that a whole lot of the troublesome cases were actually in variable initialisation or function declaration. Allowing that the whole idea is that this generates conditional code and that variable/parameter initialisation/default is by definition resolved at compilation time, is that usage even valid?

I didn't see that the majority is about variable initialization and function declarations. But no, it wouldn't work there (though in theory one could allow it for constant expressions as well for variable initializations).
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 02, 2021, 02:53:40 pm
Would there also be a mode directive to enable that individually, or would there be too much risk of incompatibility with other syntax?

Yes and there shouldn't be any risk of incompatibility due to if being a keyword recognized by all language modes.

Thanks for that, I've worked through some of the possibilities in the past as a "thought experiment" so didn't think there would be.

Quote
I didn't see that the majority is about variable initialization and function declarations. But no, it wouldn't work there (though in theory one could allow it for constant expressions as well for variable initializations).

There was something in the postmortem... specifically https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046626.html

Anyway, I should go quiet here since as has been pointed out this isn't "my thread" and I'm responsible for adding noise to it.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on December 02, 2021, 05:43:19 pm
Anyway, I should go quiet here since as has been pointed out this isn't "my thread" and I'm responsible for adding noise to it.
I guess I am the thread's "owner".  For the record, I don't mind at all reading discussions of compiler features and possible implementations.  I think it's good for ideas to be publicly discussed and analyzed.

IOW, don't go quiet on my account.
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 03, 2021, 03:51:18 pm
Quote
I didn't see that the majority is about variable initialization and function declarations. But no, it wouldn't work there (though in theory one could allow it for constant expressions as well for variable initializations).

There was something in the postmortem... specifically https://lists.freepascal.org/pipermail/fpc-pascal/2016-February/046626.html

I take it you mean this?

Code: Pascal  [Select][+][-]
  1. Type
  2.    TMyArray = Array[1..if sizeof(integer)=2 then 4 else 5] of integer;
  3.  
  4.    myconst = if sizeof(integer)=2 then 4 else 5;
  5.  
  6.    Procedure Something(AA : Integer = if sizeof(integer)=2 then 4 else 5);
  7.  
  8.    Property A : Integer Index if sizeof(integer)=2 then 4 else 5 read geta;
  9.  
  10.    Property B : Integer Read FA Write FA default if sizeof(integer)=2 then 4 else 5 ;

Yeah, I agree with MvC here that this is a very good reason not to allow this as a constant expression at least. :-X Though these would look strange with any other construct as well and in fact currently they'd be done by using $if-constructs which makes them even more ugly... :-\
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 03, 2021, 04:31:49 pm
Yeah, I agree with MvC here that this is a very good reason not to allow this as a constant expression at least. :-X Though these would look strange with any other construct as well and in fact currently they'd be done by using $if-constructs which makes them even more ugly... :-\

I must admit that I'm a bit uneasy mixing directives (i.e. $ifdef something) with symbols declared in the context of the language. Even though being able to do so is extremely useful.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 06, 2021, 09:22:29 pm
Having thought through this a bit more...


I take it you mean this?

Code: Pascal  [Select][+][-]
  1. Type
  2.    TMyArray = Array[1..if sizeof(integer)=2 then 4 else 5] of integer;
  3.  
  4.    myconst = if sizeof(integer)=2 then 4 else 5;
  5.  
  6.    Procedure Something(AA : Integer = if sizeof(integer)=2 then 4 else 5);
  7.  
  8.    Property A : Integer Index if sizeof(integer)=2 then 4 else 5 read geta;
  9.  
  10.    Property B : Integer Read FA Write FA default if sizeof(integer)=2 then 4 else 5 ;

All of those examples could of course be replaced by sizeof(integer) + something... although neither would be robust if at some point an integer became 64 (or more) bits.

However after refactoring a bit of code today where the number of bits needed to represent something was an issue, I find myself wondering whether some other potential cases could be avoided by having something like Log2SizeOf() as an operation which could be resolved at compilation time.

I've flirted with ? and ?? in some of my own scripting stuff, the difference being the behaviour when the conditional evaluated to something other than a Boolean.

However my principal reason for favouring if-then-else as an expression element is to have Pascal implement as much as possible of ALGOL-60: omissions are embarassing.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: munair on December 07, 2021, 11:28:00 am
However my principal reason for favouring if-then-else as an expression element is to have Pascal implement as much as possible of ALGOL-60: omissions are embarassing.

MarkMLl

Why? ALGOL-60 was an important development at the time and the language was of major influence to later languages. But it also stems from a time when system resources were very limited. So that the then developers chose to allow the IF-statement in expressions came from necessity rather than elegance. Later language developers were wise to drop the construct. Why that would be embarrassing is beyond me.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 07, 2021, 11:50:16 am
Why? ALGOL-60 was an important development at the time and the language was of major influence to later languages. But it also stems from a time when system resources were very limited. So that the then developers chose to allow the IF-statement in expressions came from necessity rather than elegance. Later language developers were wise to drop the construct. Why that would be embarrassing is beyond me.

Because the immediate parent of Pascal was ALGOL W, which was a refinement by Wirth of ALGOL-60 incorporating ideas by Hoare. There is no rational reason why the if-then-else expression format was omitted, but as I've said before I suspect it was a casualty of the fact that Pascal was basically a "rush job".

The omission is embarrassing since it makes C etc. look like a more comprehensive implementation of the ALGOL concepts, and inconvenient since it makes semi-mechanical conversion of code snippets in the majority of popular languages to Pascal more difficult.

It is definitely not accurate to characterise ALGOL as a language for resource-constrained systems, since some of them had virtual memory etc. that later architectures lacked for decades. If anything, the comparatively rapid implementation of Pascal on the UCSD p-system and on CP/M systems argues the opposite. In any case, I fail to see how implementation of the if-then-else expression form favours one particular scale of system over some other.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 07, 2021, 01:23:04 pm
However after refactoring a bit of code today where the number of bits needed to represent something was an issue, I find myself wondering whether some other potential cases could be avoided by having something like Log2SizeOf() as an operation which could be resolved at compilation time.

Once we have support for compile time functions (the current term for them is “pure functions”) you can add that yourself ;)
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 07, 2021, 01:53:23 pm
Once we have support for compile time functions (the current term for them is “pure functions”) you can add that yourself ;)

:-) OTOH I'd suggest that that specific case might be worth examining whenever anybody complains that his desired initialisation can't be coded... I'm not suggesting that some implementation be added prematurely, only that it might turn out to be a common "pattern".

MarkMLl
Title: Re: how to break out early of a case ?
Post by: PascalDragon on December 07, 2021, 02:05:45 pm
Once we have support for compile time functions (the current term for them is “pure functions”) you can add that yourself ;)

:-) OTOH I'd suggest that that specific case might be worth examining whenever anybody complains that his desired initialisation can't be coded... I'm not suggesting that some implementation be added prematurely, only that it might turn out to be a common "pattern".

No, it's not worth examining, because then the next one will come with the next function and so on. Better focus this into a more general approach.

Also if all you need is the size in bits then you can always use BitSizeOf.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 07, 2021, 02:07:48 pm
No, it's not worth examining, because then the next one will come with the next function and so on. Better focus this into a more general approach.

Also if all you need is the size in bits then you can always use BitSizeOf.

I didn't mean examining as in "we'll add a function" but as in "if you did it like this you wouldn't be asking us to add a function" :-)

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Kaller on December 19, 2023, 10:47:05 pm
Grave digging here but in simple cases where the condition is not deeply embedded then

Code: Pascal  [Select][+][-]
  1. case I of
  2.   somevalue :
  3.   begin
  4.     statement ...
  5.     statement...
  6.  
  7.     if someconditionhere then
  8.    begin
  9.      ; // Do nothing
  10.    else
  11.   begin
  12.     // Carry on
  13.   end;
  14.  
  15.     statement ...
  16.     statement....
  17.   end;
  18.  
  19.   <more cases here>
  20. end; { case }
  21.  

Otherwise you could do something using functions, and/or raise an exception to the same effect.

Title: Re: how to break out early of a case ?
Post by: 440bx on December 20, 2023, 12:05:52 am
After asking the question I figured a very simple way that is even more powerful and flexible than the C switch.  Something like this:

Code: Pascal  [Select][+][-]
  1. while TRUE do
  2. begin
  3.   case SelectorVariable of
  4.     1 :
  5.     begin
  6.       <statement>
  7.       <statement>
  8.  
  9.       if somecondition then break;  { exits the case early }
  10.  
  11.       <statement>
  12.  
  13.       SelectorVariable := 2; { to cause fall through as in C }
  14.     end;
  15.  
  16.     2 :  
  17.     begin
  18.       <bunch of statements here>
  19.  
  20.       break; { exit case statement }
  21.     end;
  22.  
  23.     <more cases if needed >
  24.  
  25.     otherwise
  26.     begin
  27.       <statements>
  28.  
  29.       break;
  30.     end;
  31.   end; { case }
  32. end; { while }
  33.  

Note that instead of using TRUE, the while loop could use the SelectorVariable too and exit when it becomes a specific value, this could make some executions simpler.

As shown above, any case can carry out any test it wants and execute "break" which will end the loop, therefore the "case" too since they are now tied together.

The first case shows how to accomplish the C fall through.  Using the while loop enables full control of the execution of the each case, the sequence in which they are executed, fall through and, the number of time each case is executed.

Move over C switch, go to the corner and talk to Beethoven.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 08:47:57 am
Code: Pascal  [Select][+][-]
  1.       SelectorVariable := 2; { to cause fall through as in C }
  2.     end;
  3.  
  4.     2 :  
  5.  

Does that actually work, and is it documented behaviour? My understanding was that the selector was evaluated only at the head of the case statement.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Eugene Loza on December 20, 2023, 09:21:58 am
the selector was evaluated only at the head of the case statement.
Indeed, that's my understanding too, and that's how it works for me in objfpc mode:

Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. var
  4.   A: Integer;
  5. begin
  6.   A := 1;
  7.   case A of
  8.     1: begin
  9.          WriteLn('1');
  10.          A := 3;
  11.        end;
  12.     2: WriteLn('2');
  13.     3: begin
  14.          WriteLn('3');
  15.          A := 2;
  16.       end;
  17.   end;
  18.   WriteLn('Done');
  19.   ReadLn;
  20. end.  
Title: Re: how to break out early of a case ?
Post by: Nitorami on December 20, 2023, 09:25:53 am
You are right, that puzzled me as well because the code of 440bx does work, surprisingly. The trick is in the "while true" loop which repeats the case selection.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 09:38:21 am
You are right, that puzzled me as well because the code of 440bx does work, surprisingly. The trick is in the "while true" loop which repeats the case selection.

The problem is that he's changing the selector, then implying that it falls straight into 2: in the style of C. I agree that wrapping the case in a while works (which is, after all, standard state-machine practice) but I'm very dubious about that claimed fallthrough.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Zoran on December 20, 2023, 12:10:52 pm
You are right, that puzzled me as well because the code of 440bx does work, surprisingly. The trick is in the "while true" loop which repeats the case selection.

The problem is that he's changing the selector, then implying that it falls straight into 2: in the style of C. I agree that wrapping the case in a while works (which is, after all, standard state-machine practice) but I'm very dubious about that claimed fallthrough.

MarkMLl

You didn't understand the point -- in case "1", the selector variable is set to "2", then while loop continues and case "2" is executed in next iteration -- that is emulated fall-through; and that is what 440bx meant, not a real fall-through.
Title: Re: how to break out early of a case ?
Post by: Zoran on December 20, 2023, 12:25:51 pm
Actually, I realize that I used a very similar trick (currently lines 148-182) (https://github.com/zoran-vucenovic/swan/blob/3a8096ac1aa20a8ff3dce9efd89dccc0a1d63cc8/tzxblock11abstract.inc#L148-L182) to what 440bx shows, though I have never thought about it as a kind of "fall-through".
Title: Re: how to break out early of a case ?
Post by: Joanna on December 20, 2023, 12:47:40 pm
This discussion seems very long  :D

I think having a lot of code inside a case statement can get kind of messy. Here is what I would do. I would move the code out of case into procedures.
Code: Pascal  [Select][+][-]
  1. Case condition of
  2. 1:procedure1;
  3. 2:procedure2;
  4. End;
  5.  

Procedures are easy to break out of and doing so would make you exit the case statement as well.
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 01:06:20 pm
You didn't understand the point -- in case "1", the selector variable is set to "2", then while loop continues and case "2" is executed in next iteration -- that is emulated fall-through; and that is what 440bx meant, not a real fall-through.

Well he ought to write what he means then (no disrespect intended). A constant stream of "of course Pascal can do that..." statements which turn out to be not quite accurate does nothing to promote the language when read by the broader community.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 01:11:13 pm
This discussion seems very long  :D

I think having a lot of code inside a case statement can get kind of messy. Here is what I would do. I would move the code out of case into procedures.
Code: Pascal  [Select][+][-]
  1. Case condition of
  2. 1:procedure1;
  3. 2:procedure2;
  4. End;
  5.  

Procedures are easy to break out of and doing so would make you exit the case statement as well.

That's fine in theory but not in practice. Consider the situation where the case statement is in a loop, and each case can do one of

* exit the current procedure

* break the loop

* continue at the head of the loop

* continue at the end of the case statement

As soon as you have to accommodate those you're back at the situation of having multiple lines of code in each case, you've really gained nothing except a few lines of boilerplate elsewhere.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Joanna on December 20, 2023, 01:36:32 pm
i tried this and the continue seems to work fine from inside case
Code: Pascal  [Select][+][-]
  1. h:= 1;
  2.   while h < 5 do
  3.        begin
  4.        case h of
  5.             1:begin ShowMessage('continue'); continue; end;
  6.             2:break;
  7.            END;
  8.        inc(h);
  9.        END;  

do you have any code examples to illustrate what you are saying ?
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 02:13:42 pm
It wouldn't work if it were inside a procedure, which is what you were suggesting.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on December 20, 2023, 02:22:11 pm
The trick is in the "while true" loop which repeats the case selection.
Exactly! :)
Title: Re: how to break out early of a case ?
Post by: Thaddy on December 20, 2023, 02:24:49 pm
Mark, there is also something like mode ISO or mode MACPAS that allow non-local goto. Although these modes have several other restrictions, those will exit too depending on how the code is written. On a lower level there is even SetJmp-LongJmp which basically provides you with another exit strategy.  The latter two are in system.

Both are no longer recommended of course, but they do exist.
More in general, if you deliberatly want to write bad code, use the archaic language features... O:-) 8-) Especially non-re-entrant ones..
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 20, 2023, 02:35:05 pm
Mark, there is also something like mode ISO or mode MACPAS that allow non-local goto. Although these modes have several other restrictions, those will exit too depending on how the code is written. On a lower level there is even SetJmp-LongJmp which basically provides you with another exit strategy.  The latter two are in system.

Yes, I know. However I'm not promoting use of such things inside a procedure, but demonstrating that a bit of direct coding is sometimes by far the best way of doing something and that while "stylistically correct" Joanna's suggestion can be more trouble that it's worth.

...and before anybody says anything about Dijkstra and goto, I'd point out that he's later on record as writing that a quasi-religious aversion towards goto was not necessarily a pancea. Sometimes the best thing is to use the tools that a language gives you: carefully, and where appropriate documenting your rationale.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Joanna on December 21, 2023, 01:42:43 am
This is thread is a good resource for anyone who ever wants to do this to reference  :)
Title: Re: how to break out early of a case ?
Post by: dbannon on December 21, 2023, 03:03:43 am
https://xkcd.com/292/

Davo
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 21, 2023, 09:22:21 am
Quote
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

—Edsger Dijkstra

I believe there are one or two valid use cases, such as some older serial comms protocols which do not map easily to any of the layered models. Also consider this:

Code: Pascal  [Select][+][-]
  1.   case finalParam - (nextParam - 1) of
  2.     0: ;                                (* Use values etc. from config file     *)
  3.     1: begin
  4. valueOnly:
  5.          if finalParam >= nextParam then
  6.            values[address + offset] := parseValue(nextParam, values[address + offset])
  7.        end;
  8.     2: begin
  9. offsetAndValue:
  10.          if finalParam >= nextParam then
  11.            offset := parseOffset(nextParam);
  12.          nextParam += 1;
  13.          goto valueOnly                 (* Any sane language would allow a case *)
  14.        end                              (* selector to be treated as a label in *)
  15.   otherwise                             (* this context.                        *)
  16.     if finalParam >= nextParam then
  17.       address := parseAddress(nextParam);
  18.     nextParam += 1;
  19.     goto offsetAndValue
  20.   end;
  21.  

...in fact I find myself wondering whether the label keyword could be eliminated, with the only valid (albeit discouraged) use case of goto being in a case statement.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: 440bx on December 21, 2023, 05:39:02 pm
Just for the record...

I concede that the comment "to cause fall through as in C" found in the code could be misleading but, I thought it had already been clearly established in the thread that Pascal does _not_ offer any way of exiting a "case" early much less offer a "case" fall through feature.  IOW, it seems fine in the context in which it appears.

Zoran and Nitorami quickly figured out what how the whole thing works.  I mention this because I believe the construction is simple enough to be easily understood, which is important.

On a different note, generally speaking, I am dogmatic about the non-use of "goto".  I am convinced most modern programming languages, including FPC, provide enough flow control features to make the use of "goto" unnecessary in all cases.  This might not have been the case when Dijkstra made his comments about "goto"s.

For instance, using an FSM like the one I presented, allows a programmer "goto-ing" anywhere he/she wants - within the "case" - simply by altering the case control variable.  Moreover, using "break" and "continue" allows the creation of execution flows that would require genuine spaghetti code.  To be fair though, the manipulation (and potential abuse) of the case control variable should likely be considered a potential source of spaghetti code as well.

Title: Re: how to break out early of a case ?
Post by: Joanna on December 22, 2023, 01:38:23 am
Forgive me for a possible newbie questions..
Isn’t goto just a holdover from assembly language jump command ?
Also when using pascal without the goto, isn’t there still a goto being performed behind the scenes as part of the program flow?
Title: Re: how to break out early of a case ?
Post by: 440bx on December 22, 2023, 03:06:41 am
Forgive me for a possible newbie questions..
No problem.  Asking questions are the simplest way of gathering knowledge, therefore, questions should be encouraged.

Isn’t goto just a holdover from assembly language jump command ?
No, not really.  The "goto" statement is a very general way of transferring control to someplace.  It is very common in Assembly language because Assembly, usually, doesn't provide for/while/repeat loops nor high level conditionals.  All of those must be synthesized using "goto"s or jumps of one kind or another (conditional, unconditional, indexed, etc.)

Also when using pascal without the goto, isn’t there still a goto being performed behind the scenes as part of the program flow?
Yes, there is definitely one or more "goto" (or jumps) being performed behind the scenes BUT, in spite of that, it is _very_ different than an explicit "goto" instruction.  The _crucial_ difference is that the target(s) of Pascal's "behind the scenes" jumps/gotos are pre-established.  For instance, when a programmer sees a "for" loop - which is implemented using gotos/jumps - the programmer knows the very instant he/she sees the "for" statement that there is an execution scope that is limited by the end of the "for" statement.  IOW, the "for" denotes an explicit execution boundary, the same is true of "while", "repeat", "if" and other instructions that are implemented using jumps/gotos.

Contrast that with seeing "goto somelabel" somewhere in the code.  the "somelabel" can be _anywhere_ (in Pascal, usually limited to the function/procedure in which it appears but, even that is not guaranteed.)  The fact that the label can be placed "anywhere" is what makes the "goto" totally undesirable.  It makes execution flow harder to visualize and follow.  Use more than one target label (which implies multiple gotos) and you've got code that desperately needs marinara sauce to be palatable.
 
The fact that the target of a goto can be anywhere IS what makes gotos unacceptable.

It should also be noted that "goto"s are not the only way of creating spaghetti code and, the _ultimate_ mechanism that enables creating spaghetti code is having the ability to change the target of the "goto".  Imagine there is a statement "goto label_1" and that somehow that statement can be modified at runtime to be "goto label_27" (instead of "label_1").  Now, not only the target (label_xxx) has to be located but, the real target can only be determined at runtime.  Most programmers can see the many problems that creates.  By the way, changing the target of a "goto" was/is a feature found in the COBOL language using the instruction "ALTER".  Fortunately, companies decided that any programmer who used "ALTER" had to be _fired_ (and, yes, that is an excellent reason to fire a programmer.) 

Amazingly, the runtime dynamic target "feature" has become a pervasive element in modern programming and, that's all I'm going to say about that. 

There is no room nor need for "goto" in any correct program written in Pascal. 
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 22, 2023, 09:36:33 am
Goto exists in various forms in all types of language. although proponents of some would deny it ** .

Pascal's predecessors: ALGOL, COBOL and of course FORTRAN all had different forms of goto, Wirth tidied things up enormously.

However Pascal- even pre-objects- still has procedure variables which are basically pointers to functions, and we have to trust the compiler to not (allow the user to) foul things up. Everything here is based on the strength of the compiler's type checking: in principle it will never allow control to be transferred to the wrong type of procedure (i.e. expecting the wrong number of parameters etc.) which is one reason why nil- compatible with any pointer- is considered bad news.

Wirth's early code- his doctorate through to ALGOL-W- was, generally speaking, a mess: he learnt the benefit of carefully-managed control and data structures the hard way.

Something that you will see on occasion is the claim that such-and-such a well-defined structure allows software to /reason/ about the intention of a body of source code: I think that's putting things somewhat floridly, but at the same time from the code generation POV there are definite benefits to being able to say e.g. "we're now at point C, control must have arrived here from either point A or from point B hence we know exactly what registers are being used for temporary variables".

I'd take this opportunity to criticise the exposed form variable of Delphi and the LCL. That should be protected in some way so that once set up it can never be corrupted.

** Specifically, proponents of "functional" languages would deny it, on the grounds that "returning a function" which has been created on the fly is not a goto. I'm unconvinced, since that's basically a "pointer to a procedure" as discussed above so everything again boils down to type checking.

MarkMLl
Title: Re: how to break out early of a case ?
Post by: Josh on December 22, 2023, 11:09:10 am
Hi

Not read whole thread full of the flu, but this is what i have done.

Probably not suitable or similar code already posted, but hey ho, no ho ho ho...

Code: Pascal  [Select][+][-]
  1. Var ControlVar:Integer;
  2.  
  3. function MyControlLogic(Var aValue:Integer):Boolean;
  4. begin
  5.   result:=False;
  6.   Case aValue of
  7.     1:Begin
  8.         // do Stuff
  9.        aValue:=3;
  10.        WriteLn('1 become 3');
  11.       end;
  12.     2:Begin
  13.         // do Stuff
  14.        WriteLn('At 2 Exiting');
  15.        Exit(True);
  16.       end;
  17.     3:Begin
  18.         // do Stuff
  19.        aValue:=2;
  20.        WriteLn('3 become 2');
  21.       end;
  22.     4:begin
  23.         // do Stuff
  24.         WriteLn('A One off 4');
  25.         Exit(True);
  26.       end
  27.   else
  28.     Begin
  29.       WriteLn('A Drop through');
  30.       Exit(True);
  31.     End;
  32.   end;
  33. end;
  34.  
  35. begin
  36.   ControlVar:=1;
  37.   Repeat Until MyControlLogic(ControlVar);
  38.  
  39.   ControlVar:=4;
  40.   Repeat Until MyControlLogic(ControlVar);
  41.  
  42.   ControlVar:=5;
  43.   Repeat Until MyControlLogic(ControlVar);
  44.  
  45. end.          
Title: Re: how to break out early of a case ?
Post by: Joanna on December 22, 2023, 11:27:26 am
Thanks for the clarification about the goto. My first two programming languages {Basic & FORTRAN } used numbered lines and required goto for  just about everything. When I got to pascal I was told that pascal has a goto but never to use it because it wasn’t necessary.  It was quite a surprise at the time that programming could be done without a goto...
Title: Re: how to break out early of a case ?
Post by: MarkMLl on December 22, 2023, 01:03:09 pm
Thanks for the clarification about the goto. My first two programming languages {Basic & FORTRAN } used numbered lines and required goto for  just about everything. When I got to pascal I was told that pascal has a goto but never to use it because it wasn’t necessary.  It was quite a surprise at the time that programming could be done without a goto...

BTDT, I can identify. However even before people started to consider that ALGOL-based languages might possibly be some use for application or even systems programming it was extremely common either for assemblers to come with predefined BEGIN and END constructs (etc.) or for users to define them as macros... and there are still mainframe programmers who insist that IBM Macro Assembler is the pinnacle of intellectual endeavour.

Let's look at some of the implications. If you have a simple WHILE fetz DO BEGIN ... END the compiler will build a branch on its internal Abstract Syntax Tree (AST) representing that entire structure, which at some later point will be converted to something executable by hardware. If instead you have WHILE FALSE DO BEGIN END, the compiler has the option of deleting the branch from the AST in its entirety... /provided/ that it's not possible to jump into it by some other route.

From a different angle, I was once at a conference where the paper following mine criticised BREAK/EXIT/CONTINUE as closet GOTOs: not so much because they make the code generator's job harder (which they do) but because Dijkstra was considered to be dead against GOTO in all its forms. At which point all I can do is repeat the quote from earlier:

Quote
"Please don't fall into the trap of believing that I am terribly dogmatical about [the goto statement]. I have the uncomfortable feeling that others are making a religion out of it, as if the conceptual problems of programming could be solved by a single trick, by a simple form of coding discipline!"

—Edsger Dijkstra

There are cases where it is impossible to deny that a blanket prohibition on GOTO is beneficial. For example, if a nested BEGIN...END could, at the compiler's discretion, result in the creation of a local stackframe then a complete ban on being able to jump into or out of it is valuable.

On the other hand, there are cases where the maths and in particular array-processing crowd attempts to promote the avoidance of control transfer ("GOTO and all his works") to an extent that would make them figures of fun if they weren't so deathly serious. For example, I have seen code not unlike the equations at https://en.wikipedia.org/wiki/Collatz_conjecture#Iterating_on_real_or_complex_numbers where the writer has assumed that the "purity" of an array-based computation would be so sullied by a simple iteration to decide which of two possibilities should be used for each element that he has introduced transcendals in an attempt to simulate a discontinuous operator.

The gripping hand is that sometimes suboptimal control transfer (i.e. break etc.) is justified, and on very rare occasions so is goto. But it shouldn't be used casually.

MarkMLl
TinyPortal © 2005-2018