• FOR loops in Oberon

    From john.perry@usm.edu@21:1/5 to All on Tue May 22 15:18:02 2018
    Hello!

    I'm playing with Oberon-07 using the obnc and oberonc compilers (which are great BTW). Today I hit on a surprise: I was sure that Oberon’s FOR loop behaved like that of Pascal, Modula-2, and Ada. That is, I thought the following should print the
    numbers from 1 to 20:

    i := 20;
    FOR j := 1 TO i DO
    Out.Int(j, 0); Out.Ln;
    i := i - 1;
    END;

    This is admittedly dumb code, but never mind that. I just checked this with freepascal, Gnu Modula-2, and gnat, and all three produce code that prints all the numbers from 1 to 20. I guess this is because the value of i is evaluated at the beginning of
    the loop, then stored for future tests. This can be more efficient in general, especially if the test involves a complicated function.

    In C, on the other hand,it prints the numbers from 1 to 10, because C would evaluate the value of i on each pass through the loop.

    It turns out that in code produced by obnc and oberonc, the FOR loop behaves like C, and NOT like the other Pascal-derived languages I mentioned.

    I was sure this was a bug: why would Wirth change the semantics of a FOR loop? I went to look in “Programming in Oberon,” and the semantics are explained on p. 21:

    It is recommended that the for statement be used in simple cases only;
    in particular, no components of the expressions determining the range
    must be affected by the repeated statements, and, above all, the
    control variable itself must not be changed by the repeated statements.
    The value of the control variable must be considered as undefined after
    the for statement is terminated.

    Again, I concede that the loop above is bad code. Still, I’m curious:

    1) Is it an error, because I modify i, violating “no components of the expressions determining the range must be affected”?

    2) If so, should the compiler report an error?

    3) If not, should the compiler produce code similar to Pascal et al., or identical to C?

    I realize there's a workaround. I just want to know what the language lawyers think of it.

    sincerely
    john perry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to john.perry@usm.edu on Wed May 23 06:59:11 2018
    On 2018-05-23 00:18, john.perry@usm.edu wrote:
    Hello!

    I'm playing with Oberon-07 using the obnc and oberonc compilers (which are great BTW). Today I hit on a surprise: I was sure that Oberon’s FOR loop behaved like that of Pascal, Modula-2, and Ada. That is, I thought the following should print the
    numbers from 1 to 20:

    i := 20;
    FOR j := 1 TO i DO
    Out.Int(j, 0); Out.Ln;
    i := i - 1;
    END;

    If you read section 9.8. in the language report you will find that the
    above statement sequence is equivalent to

    i := 20;
    j := 1;
    WHILE j <= i DO
    Out.Int(j, 0);
    Out.Ln;
    i := i - 1;
    j := j + 1
    END

    http://miasap.se/obnc/oberon-report.html#sec9.8


    Regards,

    August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Diego Sardina@21:1/5 to All on Tue May 22 21:47:19 2018
    In Oberon-07 the FOR statement is just a syntactic sugar of the WHILE statement in the case all elements are to be processed.

    It's currently defined as this (assuming inc > 0):

    FOR v := beg TO end BY inc DO S END

    v := beg;
    WHILE v <= end DO S; v := v + inc END

    As you see, the "end" expression is not copied and it's evaluated in every repetition.

    However, in Oberon-07 reports released in 2007, 2011 and 2013 the definition was taken from Oberon-2 as is (that was slightly different from the actual).

    v := beg; lim := end;
    WHILE v <= lim DO S; v := v + inc END

    The end condition is copied to a new variable and the WHILE is tested against that variable. That means the condition after TO is not evaluated in every repetition and is considered constant.

    In my opinion, this one is the correct behaviour.

    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.


    --
    Diego Sardina

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to Diego Sardina on Wed May 23 06:11:11 2018
    On Tuesday, May 22, 2018 at 11:47:19 PM UTC-5, Diego Sardina wrote:
    However, in Oberon-07 reports released in 2007, 2011 and 2013 the definition was taken from Oberon-2 as is (that was slightly different from the actual).

    v := beg; lim := end;
    WHILE v <= lim DO S; v := v + inc END

    The end condition is copied to a new variable and the WHILE is tested against that variable. That means the condition after TO is not evaluated in every repetition and is considered constant.

    In my opinion, this one is the correct behaviour.

    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    That's very interesting. Thank you for pointing that out!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to August Karlstrom on Wed May 23 06:10:15 2018
    On Tuesday, May 22, 2018 at 11:59:13 PM UTC-5, August Karlstrom wrote:
    If you read section 9.8. in the language report you will find that the
    above statement sequence is equivalent to...

    i := 20;
    j := 1;
    WHILE j <= i DO
    Out.Int(j, 0);
    Out.Ln;
    i := i - 1;
    j := j + 1
    END

    http://miasap.se/obnc/oberon-report.html#sec9.8

    I see; I was looking at PiO when perhaps I should have been looking at that document, instead. Are you saying that the reason it is not a syntax error is that "Programming in Oberon" is not as official as the language report?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to john.perry@usm.edu on Wed May 23 17:01:08 2018
    On 2018-05-23 15:10, john.perry@usm.edu wrote:
    On Tuesday, May 22, 2018 at 11:59:13 PM UTC-5, August Karlstrom wrote:
    If you read section 9.8. in the language report you will find that the
    above statement sequence is equivalent to...

    i := 20;
    j := 1;
    WHILE j <= i DO
    Out.Int(j, 0);
    Out.Ln;
    i := i - 1;
    j := j + 1
    END

    http://miasap.se/obnc/oberon-report.html#sec9.8

    I see; I was looking at PiO when perhaps I should have been looking at that document, instead. Are you saying that the reason it is not a syntax error is that "Programming in Oberon" is not as official as the language report?

    The language report is of course the reference. There may be a few
    places where Wirth has forgot to update PiO in accordance with the
    latest revision of Oberon.

    -- August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to August Karlstrom on Wed May 23 09:06:55 2018
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote:
    On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END

    is that it's left unspecified whether the expression `end' is evaluated
    once or each time the guard is evaluated. In that case the compiler implementor can choose one of the two evaluation strategies and the the programmer should not rely on any of them.

    In 9.6, Wirth writes, "The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE."

    The phrase, "the expression evaluation ...[is] repeated" would suggest to me that "end" also has to be evaluated.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to john....@usm.edu on Wed May 23 09:07:28 2018
    On Wednesday, May 23, 2018 at 11:06:56 AM UTC-5, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote:
    On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END

    is that it's left unspecified whether the expression `end' is evaluated once or each time the guard is evaluated. In that case the compiler implementor can choose one of the two evaluation strategies and the the programmer should not rely on any of them.

    In 9.6, Wirth writes, "The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE."

    The phrase, "the expression evaluation ...[is] repeated" would suggest to me that "end" also has to be evaluated.

    Sorry, I forgot to say something: this is especially true if "end" is a procedure, rather than a variable!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to Diego Sardina on Wed May 23 17:25:16 2018
    On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END

    is that it's left unspecified whether the expression `end' is evaluated
    once or each time the guard is evaluated. In that case the compiler
    implementor can choose one of the two evaluation strategies and the the programmer should not rely on any of them.

    -- August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to john.perry@usm.edu on Wed May 23 20:29:54 2018
    On 2018-05-23 18:06, john.perry@usm.edu wrote:
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote:
    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END

    is that it's left unspecified whether the expression `end' is evaluated
    once or each time the guard is evaluated. In that case the compiler
    implementor can choose one of the two evaluation strategies and the the
    programmer should not rely on any of them.

    In 9.6, Wirth writes, "The expression evaluation and the statement execution are repeated until none of the Boolean expressions yields TRUE."

    The phrase, "the expression evaluation ...[is] repeated" would suggest to me that "end" also has to be evaluated.

    "The expression" refers to the loop guard as a whole, not the limit
    expression.

    -- August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to August Karlstrom on Wed May 23 11:45:48 2018
    On Wednesday, May 23, 2018 at 1:29:55 PM UTC-5, August Karlstrom wrote:
    "The expression" refers to the loop guard as a whole, not the limit expression.

    Even in light of this example from page 13 of "Programming in Oberon," section 6.1?

    A simple rule is to avoid expressions within repetitive statements, in which no variable changes its value. For example, the statement

    WHILE i < 3*N DO tab[i] := x + y*z + z*i; i := i+1 END

    should be formulated more effectively as

    n := 3*N; u := x + y*z;
    WHILE i < n DO tab[i] := u + z*i; i := i+1 END

    I realize it isn't the language definition, but in cases where that is ambiguous this ought to carry *some* weight.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From cfbsoftware@gmail.com@21:1/5 to john....@usm.edu on Wed May 23 15:28:08 2018
    On Thursday, May 24, 2018 at 1:37:29 AM UTC+9:30, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 11:06:56 AM UTC-5, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote:
    On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END



    Sorry, I forgot to say something: this is especially true if "end" is a procedure, rather than a variable!

    Incorrect. In this example "end" could not be a procedure. If it was, in Oberon-07 it would have to be written as:

    WHILE v <= end() DO S; v := v + inc END

    Refer to Section 10.1 Formal Parameters.

    Regards,
    Chris Burrows
    CFB Software
    http://www.astrobe.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to cfbso...@gmail.com on Wed May 23 15:37:53 2018
    On Wednesday, May 23, 2018 at 5:28:09 PM UTC-5, cfbso...@gmail.com wrote:
    On Thursday, May 24, 2018 at 1:37:29 AM UTC+9:30, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 11:06:56 AM UTC-5, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote:
    On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END



    Sorry, I forgot to say something: this is especially true if "end" is a procedure, rather than a variable!

    Incorrect. In this example "end" could not be a procedure. If it was, in Oberon-07 it would have to be written as:

    WHILE v <= end() DO S; v := v + inc END

    Refer to Section 10.1 Formal Parameters.

    Sorry; you're right, I knew that (in principle, anyway). I don't see how that changes my point, though; I can't imagine that one would want to evaluate end() only on the first pass through a while loop, but that's what the proposed alternate reading
    suggests.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luca Boasso@21:1/5 to All on Wed May 23 19:41:18 2018
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.


    --
    Diego Sardina

    While developing the oberonc compiler, I remember asking Prof. Wirth why his implementation of the FOR loop was re-evaluating the "end" expression at each iteration.
    In his reply he mentioned that he prefers to avoid the problem of allocating a temporary variable. Indeed, if you look at his latest compiler's source code, there is no other place where a temporary variable must be reserved in the stack frame (I don't
    consider the save/restore of registers at function calls as temporary variables).
    I think this is another example of "make it as simple as possible, but not simpler", the compiler avoid extra complexity, and the end user can always introduce manually the temporary variable in his source code, making his choice explicit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From cfbsoftware@gmail.com@21:1/5 to Luca Boasso on Wed May 23 21:00:43 2018
    On Thursday, May 24, 2018 at 12:11:19 PM UTC+9:30, Luca Boasso wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.


    --
    Diego Sardina

    While developing the oberonc compiler, I remember asking Prof. Wirth why his implementation of the FOR loop was re-evaluating the "end" expression at each iteration.
    In his reply he mentioned that he prefers to avoid the problem of allocating a temporary variable. Indeed, if you look at his latest compiler's source code, there is no other place where a temporary variable must be reserved in the stack frame (I don't
    consider the save/restore of registers at function calls as temporary variables).
    I think this is another example of "make it as simple as possible, but not simpler", the compiler avoid extra complexity, and the end user can always introduce manually the temporary variable in his source code, making his choice explicit.

    I agree - the simpler the definition the less confusing it is to use. It eliminates the seemingly arbitrary conditions associated with FOR loops in other languages. e.g. the control variable can only be a local, the value of the control variable is
    undefined after the loop has exited etc.
    etc.

    It should be noted that Wirth initially removed FOR loops in the transition from Modula-2 to Oberon. They were only reinstated some time later (with Oberon-2?).

    I find them useful for the really simple loops e.g. initialising an array:

    FOR i := 0 TO LEN(a) - 1 DO a[i] := -1 END;

    In other cases a WHILE or REPEAT loop is usually a more appropriate choice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luca Boasso@21:1/5 to All on Wed May 23 21:18:36 2018
    The only thing that I find less convenient is that the range of the control variable goes all the way to the "end" value included: [begin, end]
    For example, I would prefer to write

    FOR i := 0 TO LEN(a) DO a[i] := -1 END;

    where i is in the interval [0, LEN(a)) instead of

    FOR i := 0 TO LEN(a)-1 DO a[i] := -1 END;

    where i is in the interval [0, LEN(a)-1].

    But this is just a matter of taste.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Diego Sardina@21:1/5 to Luca Boasso on Wed May 23 21:57:46 2018
    On Thursday, May 24, 2018 at 6:18:37 AM UTC+2, Luca Boasso wrote:
    The only thing that I find less convenient is that the range of the control variable goes all the way to the "end" value included: [begin, end]
    For example, I would prefer to write

    FOR i := 0 TO LEN(a) DO a[i] := -1 END;

    where i is in the interval [0, LEN(a)) instead of

    FOR i := 0 TO LEN(a)-1 DO a[i] := -1 END;

    where i is in the interval [0, LEN(a)-1].

    But this is just a matter of taste.

    Indeed the FOR-WHILE equivalence was offered to give a precise behavior of the FOR statement, since it's not clear if it may include the right limit or not and what value the control variable assumes at the end of repetitions.

    This solved the problem that the FOR statement had different behaviors among Modula-2 implementations.

    Anyway I agree with you (in Oberon), in Modula-2 this definition was less unpleasant since you had LOW(a) and HIGH(a). However I think that this definition is what makes more sense since we are dealing with (ranges of) integers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to cfbsoftware@gmail.com on Thu May 24 10:38:30 2018
    On 2018-05-24 00:28, cfbsoftware@gmail.com wrote:
    On Thursday, May 24, 2018 at 1:37:29 AM UTC+9:30, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 11:06:56 AM UTC-5, john....@usm.edu wrote:
    On Wednesday, May 23, 2018 at 10:25:19 AM UTC-5, August Karlstrom wrote: >>>> On 2018-05-23 06:47, Diego Sardina wrote:
    I don't know why in the latest report Prof. Wirth changed the FOR definition. This happened during 2016, IIRC.

    One way to interpret

    v := beg;
    WHILE v <= end DO S; v := v + inc END



    Sorry, I forgot to say something: this is especially true if "end" is a procedure, rather than a variable!

    Incorrect. In this example "end" could not be a procedure. If it was, in Oberon-07 it would have to be written as:

    WHILE v <= end() DO S; v := v + inc END

    Then I'm probably wrong as I thought of `beg', `end' and `inc' as meta-variables representing expressions.


    -- August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From cfbsoftware@gmail.com@21:1/5 to cfbso...@gmail.com on Thu May 24 04:12:52 2018
    On Thursday, May 24, 2018 at 1:30:43 PM UTC+9:30, cfbso...@gmail.com wrote:

    It should be noted that Wirth initially removed FOR loops in the transition from Modula-2 to Oberon.

    The reason given at the time was:

    "The elimination of the for statement constitutes a break with another long standing tradition. The baroque mechanism of Algol 60’s for statement had been trimmed significantly in Pascal (and Modula). Its marginal value in practice has led to its
    absence from Oberon."

    From Modula to Oberon (ModToOberon2.Doc / NW 1.10.90)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From cfbsoftware@gmail.com@21:1/5 to cfbso...@gmail.com on Thu May 24 04:16:54 2018
    On Thursday, May 24, 2018 at 8:42:52 PM UTC+9:30, cfbso...@gmail.com wrote:
    On Thursday, May 24, 2018 at 1:30:43 PM UTC+9:30, cfbso...@gmail.com wrote:

    It should be noted that Wirth initially removed FOR loops in the transition from Modula-2 to Oberon.

    The reason given at the time was:

    "The elimination of the for statement constitutes a break with another long standing tradition. The baroque mechanism of Algol 60’s for statement had been trimmed significantly in Pascal (and Modula). Its marginal value in practice has led to its
    absence from Oberon."

    From Modula to Oberon (ModToOberon2.Doc / NW 1.10.90)

    And then when Oberon-2 was introduced as an extension of Oberon:

    "Although for statements can always be expressed by while statements, they are sometimes more convenient because they are shorter and termination is inherent. This is the case if the number of iterations is fixed like in many
    applications dealing with arrays."

    Differences between Oberon and Oberon–2. H. Mössenböck, N. Wirth Oberon2.Differences.Text (20 Jul 93)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to cfbso...@gmail.com on Thu May 24 04:57:07 2018
    On Thursday, May 24, 2018 at 6:16:55 AM UTC-5, cfbso...@gmail.com wrote:
    "Although for statements can always be expressed by while statements, they are sometimes more convenient because they are shorter and termination is inherent.

    This is probably no longer true in Oberon-07:

    n := 2;
    FOR i := 1 TO n DO n := n + 1; END;
    Out.Int(i, 0); Out.Ln;

    Output is -2147483648 with both obnc and oberonc -- probably not what was expected! Now imagine that the end condition is not a variable n, but a procedure that returns a boolean and depends on n.

    john perry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to john....@usm.edu on Thu May 24 07:28:40 2018
    On Thursday, May 24, 2018 at 9:27:11 AM UTC-5, john....@usm.edu wrote:
    On Thursday, May 24, 2018 at 6:57:08 AM UTC-5, john....@usm.edu wrote:
    Now imagine that the end condition is not a variable n, but a procedure that returns a boolean...

    *sigh* "boolean" -> "integer"

    ...and even then my suggestion is probably wrong, because eventually i becomes the largest possible integer on the system. So the FOR loop really terminates.

    Sorry for the noise.

    john perry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to john....@usm.edu on Thu May 24 07:27:10 2018
    On Thursday, May 24, 2018 at 6:57:08 AM UTC-5, john....@usm.edu wrote:
    Now imagine that the end condition is not a variable n, but a procedure that returns a boolean...

    *sigh* "boolean" -> "integer"

    john perry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pascal J. Bourguignon@21:1/5 to john.perry@usm.edu on Thu May 24 20:43:42 2018
    john.perry@usm.edu writes:

    Hello!

    I'm playing with Oberon-07 using the obnc and oberonc compilers (which
    are great BTW). Today I hit on a surprise: I was sure that Oberon’s
    FOR loop behaved like that of Pascal, Modula-2, and Ada. That is, I
    thought the following should print the numbers from 1 to 20:

    i := 20;
    FOR j := 1 TO i DO
    Out.Int(j, 0); Out.Ln;
    i := i - 1;
    END;

    This is admittedly dumb code, but never mind that. I just checked this
    with freepascal, Gnu Modula-2, and gnat, and all three produce code
    that prints all the numbers from 1 to 20. I guess this is because the
    value of i is evaluated at the beginning of the loop, then stored for
    future tests. This can be more efficient in general, especially if the
    test involves a complicated function.

    In C, on the other hand,it prints the numbers from 1 to 10, because C
    would evaluate the value of i on each pass through the loop.

    It turns out that in code produced by obnc and oberonc, the FOR loop
    behaves like C, and NOT like the other Pascal-derived languages I
    mentioned.

    I was sure this was a bug: why would Wirth change the semantics of a
    FOR loop? I went to look in “Programming in Oberon,” and the semantics are explained on p. 21:

    It is recommended that the for statement be used in simple cases only;
    in particular, no components of the expressions determining the range
    must be affected by the repeated statements, and, above all, the
    control variable itself must not be changed by the repeated statements.
    The value of the control variable must be considered as undefined after
    the for statement is terminated.

    Again, I concede that the loop above is bad code. Still, I’m curious:

    1) Is it an error, because I modify i, violating “no components of the expressions determining the range must be affected”?

    2) If so, should the compiler report an error?

    3) If not, should the compiler produce code similar to Pascal et al.,
    or identical to C?

    I realize there's a workaround. I just want to know what the language
    lawyers think of it.

    I would have an orthogonal point of view.

    1- both options are equally useful (evaluating the limit once, or
    evaluating it on each iteration). The proof is that you have
    languages with either semantics that are surviving equally well
    during decades.

    2- Even if Wirth was God on Earth, his opinion and his specification of
    the language may be inconvenient for your own programs. It is
    definitely very inconvenient to have to way years on him (or other
    benevolent dictators, or on commitees for commitee-defined languages)
    to decide for a change of semantics, or a new language feature.

    3- even if there are "work arounds", they usually involve boiler
    plate. This is never addressed satisfactorily or at all by those
    languages.

    This is quite surprising, given that since 1964, we know how to design programming languages so that instead of bearing all those problems, you
    can decide for yourself the language features and semantics you want,
    and you can implement them yourself, trivially.


    (defmacro for/re-evaluate-the-limit ((var start limit) &body body)
    (let ((vvar (gensym "VAR-")))
    `(prog ((,vvar ,start))
    loop
    (if (> ,vvar ,limit)
    (go end))
    (let ((,var ,vvar))
    ,@body)
    (incf ,vvar)
    (go loop)
    end)))


    (defmacro for/evaluate-the-limit-once ((var start limit) &body body)
    (let ((vvar (gensym "VAR-"))
    (vlimit (gensym "LIMIT-")))
    `(prog ((,vvar ,start)
    (,vlimit ,limit))
    loop
    (if (> ,vvar ,vlimit)
    (go end))
    (let ((,var ,vvar))
    ,@body)
    (incf ,vvar)
    (go loop)
    end)))


    (let ((i 20))
    (for/re-evaluate-the-limit (j 0 i)
    (format t "~A " j)
    (decf i)
    (setf j 42))
    (format t "~%"))
    ;; prints: 0 1 2 3 4 5 6 7 8 9 10


    (let ((i 20))
    (for/evaluate-the-limit-once (j 0 i)
    (format t "~A " j)
    (decf i)
    (setf j 42))
    (format t "~%"))
    ;; prints: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20


    I spent a long time in my youth with Pascal, Modual-2, hoping from one
    language to the other in search of the perfect programming language.
    There's no such thing.
    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    --
    __Pascal J. Bourguignon
    http://www.informatimago.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From john.perry@usm.edu@21:1/5 to Pascal J. Bourguignon on Thu May 24 18:41:05 2018
    On Thursday, May 24, 2018 at 1:43:45 PM UTC-5, Pascal J. Bourguignon wrote:
    I spent a long time in my youth with Pascal, Modual-2, hoping from one language to the other in search of the perfect programming language.
    There's no such thing.

    No disagreement there.

    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    I didn't know you could do this in LISP, but I was under the impression that you could do something like this with the most recent Perl (not sure; may have misread something). I immediately wondered, doesn't that run the risk of making code harder to
    read, if everyone can write his own "dialect"? Perhaps I just don't know enough about this sort of thing.

    john perry

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From August Karlstrom@21:1/5 to Pascal J. Bourguignon on Fri May 25 10:35:00 2018
    On 2018-05-24 20:43, Pascal J. Bourguignon wrote:
    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    I guess the price you pay for that is an added effort required to
    understand a program written in such an ad hoc language. Also, if I'm
    not mistaken, Common Lisp is not a system programming language. Nor is
    it as efficient as compiled Oberon code.

    -- August

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pascal J. Bourguignon@21:1/5 to August Karlstrom on Fri May 25 19:53:46 2018
    August Karlstrom <fusionfile@gmail.com> writes:

    On 2018-05-24 20:43, Pascal J. Bourguignon wrote:
    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    I guess the price you pay for that is an added effort required to
    understand a program written in such an ad hoc language.

    I guess this is why there are actually several operating systems written
    in lisp and in Common Lisp :-)

    https://common-lisp.net/project/movitz/movitz.html

    A more recent example, is this OS that includes even a GUI: https://github.com/froggey/Mezzano

    And of course, there were the Lisp Machines entirely written in lisp
    (but pre- Common Lisp). https://www.youtube.com/watch?v=o4-YnLpLgtk
    Oberon seems to be very much inspired from them ;-)


    You should probably forget this argument, nowadays people write
    Operating Systems in Java Script running in Web browsers!


    Also, if I'm not mistaken, Common Lisp is not a system programming
    language. Nor is it as efficient as compiled Oberon code.

    Some CL compilers can generate code even more efficient than some gcc compilers. I don't know if Oberon compilers have enough resources
    invested in them to generate that efficient code: the speed of the code generated by a compiler doesn't depend on the programming language, but
    on the money invested in developping the compiler.

    https://www.cliki.net/performance

    --
    __Pascal J. Bourguignon
    http://www.informatimago.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pascal J. Bourguignon@21:1/5 to john.perry@usm.edu on Fri May 25 19:47:01 2018
    john.perry@usm.edu writes:

    On Thursday, May 24, 2018 at 1:43:45 PM UTC-5, Pascal J. Bourguignon wrote:
    I spent a long time in my youth with Pascal, Modual-2, hoping from one
    language to the other in search of the perfect programming language.
    There's no such thing.

    No disagreement there.

    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    I didn't know you could do this in LISP, but I was under the
    impression that you could do something like this with the most recent
    Perl (not sure; may have misread something).

    Yes, it's also possible to do something like that in perl.
    Also, to some extend, using the C pre-processor, or any other
    pre-processor, such as m4.

    The main difference between lisp and the other meta-programming systems,
    is that lisp source is not the text stored in the source files, but the
    data structure that is "deserialised" from this text, ie. the symbolic expressions (or S-exp). Theres no parenthesis in the S-exps, only
    atoms and lists of atoms or S-exps. Processing this tree-like data
    structure is easy and much less error-prone than processing text-level
    sources. The risk of evil code-injection is basically excluded with you
    are processing S-exps to generate code.


    I immediately wondered,
    doesn't that run the risk of making code harder to read, if everyone
    can write his own "dialect"? Perhaps I just don't know enough about
    this sort of thing.

    Not really.

    First, notice that you can write your own "dialect" by using the
    standard features of programming language, such as the mere definition
    of functions (and you can have even more "fun" with methods and OO).

    So theorically, you could indeed write your own dialect or several
    dialects inside the same software. In a way, lisp (or any
    meta-programming programming language) would be the ultimate tool in
    obfuscated code contests. This is the reason why there is no obfuscated
    lisp code contest! Way to easy. (perl is obfuscated by default, you
    have to work hard to write non-obfuscated perl programs; it's the
    epitome of line-noise code). Therefore what happens, is that lisp
    programmers are very measured in their use of macros and the definition
    of dialects. The first thing we teach to newbies about macros, is not
    to write one! (if it can be written as a function).

    So you have to be careful not to abuse the power of lisp and its macros
    (just like you would avoid abusing the power of C and the tricks you can
    do with it). Knowing the eco-system and the culture helps, since over
    the time (60 years), some conventions and good practices have emerged
    that helps writing code that doesn't break expectations, and that helps
    reading the code, even with a lot of "unknown" operators, while still understanding what it does without having to read the source of the
    macros of functions used.

    In this respect, the naming of the operators (and other objects of the language), and the corresponding naming conventions, help a lot, when respected, and may obfuscate or render the code quite unmaintainable
    when broken. This being independent of the programming language. I'm currently working on some C code, where some functions are named in
    quite misleading ways, leading you believe they do something, when in
    fact, they do something else barely related to their name!

    However, when the problem you have to solve requires a different
    paradigm than the usual procedural programming paradigm, you can easily
    shape lisp into the paradigm that is best suited to your application.
    And you can do it while still integrating nicely with lisp, and
    therefore with all the lisp libraries that can themselves be written
    using different paradigms. Lisp already includes features supporting
    the functional paradigm (it's not a purely functional programming
    language like Haskell however). Common Lisp already includes one of the
    nicest (and the first having been standardized) Object Oriented system
    (CLOS), including a Meta Object Protocol (MOP), to let you define your
    own Object System if the one provided by CL wasn't good enough. (Which
    happens some times. For example, if you wanted to interface with C++,
    which has a very different "Object System", you could define C++-like
    classes and metaclasses with the MOP. Other extensions include for
    example OODBMS (Object Oriented Database Base Management System).

    And finally, YOLO and life is too short to write gratuituous "dialects":
    if you have an operator already doing what you want, there's very little
    reason to spend time writing your own variant. So this is rarely done.
    For example, in the case of our two loop variants, this is already
    covered by the LOOP macros provided by CL (which is a whole programming language in itself, its own iterative paradigms!), so you would never
    actually define the macros I defined previously, but in newsgroups or as teaching examples. ;-)

    In normal programs, you'd write:

    (loop :with i := 20
    :for j :from 0
    :while (< j i)
    :do (format t "~A " j)
    (decf i)
    :finally (format t "~%"))

    ;; prints: 0 1 2 3 4 5 6 7 8 9
    ;; returns: nil

    (loop :with i := 20
    :for j :below i
    :do (format t "~A " j)
    (decf i)
    :finally (format t "~%"))

    ;; prints: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
    ;; returns: nil


    Some argue that loop is not very lispy, since it introduces a syntax
    very different from the usual S-exps, so they developped an iterative
    package that allows you to express iterations as sophisticated as LOOP or
    even more, but using S-exps to define the internal clauses; this is a
    good example of users of the programming language disagreeing with the
    commitee who defined it, where the users could easily define their own
    language constructs.

    But again, in a sense, any library is kind of defining your own "dialect".

    --
    __Pascal J. Bourguignon
    http://www.informatimago.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pascal J. Bourguignon@21:1/5 to August Karlstrom on Fri May 25 20:03:08 2018
    August Karlstrom <fusionfile@gmail.com> writes:

    On 2018-05-24 20:43, Pascal J. Bourguignon wrote:
    This is why you need a meta-programming language, such as Common Lisp,
    where you can shape the language to your own needs and will, right on
    the spot, when you have a problem to solve that calls for it.

    I guess the price you pay for that is an added effort required to
    understand a program written in such an ad hoc language. Also, if I'm
    not mistaken, Common Lisp is not a system programming language. Nor is
    it as efficient as compiled Oberon code.

    I should add, that while I totally agree with Wirth's idea of defining
    a programming language so that you can easily write a simple compiler
    that is efficient (or efficient enough) to do serious work with it,
    writing such a compiler, or even modifying it to add your own feature,
    is in general beyond the average programmer or beyond the time allocated
    to any programmer (ten-x or normal) by the managers.

    So I want to stress that when you extend lisp by writing macros (and in
    general meta-programming functions), you do it relying on the existing
    lisp compiler: instead of genering binary code, you generate lisp code,
    and let the existing compiler compile and optimize your generated lisp
    code.

    Doing the equivalent with other languages, such as Oberon or C, implies
    writing a pre-processor (which requires implementing at least a lexer
    and scanner, which again, is not within the reach of most programmers (unfortunately), or worse, involves hacks (perhaps using regexps!)).
    And we know where this path leads: to Objective-C, or worse, to C++!
    In any case, this is way more complex than writing a couple of lisp
    macros.

    The reason why lisp is very sparse in means, is of course because it was developped on hardware 60 years ago: they didn't have the CPU cycles and
    the memory to waste, so a lisp system is fast, by default, on current
    hardware. (C, Pascal, Modula-2, Oberon benefit of this too of course,
    but they don't have meta-programming features included).


    The secret is that macros are normal lisp functions, but hooked into the compiler. They are compiler plug-ins, in a well structured way,
    integrating perfectly in the syntax of the language (the S-exps) and its semantics.

    --
    __Pascal J. Bourguignon
    http://www.informatimago.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard@21:1/5 to Luca Boasso on Fri May 25 22:07:15 2018
    On 2018-05-24 04:41, Luca Boasso wrote:

    I think this is another example of "make it as simple as possible, but not simpler", the compiler avoid extra complexity, and the end user can always introduce manually the temporary variable in his source code, making his choice explicit.

    Also, the generated code can actually be more efficient, because if the
    end expression is a constant or a variable in a register, no assignment
    to a a temporary variable is necessary.

    Richard

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Pascal J. Bourguignon@21:1/5 to Richard on Sat May 26 02:47:55 2018
    Richard <portempa@aon.at> writes:

    On 2018-05-24 04:41, Luca Boasso wrote:

    I think this is another example of "make it as simple as possible,
    but not simpler", the compiler avoid extra complexity, and the end
    user can always introduce manually the temporary variable in his
    source code, making his choice explicit.

    Also, the generated code can actually be more efficient, because if the
    end expression is a constant or a variable in a register, no assignment
    to a a temporary variable is necessary.

    Indeed, optimizing compilers can really seem magical. There are some interesting examples in the case of C compilers, where writing the most
    plain and direct source code lead to actually more efficient native
    code, than when you try to "optimize" at the source level.

    This is why using such compilers as backend for most programming
    languages is a reasonable option.

    If you want to make a valid comparison, you should compare the
    optimization algorithms and trick implemented in the different compiler backends, and not compare them on the source language basis.

    --
    __Pascal J. Bourguignon
    http://www.informatimago.com

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)