* * *

Author Topic: Why Pascal?  (Read 12100 times)

kupferstecher

  • Full Member
  • ***
  • Posts: 208
Re: Why Pascal?
« Reply #30 on: April 30, 2018, 03:30:49 pm »
"routine nested in routine" Pascal feature
I quite hate that feature. The function name gets pulled away from its code. Besides I don't see why the feature would be needed, just reduces a bit typing.
Anyways, I don't use it so in the end I'm fine with it.

Zoran

  • Hero Member
  • *****
  • Posts: 1244
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: Why Pascal?
« Reply #31 on: April 30, 2018, 06:37:29 pm »
Besides I don't see why the feature would be needed, just reduces a bit typing.

Reduces typing? No. It is not that trivial (and, by the way, it does not reduce typing -- you still have to write the whole function, don't you?).

When you need some functionality inside a function, you can implement it only there, by creating nested function, whose scope is within the function it is nested in, so not polluting outer scope.

This is far from trivial, as reducing typing would be, this gives one more possibility to avoid polluting the outer level scope -- a strong contribution to cleaner code.

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6403
Re: Why Pascal?
« Reply #32 on: April 30, 2018, 07:24:09 pm »
I quite hate that feature. The function name gets pulled away from its code. Besides I don't see why the feature would be needed, just reduces a bit typing.

Nested procedure can take parameters, so that is more than just typing.

I like them a lot. The IDE keeps the procedure signatures (multiple, if nested) in the top lines even to assist.

Handoko

  • Hero Member
  • *****
  • Posts: 2443
  • My goal: build my own game engine using Lazarus
Re: Why Pascal?
« Reply #33 on: April 30, 2018, 07:50:21 pm »
When you need some functionality inside a function, you can implement it only there, by creating nested function, whose scope is within the function it is nested in, so not polluting outer scope.
+1

I rarely use nested procedure/function, but it is a feature I like very much. Using it properly it really makes the code more readable and maintainable. It's has been discussed here:
http://forum.lazarus.freepascal.org/index.php/topic,38536.msg262013.html#msg262013

Pascal

  • Hero Member
  • *****
  • Posts: 693
Re: Why Pascal?
« Reply #34 on: April 30, 2018, 08:42:57 pm »
When you need some functionality inside a function, you can implement it only there, by creating nested function, whose scope is within the function it is nested in, so not polluting outer scope.
+1

It helps deduplicating code and making complex routines more readable by splitting code into meaningfull nested routines.
laz trunk - fpc trunk 32bit - Windows 10 Pro x64 (1803)

Leledumbo

  • Hero Member
  • *****
  • Posts: 7936
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Why Pascal?
« Reply #35 on: May 01, 2018, 06:07:18 pm »
I quite hate that feature. The function name gets pulled away from its code. Besides I don't see why the feature would be needed, just reduces a bit typing.
Anyways, I don't use it so in the end I'm fine with it.
Quite the contrary, you can name the nested subroutines very well according to their specific task inside the outer routine. And no, it doesn't reduce typing, it adds instead. But for the much more increased readibility and potentially prevent duplication, the more typing pays.

This is a rather extreme example (you don't do this often), but imagine how will this be written without the nested subroutines:
Code: Pascal  [Select]
  1. function Tokenize: TTokenList;
  2. var
  3.   StartLine,StartCol: LongWord;
  4.   Kind: TTokenKind;
  5.   Lexeme,ContextBasePath: String;
  6.   Look: Char;
  7.   TempState: TLexerState;
  8.  
  9.   procedure InitFields;
  10.   begin
  11.     StartLine := Line;
  12.     StartCol := Col;
  13.     Lexeme := EmptyStr;
  14.   end;
  15.  
  16.   procedure HandleText;
  17.   begin
  18.     case Look of
  19.       '{': begin
  20.         LexerState := lsPossiblyTagStart;
  21.       end;
  22.       else begin
  23.         Lexeme := Lexeme + Look;
  24.       end;
  25.     end;
  26.     Look := ReadChar;
  27.   end;
  28.  
  29.   procedure HandlePossiblyTagStart;
  30.   begin
  31.     case Look of
  32.       '{': begin
  33.         if Lexeme <> EmptyStr then begin
  34.           AddToken(StartLine,StartCol,Kind,Lexeme,ContextBasePath,Result);
  35.           InitFields;
  36.         end;
  37.         LexerState := lsTagStart;
  38.       end;
  39.       else begin
  40.         Lexeme := Lexeme + '{' + Look;
  41.         LexerState := lsText;
  42.       end;
  43.     end;
  44.     Look := ReadChar;
  45.   end;
  46.  
  47.   procedure HandleTagStart;
  48.   begin
  49.     case Look of
  50.       '{': begin
  51.         LexerState := lsUnescapedVariable;
  52.         Kind := tkUnescapedVariable;
  53.       end;
  54.       '&': begin
  55.         LexerState := lsUnescapedVariableAmp;
  56.         Kind := tkUnescapedVariable;
  57.       end;
  58.       '#': begin
  59.         LexerState := lsOpenSection;
  60.         Kind := tkOpenSection;
  61.       end;
  62.       '/': begin
  63.         LexerState := lsCloseSection;
  64.         Kind := tkCloseSection;
  65.       end;
  66.       'a'..'z','A'..'Z','_',' ': begin
  67.         LexerState := lsEscapedVariable;
  68.         Kind := tkEscapedVariable;
  69.         Lexeme := Look;
  70.       end;
  71.       else begin
  72.         raise ETokenize.Create('Invalid tag char: ' + Look);
  73.       end;
  74.     end;
  75.     Look := ReadChar;
  76.   end;
  77.  
  78.   procedure HandlePossiblyTagEnd;
  79.   var
  80.     p: SizeInt;
  81.   begin
  82.     case Look of
  83.       '}': begin
  84.         Lexeme := Trim(Lexeme);
  85.         case TempState of
  86.           lsOpenSection: ContextBasePath := ContextBasePath + Lexeme + '.';
  87.           lsCloseSection: begin
  88.             if ContextBasePath = EmptyStr then
  89.               raise Exception.Create('Closing unopened section: ' + Lexeme)
  90.             else begin
  91.               p := RPos('.',ContextBasePath);
  92.               Delete(ContextBasePath,p,Length(ContextBasePath) - p);
  93.             end;
  94.           end;
  95.         end;
  96.         AddToken(StartLine,StartCol,Kind,Lexeme,ContextBasePath,Result);
  97.         InitFields;
  98.         LexerState := lsTagEnd;
  99.       end;
  100.       else begin
  101.         Lexeme := Lexeme + '}' + Look;
  102.         LexerState := TempState;
  103.       end;
  104.     end;
  105.     Look := ReadChar;
  106.   end;
  107.  
  108.   procedure HandleTagEnd;
  109.   begin
  110.     LexerState := lsText;
  111.     Kind := tkText;
  112.   end;
  113.  
  114.   procedure HandleVariable;
  115.   begin
  116.     case Look of
  117.       '}': begin
  118.         TempState := LexerState;
  119.         if LexerState = lsEscapedVariable then
  120.           LexerState := lsPossiblyTagEnd
  121.         else
  122.           LexerState := lsPossiblyUnescapedVariableEnd;
  123.       end;
  124.       'a'..'z','A'..'Z','0'..'9','_',' ','.': begin
  125.         Lexeme := Lexeme + Look;
  126.       end;
  127.       else begin
  128.         raise ETokenize.Create('Invalid tag char: ' + Look);
  129.       end;
  130.     end;
  131.     Look := ReadChar;
  132.   end;
  133.  
  134.   procedure HandlePossiblyUnescapedVariableEnd;
  135.   begin
  136.     case Look of
  137.       '}': begin
  138.         LexerState := lsPossiblyTagEnd;
  139.       end;
  140.       else begin
  141.         raise ETokenize.Create('Invalid tag char: ' + Look);
  142.       end;
  143.     end;
  144.     Look := ReadChar;
  145.   end;
  146.  
  147.   procedure HandleUnescapedVariableAmp;
  148.   begin
  149.     case Look of
  150.       '}': begin
  151.         TempState := LexerState;
  152.         LexerState := lsPossiblyTagEnd;
  153.       end;
  154.       'a'..'z','A'..'Z','0'..'9','_',' ','.': begin
  155.         Lexeme := Lexeme + Look;
  156.       end;
  157.       else begin
  158.         raise ETokenize.Create('Invalid tag char: ' + Look);
  159.       end;
  160.     end;
  161.     Look := ReadChar;
  162.   end;
  163.  
  164.   procedure HandleSection;
  165.   begin
  166.     case Look of
  167.       '}': begin
  168.         TempState := LexerState;
  169.         LexerState := lsPossiblyTagEnd;
  170.       end;
  171.       'a'..'z','A'..'Z','0'..'9','_',' ','.': begin
  172.         Lexeme := Lexeme + Look;
  173.       end;
  174.       else begin
  175.         raise ETokenize.Create('Invalid tag char: ' + Look);
  176.       end;
  177.     end;
  178.     Look := ReadChar;
  179.   end;
  180.  
  181. begin
  182.   Initialize(Result);
  183.   LexerState := lsText;
  184.  
  185.   InitFields;
  186.   ContextBasePath := EmptyStr;
  187.   Look := ReadChar;
  188.   Kind := tkText;
  189.   while Look <> EOFChar do begin
  190.     case LexerState of
  191.       lsText                        : HandleText;
  192.       lsPossiblyTagStart            : HandlePossiblyTagStart;
  193.       lsTagStart                    : HandleTagStart;
  194.       lsPossiblyTagEnd              : HandlePossiblyTagEnd;
  195.       lsTagEnd                      : HandleTagEnd;
  196.       lsEscapedVariable,
  197.       lsUnescapedVariable           : HandleVariable;
  198.       lsPossiblyUnescapedVariableEnd: HandlePossiblyUnescapedVariableEnd;
  199.       lsUnescapedVariableAmp        : HandleUnescapedVariableAmp;
  200.       lsOpenSection,lsCloseSection  : HandleSection;
  201.     end;
  202.   end;
  203.   if Lexeme <> EmptyStr then begin
  204.     AddToken(StartLine,StartCol,Kind,Lexeme,ContextBasePath,Result);
  205.   end;
  206. end;
  207.  
The inner subroutines depend on local variables of the outer subroutine, so without nested subroutine feature, you will need to duplicate that for ALL of them. It's not just unnecessarily tedious, but can easily fail when you forget to mark one (or two or three or more) as var (every inner subroutine will both read and write those variables). Otherwise, you can just copy their bodies into the case statement, but... are you sure you really want to give up readability that much?
« Last Edit: May 01, 2018, 06:11:50 pm by Leledumbo »

Thaddy

  • Hero Member
  • *****
  • Posts: 6529
Re: Why Pascal?
« Reply #36 on: May 01, 2018, 06:29:50 pm »
@leledumbo:
I agree with you but consider this:
Unindent all...... :D After all, white space is actually ignored by most languages except Python.... :(  It is up to the - not only Pascal! - programmer to respect it and make sense of it.
Code: Pascal  [Select]
  1. {$macro on} {$define labas:='hello,'}{$define pasauli:='world'}program dosomethingsilly;begin;writeln(labas, pasauli);end.
Btw: that's Lithuanian. My wife is...

Point I am trying to make that this feature relies on programmer discipline. It is not a language feature of Pascal to make things understandable. It is a language feature to make things readable (after all) O:-) Not everybody has the gift of logic. Not even programmers. :'( :o :(
« Last Edit: May 01, 2018, 06:49:05 pm by Thaddy »
Ada's daddy wrote this:"Fools are my theme, let satire be my song."

kupferstecher

  • Full Member
  • ***
  • Posts: 208
Re: Why Pascal?
« Reply #37 on: May 01, 2018, 11:02:26 pm »
Nested procedure can take parameters, so that is more than just typing.
I wasn't aware of that so I didn't see the point of nested functions. Already convinced!

(From a code-readability point of view I'd prefere the nested functions to be placed below the "outer" function.)

HeavyUser

  • Full Member
  • ***
  • Posts: 148
Re: Why Pascal?
« Reply #38 on: May 01, 2018, 11:15:21 pm »
(From a code-readability point of view I'd prefere the nested functions to be placed below the "outer" function.)
that is impossible in all known languages, all procedures/functions must be declared and compiled before they are used so above is the only appropriate place.

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 796
    • Burdjia
Re: Why Pascal?
« Reply #39 on: May 02, 2018, 01:39:30 pm »
About nested procedures/functions:  C allows them.  Or they allowed in ANSI, not sure if C-99 and after allowed (I'm sure old GCC compilers do and some other don't, as Microsoft).  They work(ed) in a very similar way than Pascal ones.  I'm not sure if the actual language definitions allows them (I read somewere it is impossible to build a BNF depiction of C), and AFAIK it is not "standard".  I used them only once (in an old version of Allegro.pas, when an extra DLL was needed) and somebody asked because he can't compile it (Microsoft compiler).
« Last Edit: May 02, 2018, 01:42:45 pm by Ñuño_Martínez »
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6403
Re: Why Pascal?
« Reply #40 on: May 02, 2018, 03:26:08 pm »
Afaik originally it was a gcc extension (maybe meanwhile standarized), but is syntax only. Afaik gcc doesn't implement children touching the parent variables or parameters.

Without that, it is fairly superficial

Leledumbo

  • Hero Member
  • *****
  • Posts: 7936
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Why Pascal?
« Reply #41 on: May 02, 2018, 04:27:51 pm »
Nope, it's not (yet?) standardized in C, not in C99, not even in C11. It remains a GCC specific extension as Clang also doesn't implement it.

Ñuño_Martínez

  • Hero Member
  • *****
  • Posts: 796
    • Burdjia
Re: Why Pascal?
« Reply #42 on: May 02, 2018, 05:44:23 pm »
Afaik originally it was a gcc extension (maybe meanwhile standarized), but is syntax only. Afaik gcc doesn't implement children touching the parent variables or parameters.
May be not now, but it did allow to use parent variables and parameters.  I can't find the code right now (I've found it wasn't in the old Allegro.pas code %)) but I did it and it worked.

Anyway, as you said, it isn't standard so it is not a good idea to do it.  As I've said somebody said it didn't compiled in his system.
Are you interested in game programming? Join the Pascal Game Development community!
Also visit the Game Development Portal

metis

  • Full Member
  • ***
  • Posts: 122
Re: Why Pascal?
« Reply #43 on: May 08, 2018, 12:56:23 pm »
@Zoran
Quote
"Don't declare variable outside of exact block they belong to" is what I really miss in Pascal.
How would You declare global Vars then ?

@All the others
What I really miss in Pascal, is the Possibility to declare static Vars within a Routine. :(

Maybe this is a good Opportunity to turn the Discussion to...
What is missing in Pascal ?
What can not be done with Pascal without "big Detours" ?

 :)
« Last Edit: May 08, 2018, 12:58:35 pm by metis »
Life could be so easy, if there weren't those f*** Details.

Handoko

  • Hero Member
  • *****
  • Posts: 2443
  • My goal: build my own game engine using Lazarus
Re: Why Pascal?
« Reply #44 on: May 08, 2018, 01:01:33 pm »
What I really miss in Pascal, is the Possibility to declare static Vars within a Routine. :(

Did you mean local typed constants?

It should be stressed that typed constants are automatically initialized at program start. This is also true for local typed constants and initialized variables. Local typed constants are also initialized at program start. If their value was changed during previous invocations of the function, they will retain their changed value, i.e. they are not initialized each time the function is invoked.

Source: https://freepascal.org/docs-html/ref/refse10.html

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus