• Recursion, Yo

    From Lawrence D'Oliveiro@3:633/280.2 to All on Sun Apr 7 12:58:23 2024
    Some homies they be sayin’ recursion is Teh Evulz. Well, check this
    out. This program be recursing to the max, yo.

    Peace.

    ----
    /*
    Generate permutations of a list of items.
    Pass a list of arbitary words as command arguments, and this
    program will print out all possible orderings of them.
    */

    #include <iso646.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stdio.h>

    typedef void (*permute_action)
    (
    const char * const * words
    );

    void permute
    (
    unsigned int nrwords,
    const char * const * words,
    permute_action action
    )
    {
    if (nrwords > 0)
    {
    const char ** const permbuf = (const char **)malloc(nrwords * sizeof(char *));
    bool * const used = (bool *)malloc(nrwords);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    used[i] = false;
    } /*for*/

    void permute1
    (
    unsigned int depth
    )
    {
    if (depth < nrwords)
    {
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    if (not used[i])
    {
    permbuf[depth] = words[i];
    used[i] = true;
    permute1(depth + 1);
    used[i] = false;
    } /*if*/
    } /*for*/
    }
    else
    {
    action(permbuf);
    } /*if*/
    } /*permute1*/

    permute1(0);

    free(permbuf);
    free(used);
    } /*if*/
    } /*permute*/

    int main
    (
    int argc,
    char ** argv
    )
    {
    const unsigned int nrwords = argc - 1;
    unsigned int count = 0;

    void collect
    (
    const char * const * words
    )
    {
    count += 1;
    fprintf(stdout, "[%d](", count);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    if (i != 0)
    {
    fputs(", ", stdout);
    } /*if*/
    fputs(words[i], stdout);
    } /*for*/
    fputs(")\n", stdout);
    } /*collect*/

    permute
    (
    /*nrwords =*/ nrwords,
    /*words =*/ (const char * const * )(argv + 1),
    /*permute_action =*/ (permute_action)collect
    );

    fprintf(stdout, "Nr found: %d\n", count);
    } /*main*/

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From fir@3:633/280.2 to All on Sun Apr 7 19:52:29 2024
    Lawrence D'Oliveiro wrote:
    Some homies they be sayin’ recursion is Teh Evulz. Well, check this
    out. This program be recursing to the max, yo.

    Peace.

    ----
    /*
    Generate permutations of a list of items.
    Pass a list of arbitary words as command arguments, and this
    program will print out all possible orderings of them.
    */

    #include <iso646.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stdio.h>

    typedef void (*permute_action)
    (
    const char * const * words
    );

    void permute
    (
    unsigned int nrwords,
    const char * const * words,
    permute_action action
    )
    {
    if (nrwords > 0)
    {
    const char ** const permbuf = (const char **)malloc(nrwords * sizeof(char *));
    bool * const used = (bool *)malloc(nrwords);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    used[i] = false;
    } /*for*/

    void permute1
    (
    unsigned int depth
    )
    {
    if (depth < nrwords)
    {
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    if (not used[i])
    {
    permbuf[depth] = words[i];
    used[i] = true;
    permute1(depth + 1);
    used[i] = false;
    } /*if*/
    } /*for*/
    }
    else
    {
    action(permbuf);
    } /*if*/
    } /*permute1*/

    permute1(0);

    free(permbuf);
    free(used);
    } /*if*/
    } /*permute*/

    int main
    (
    int argc,
    char ** argv
    )
    {
    const unsigned int nrwords = argc - 1;
    unsigned int count = 0;

    void collect
    (
    const char * const * words
    )
    {
    count += 1;
    fprintf(stdout, "[%d](", count);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    if (i != 0)
    {
    fputs(", ", stdout);
    } /*if*/
    fputs(words[i], stdout);
    } /*for*/
    fputs(")\n", stdout);
    } /*collect*/

    permute
    (
    /*nrwords =*/ nrwords,
    /*words =*/ (const char * const * )(argv + 1),
    /*permute_action =*/ (permute_action)collect
    );

    fprintf(stdout, "Nr found: %d\n", count);
    } /*main*/

    okay, there are some class of things that suit good for recursion - and
    its probably good to spot whose they are

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: i2pn2 (i2pn.org) (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 9 18:25:46 2024
    On 07.04.2024 11:52, fir wrote:

    okay, there are some class of things that suit good for recursion - and
    its probably good to spot whose they are

    All iterations. (So, basically all code we typically write with 'for'
    and 'while' loops.) For example. Plus all the things where emulation
    with non-recursive means is cumbersome and complex. (Like operating
    on recursive data structures like trees.)

    Actually, you can formally derive the iterative constructs from [a
    subset of] the recursive ones (by introducing variables and loops).

    It makes a difference where one comes from. If folks learned coding
    from imperative languages they have more problems with a functional
    view (and also with recursion, it seems). Here, with "C", we're more
    used to 'while' loops and variables, I think.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Tue Apr 9 18:43:05 2024
    On Tue, 9 Apr 2024 10:25:46 +0200, Janis Papanagnou wrote:

    If folks learned coding from imperative languages they have more
    problems with a functional view (and also with recursion, it seems).

    One of the first few books I ever read on Comp Sci (while still at school, back when schools didn’t have computers) was “A Comparative Study Of Programming Languages” by Bryan Higman. In one of the chapters, I came across the concept of recursion. I also came across Ackermann’s Function.

    Let’s just say that, after staring into that abyss, nothing about
    recursion could possibly scare me, ever again ...

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Tue Apr 9 18:44:14 2024
    /*
    Generate permutations of a list of items.
    Pass a list of arbitary words as command arguments, and this program
    will print out all possible orderings of them.
    */

    Example run:

    ./permute a b c

    Output:

    [1](a, b, c)
    [2](a, c, b)
    [3](b, a, c)
    [4](b, c, a)
    [5](c, a, b)
    [6](c, b, a)
    Nr found: 6

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Tue Apr 9 20:44:23 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    On 07.04.2024 11:52, fir wrote:

    okay, there are some class of things that suit good for recursion - and
    its probably good to spot whose they are

    All iterations. (So, basically all code we typically write with 'for'
    and 'while' loops.) For example. Plus all the things where emulation
    with non-recursive means is cumbersome and complex. (Like operating
    on recursive data structures like trees.)

    Actually, you can formally derive the iterative constructs from [a
    subset of] the recursive ones (by introducing variables and loops).

    It makes a difference where one comes from. If folks learned coding
    from imperative languages they have more problems with a functional
    view (and also with recursion, it seems). Here, with "C", we're more
    used to 'while' loops and variables, I think.

    The language used is key. I would not say that recursion is a good fit
    for "all iterations" when using C. It's significant (or at least note
    worthy) that the code in the original post was not ISO C and re-writing
    it in ISO C would show up some of the issues involved, though this task
    is still a good fit for an explicitly recursive solution.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Tue Apr 9 22:25:51 2024
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    On 07.04.2024 11:52, fir wrote:

    okay, there are some class of things that suit good for recursion - and
    its probably good to spot whose they are

    All iterations. (So, basically all code we typically write with 'for'
    and 'while' loops.) For example. Plus all the things where emulation
    with non-recursive means is cumbersome and complex. (Like operating
    on recursive data structures like trees.)

    Actually, you can formally derive the iterative constructs from [a
    subset of] the recursive ones (by introducing variables and loops).

    It makes a difference where one comes from. If folks learned coding
    from imperative languages they have more problems with a functional
    view (and also with recursion, it seems). Here, with "C", we're more
    used to 'while' loops and variables, I think.

    The language used is key. I would not say that recursion is a good fit
    for "all iterations" when using C. It's significant (or at least note worthy) that the code in the original post was not ISO C and re-writing
    it in ISO C would show up some of the issues involved, though this task
    is still a good fit for an explicitly recursive solution.

    Though I want to say that for many application in standard C the use of
    a "visitor function" makes the OP's code awkward. Instead, I would
    probably iterate over the permutations like this:

    #include <stdbool.h>
    #include <stdio.h>

    void swap(int *a, int *b)
    {
    int t = *a; *a = *b; *b = t;
    }

    bool next_perm(int n, int *p)
    {
    int i = n - 2;
    while (i >= 0 && p[i] >= p[i+1]) i--;
    // i is now -1 or the largest index with p[i] < p[i+1]
    if (i < 0)
    return false;
    int j = n - 1;
    while (p[j] <= p[i]) j--;
    // j is now the largest index > i with p[j] > p[i]
    swap(&p[i], &p[j]);
    // reverse p[i+1] ... p[n-1]
    int l = i, h = n;
    while (++l < --h)
    swap(&p[l], &p[h]);
    return true;
    }

    int main(int argc, char **argv)
    {
    if (argc > 1) {
    int n = argc - 1, idx[n];
    for (int i = 0; i < n; i++)
    idx[i] = i;
    do for (int i = 0; i < n; i++)
    printf("%s%s", argv[idx[i] + 1], i == n-1 ? "\n" : " ");
    while (next_perm(n, idx));
    }
    }

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 9 22:27:19 2024
    On 09.04.2024 10:43, Lawrence D'Oliveiro wrote:

    One of the first few books I ever read on Comp Sci (while still at school, back when schools didn’t have computers) was “A Comparative Study Of Programming Languages” by Bryan Higman. In one of the chapters, I came across the concept of recursion. I also came across Ackermann’s Function.

    Let’s just say that, after staring into that abyss, nothing about recursion could possibly scare me, ever again ...

    Well, Ackermann is a good example how to scare folks - especially
    if folks implement it straightforward recursively, then running it
    and waiting for termination. :-)

    But there's also less complex algorithms, like Fibonacci, that have
    a bad runtime complexity if implemented in a trivial way. Though that
    depends on how you actually implement it[*] and it's not an inherent
    [bad] property of recursion (as sometimes wrongly assumed).

    Janis

    [*] I once wrote (for an "obfuscated Awk" post) the usual definition
    (with some nasty side-effects, granted), which basically was

    func __(_){return _>2?__(--_)+__(--_):1}

    The (for this thread) noteworthy detail is that you can transform the
    function to

    func ___(_) { return __(_,x^x,x^x^x) }
    func __(_,_x,x_) { return --_?__(_,x_,_x+x_):_x+x_ }

    which is also very fast for larger arguments, while the implementation
    still being within the (non-imperative) recursive functions paradigm.

    PS: I hope *this* scared you at least a bit, since that was one of the
    original purposes of the post. ;-)

    PPS: Rewrite to "C" left as an exercise to the reader.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Wed Apr 10 00:27:03 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    On 09.04.2024 10:43, Lawrence D'Oliveiro wrote:

    One of the first few books I ever read on Comp Sci (while still at school, >> back when schools didn’t have computers) was “A Comparative Study Of
    Programming Languages” by Bryan Higman. In one of the chapters, I came
    across the concept of recursion. I also came across Ackermann’s Function. >>
    Let’s just say that, after staring into that abyss, nothing about
    recursion could possibly scare me, ever again ...

    Well, Ackermann is a good example how to scare folks - especially
    if folks implement it straightforward recursively, then running it
    and waiting for termination. :-)

    Ackemann's function in FORTRAN (which had no recursion in those days)
    used to be a student programming assignment. Not so much scary as a
    good learning exercise.

    But there's also less complex algorithms, like Fibonacci, that have
    a bad runtime complexity if implemented in a trivial way. Though that
    depends on how you actually implement it[*] and it's not an inherent
    [bad] property of recursion (as sometimes wrongly assumed).

    Yes. Haskell's

    fib = 1 : 1 : zipWith (+) fib (tail fib)

    is (in Haskell terms) quite efficient.

    [*] I once wrote (for an "obfuscated Awk" post) the usual definition
    (with some nasty side-effects, granted), which basically was

    func __(_){return _>2?__(--_)+__(--_):1}

    The (for this thread) noteworthy detail is that you can transform the function to

    func ___(_) { return __(_,x^x,x^x^x) }
    func __(_,_x,x_) { return --_?__(_,x_,_x+x_):_x+x_ }

    The trouble with AWK (without gawk's -M) is that functions like this
    just start to give a wrong, but plausibly sized, result. For example
    ___(80) is 23416728348467684 when it should be 23416728348467685.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Wed Apr 10 05:55:08 2024
    On 09.04.2024 16:27, Ben Bacarisse wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    func ___(_) { return __(_,x^x,x^x^x) }
    func __(_,_x,x_) { return --_?__(_,x_,_x+x_):_x+x_ }

    The trouble with AWK (without gawk's -M) is that functions like this
    just start to give a wrong, but plausibly sized, result. For example
    ___(80) is 23416728348467684 when it should be 23416728348467685.

    A general issue with functions and arithmetic producing "too large"
    numbers with languages and systems that don't support them. Yes.

    I'd prefer at least an error indication, though, and not a silent
    overflow.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Wed Apr 10 06:31:32 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 09.04.2024 16:27, Ben Bacarisse wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    func ___(_) { return __(_,x^x,x^x^x) }
    func __(_,_x,x_) { return --_?__(_,x_,_x+x_):_x+x_ }

    The trouble with AWK (without gawk's -M) is that functions like this
    just start to give a wrong, but plausibly sized, result. For example
    ___(80) is 23416728348467684 when it should be 23416728348467685.

    A general issue with functions and arithmetic producing "too large"
    numbers with languages and systems that don't support them. Yes.

    I'd prefer at least an error indication, though, and not a silent
    overflow.

    If I understand correctly, it's not overflow, it's quiet loss of
    precision.

    awk doesn't normally distinguish between integer and floating-point
    numbers, and uses FP to represent integers. A computation that should
    yield 23416728348467685 doesn't overflow, but it quietly yields 23416728348467684. (`gawk -M` supports arbitrary precision integer arithmetic.)

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Wed Apr 10 11:50:38 2024
    On Tue, 09 Apr 2024 11:44:23 +0100, Ben Bacarisse wrote:

    It's significant (or at least note worthy) that the code in the
    original post was not ISO C ...

    Interesting that GCC’s C compiler allows nested routine definitions,
    but the C++ compiler does not.

    and re-writing it in ISO C would show up some of the issues involved
    ...

    Ask and you shall receive ...

    /*
    Generate permutations of a list of items.
    Pass a list of arbitary words as command arguments, and this
    program will print out all possible orderings of them.
    */

    #include <iso646.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stdio.h>

    typedef void (*permute_action)
    (
    unsigned int nrwords,
    const char * const * words,
    void * arg
    );

    struct permute_ctx
    {
    unsigned int nrwords;
    const char * const * words;
    permute_action action;
    void * action_arg;
    const char ** permbuf;
    bool * used;
    } /*permute_ctx*/;

    static void permute1
    (
    struct permute_ctx * ctx,
    unsigned int depth
    )
    {
    if (depth < ctx->nrwords)
    {
    for (unsigned int i = 0; i < ctx->nrwords; ++i)
    {
    if (not ctx->used[i])
    {
    ctx->permbuf[depth] = ctx->words[i];
    ctx->used[i] = true;
    permute1(ctx, depth + 1);
    ctx->used[i] = false;
    } /*if*/
    } /*for*/
    }
    else
    {
    ctx->action(ctx->nrwords, ctx->permbuf, ctx->action_arg);
    } /*if*/
    } /*permute1*/

    void permute
    (
    unsigned int nrwords,
    const char * const * words,
    permute_action action,
    void * action_arg
    )
    {
    if (nrwords > 0)
    {
    struct permute_ctx ctx;
    ctx.nrwords = nrwords;
    ctx.words = words;
    ctx.action = action;
    ctx.action_arg = action_arg;
    ctx.permbuf = (const char **)malloc(nrwords * sizeof(char *));
    ctx.used = (bool *)malloc(nrwords);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    ctx.used[i] = false;
    } /*for*/

    permute1(&ctx, 0);

    free(ctx.permbuf);
    free(ctx.used);
    } /*if*/
    } /*permute*/

    struct main_ctx
    {
    FILE * out;
    unsigned int count;
    } /*main_ctx*/;

    void collect
    (
    unsigned int nrwords,
    const char * const * words,
    struct main_ctx * ctx
    )
    {
    ctx->count += 1;
    fprintf(ctx->out, "[%d](", ctx->count);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    if (i != 0)
    {
    fputs(", ", ctx->out);
    } /*if*/
    fputs(words[i], ctx->out);
    } /*for*/
    fputs(")\n", ctx->out);
    } /*collect*/

    int main
    (
    int argc,
    char ** argv
    )
    {
    struct main_ctx ctx;
    ctx.out = stdout;
    ctx.count = 0;

    permute
    (
    /*nrwords =*/ argc - 1,
    /*words =*/ (const char * const * )(argv + 1),
    /*permute_action =*/ (permute_action)collect,
    /*action_arg =*/ (void *)&ctx
    );

    fprintf(stdout, "Nr found: %d\n", ctx.count);
    } /*main*/

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Chris M. Thomasson@3:633/280.2 to All on Wed Apr 10 13:54:17 2024
    On 4/9/2024 6:50 PM, Lawrence D'Oliveiro wrote:
    On Tue, 09 Apr 2024 11:44:23 +0100, Ben Bacarisse wrote:

    It's significant (or at least note worthy) that the code in the
    original post was not ISO C ...

    Interesting that GCC’s C compiler allows nested routine definitions,
    but the C++ compiler does not.

    and re-writing it in ISO C would show up some of the issues involved
    ...

    Ask and you shall receive ...
    [...]

    Are you an AI?


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Wed Apr 10 16:30:23 2024
    On 09.04.2024 22:31, Keith Thompson wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 09.04.2024 16:27, Ben Bacarisse wrote:

    A general issue with functions and arithmetic producing "too large"
    numbers with languages and systems that don't support them. Yes.

    I'd prefer at least an error indication, though, and not a silent
    overflow.

    If I understand correctly, it's not overflow, it's quiet loss of
    precision.

    Yes, you are right.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Wed Apr 10 17:11:49 2024
    On 10/04/2024 03:50, Lawrence D'Oliveiro wrote:
    On Tue, 09 Apr 2024 11:44:23 +0100, Ben Bacarisse wrote:

    It's significant (or at least note worthy) that the code in the
    original post was not ISO C ...

    Interesting that GCC’s C compiler allows nested routine definitions,
    but the C++ compiler does not.

    It is an old extension, going back to when gcc barely (if at all)
    supported C++. The compiler middle-end had to have support for nested functions for languages like Pascal, Module 2 and Ada (I believe Ada is
    the only one of these that stuck around in gcc mainline, but other
    language front-ends have been made outside the main tree). Someone
    thought it might be a useful feature in C too, and perhaps something
    that would catch on in the standards (several early gcc extensions ended
    up standardised in C99).

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    Nested functions were never as interesting for C++ as you already have
    better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes. And once lambdas joined the
    party in C++11 there was absolutely no reason to have nested functions -
    there is nothing (AFAIK) that you could do with nested functions that
    you can't do at least as well, and often better, with lambdas.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Wed Apr 10 17:52:13 2024
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    That would be true of those other languages that require the feature, too.

    Nested functions were never as interesting for C++ as you already have
    better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes.

    Python does both. Just because you have classes doesn’t mean functions can’t be first-class objects, too.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Wed Apr 10 19:18:12 2024
    On 10/04/2024 09:52, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other
    cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    That would be true of those other languages that require the feature, too.


    There may be other differences in the languages that reduce that effect
    - or it could be a problem there too. I don't know the details.
    (Perhaps those on an Ada newsgroup would know better.)

    The problem with trampolines comes about when you want to take the
    address of the nested function and use that outside of the surrounding function, while you also have variables captured by reference.

    Nested functions were never as interesting for C++ as you already have
    better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes.

    Python does both. Just because you have classes doesn’t mean functions can’t be first-class objects, too.

    True. But Python is a very different language from C++. In Python, not
    only are functions objects in themselves, but so are classes (the
    definition of the classes, rather than just instances). Python is a lot
    more "meta" than C++.

    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    int sum_square(int x, int y) {
    int square(z) {
    return z * z;
    }
    return square(x) + square(y);
    }

    int sum_square(int x, int y) {
    auto square = [](int z) {
    return z * z;
    }
    return square(x) + square(y);
    }



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Wed Apr 10 22:42:14 2024
    On 10/04/2024 10:18, David Brown wrote:
    On 10/04/2024 09:52, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other >>> cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    That would be true of those other languages that require the feature,
    too.


    There may be other differences in the languages that reduce that effect
    - or it could be a problem there too. I don't know the details.
    (Perhaps those on an Ada newsgroup would know better.)

    The problem with trampolines comes about when you want to take the
    address of the nested function and use that outside of the surrounding function, while you also have variables captured by reference.

    Nested functions were never as interesting for C++ as you already have
    better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes.

    Python does both. Just because you have classes doesn’t mean functions
    can’t be first-class objects, too.

    True. But Python is a very different language from C++. In Python, not only are functions objects in themselves, but so are classes (the
    definition of the classes, rather than just instances). Python is a lot more "meta" than C++.

    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    int sum_square(int x, int y) {
    int square(z) { // int z ??
    return z * z;
    }
    return square(x) + square(y);
    }

    That's not an interesting use of a local function! You can move square() outside, and it would still work, putting aside any clashes with
    existing names called 'square'.

    The challenges of local functions are to do with accessing transient
    variables belonging to their enclosing function. Especially when a
    reference to the local function is called from outside the lexical scope
    of the enclosing function, with extra difficulties when the enclosing
    function is no longer active.



    int sum_square(int x, int y) {
    auto square = [](int z) {
    return z * z;
    }
    return square(x) + square(y);
    }

    What's the significance of the '[]' here?


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Wed Apr 10 23:18:18 2024
    On 10.04.2024 11:18, David Brown wrote:
    On 10/04/2024 09:52, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    The problem with trampolines comes about when you want to take the
    address of the nested function and use that outside of the surrounding function, while you also have variables captured by reference.

    Sounds like a C-issue (not one of nesting functions). Usually I use
    nested functions to have and use them locally and not in surrounding
    or global scope. - I'd expect a C compiler to notice such situations.


    Nested functions were never as interesting for C++ as you already have
    better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes.

    Python does both. Just because you have classes doesn’t mean functions
    can’t be first-class objects, too.

    Indeed.

    (You could also omit use of C++ templates completely and emulate them
    with class inheritance; nonetheless they are useful and convenient to
    have them supported as concept as well. - Just for another example.)

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Thu Apr 11 00:23:38 2024
    Reply-To: slp53@pacbell.net

    David Brown <david.brown@hesbynett.no> writes:
    On 10/04/2024 09:52, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other >>> cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    That would be true of those other languages that require the feature, too. >>

    There may be other differences in the languages that reduce that effect
    - or it could be a problem there too. I don't know the details.
    (Perhaps those on an Ada newsgroup would know better.)

    The problem with trampolines comes about when you want to take the
    address of the nested function and use that outside of the surrounding >function, while you also have variables captured by reference.

    The Burroughs Algol systems used something called lex levels to
    handle nested functions.

    https://en.wikipedia.org/wiki/Burroughs_Large_Systems#Stack_architecture


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Thu Apr 11 00:46:43 2024
    On 10/04/2024 14:42, bart wrote:
    On 10/04/2024 10:18, David Brown wrote:
    On 10/04/2024 09:52, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 09:11:49 +0200, David Brown wrote:

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In
    other
    cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    That would be true of those other languages that require the feature,
    too.


    There may be other differences in the languages that reduce that
    effect - or it could be a problem there too. I don't know the
    details. (Perhaps those on an Ada newsgroup would know better.)

    The problem with trampolines comes about when you want to take the
    address of the nested function and use that outside of the surrounding
    function, while you also have variables captured by reference.

    Nested functions were never as interesting for C++ as you already have >>>> better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes.

    Python does both. Just because you have classes doesn’t mean functions >>> can’t be first-class objects, too.

    True. But Python is a very different language from C++. In Python,
    not only are functions objects in themselves, but so are classes (the
    definition of the classes, rather than just instances). Python is a
    lot more "meta" than C++.

    Basically, anything you could do with a nested function in gcc C you
    can do in C++:

    int sum_square(int x, int y) {
    int square(z) { // int z ??
    return z * z;
    }
    return square(x) + square(y);
    }

    That's not an interesting use of a local function! You can move square() outside, and it would still work, putting aside any clashes with
    existing names called 'square'.

    Sure. It was not intended to be anything other than a very simple
    nested function written in gcc C extension syntax, and the same thing
    written in standard C++, to demonstrate how you can write nested
    functions in C++ without this extension. Local functions without
    capturing local data are perhaps not interesting, but they can be neat
    from the viewpoint of limiting the scope of identifiers.


    The challenges of local functions are to do with accessing transient variables belonging to their enclosing function.

    Yes. And you can do that with the gcc extension, and also with C++ lambdas.

    Especially when a
    reference to the local function is called from outside the lexical scope
    of the enclosing function, with extra difficulties when the enclosing function is no longer active.


    These are the kinds of situations where the gcc extension needs to use trampolines. C++ lambdas are closures, and can hold more information -
    each lambda is its own type, and the size of that type can be different
    for different lambdas (it can include pointers to the variables captured
    by reference, for example). This makes lambdas much easier, safer and
    more efficient to use within a translation unit - but makes them a bit
    more fiddly if they capture local variables and you need to pass them to something defined outside the current TU.

    (You can't use any local function outside the scope of the enclosing
    code if the local function has reference captures - that applies to all
    types of local functions.)



    int sum_square(int x, int y) {
    auto square = [](int z) {
    return z * z;
    }
    return square(x) + square(y);
    }

    What's the significance of the '[]' here?


    It's the syntax for a C++ lambda. You can put captures in there, or
    leave it empty for no captures.

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    (It defines a lambda with no captures and no parameters, that does
    nothing, then it invokes it.)






    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Thu Apr 11 03:10:40 2024
    On 2024-04-10, David Brown <david.brown@hesbynett.no> wrote:
    On 10/04/2024 03:50, Lawrence D'Oliveiro wrote:
    On Tue, 09 Apr 2024 11:44:23 +0100, Ben Bacarisse wrote:

    It's significant (or at least note worthy) that the code in the
    original post was not ISO C ...

    Interesting that GCC’s C compiler allows nested routine definitions,
    but the C++ compiler does not.

    It is an old extension, going back to when gcc barely (if at all)
    supported C++. The compiler middle-end had to have support for nested functions for languages like Pascal, Module 2 and Ada (I believe Ada is
    the only one of these that stuck around in gcc mainline, but other
    language front-ends have been made outside the main tree). Someone
    thought it might be a useful feature in C too, and perhaps something
    that would catch on in the standards (several early gcc extensions ended
    up standardised in C99).

    It is not much used in practice, AFAIK. For some cases the code
    generation for nested functions was fine and straight-forward. In other cases, however, it required a trampoline generated on the stack, and
    that became a real pain once non-executable stacks came into fashion.

    Nested functions were never as interesting for C++ as you already have better mechanisms for controlling scope and data access - classes and
    their methods, including nested classes. And once lambdas joined the

    Higher level languages like Common Lisp show that you want both;
    even though theoretically you can implement OOP with lambdas, or
    simulate some aspects of lambdas with OOP (particularly if your OOP
    supports callable objects).

    party in C++11 there was absolutely no reason to have nested functions - there is nothing (AFAIK) that you could do with nested functions that
    you can't do at least as well, and often better, with lambdas.

    GNU C nested functions don't require any special syntax (other than teh
    fact of allowing a function definition inside a statement block). They implicitly capture the actual variables in the surrounding scope using
    ordinary C function syntax.

    The address of a GNU C nested function is regular function pointer;
    you can pass it to qsort and bsearch.

    In fact, you can pass a GNU C nested function into a library that was
    not even compiled with GCC. If it understand the calling conventions of function pointers, it can call the function.

    However, GNU C nested functions are "downward funarg" only, which
    is a pretty severe limitation. (Worse, GNU C nested function can
    escape from a scope in spite of being dowward funarg only,
    causing undefined behavior when they are called.)

    (The "downward funarg" terminology is from ancient Lisp or Algol
    literature, means "downward (only) functional argument": a function that
    can be used as an argument to function, but not returned; i.e. passed
    downward only.)

    The downward funarg restriction has benefits too; such functions do not
    require any dynamic allocation at all. The closed-over variables are
    referenced directly in their still-living stack frame.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Thu Apr 11 03:14:01 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Tue, 09 Apr 2024 11:44:23 +0100, Ben Bacarisse wrote:

    It's significant (or at least note worthy) that the code in the
    original post was not ISO C ...

    Interesting that GCC?s C compiler allows nested routine definitions,
    but the C++ compiler does not.

    and re-writing it in ISO C would show up some of the issues involved
    ...

    Ask and you shall receive ...

    /*
    Generate permutations of a list of items.
    Pass a list of arbitary words as command arguments, and this
    program will print out all possible orderings of them.
    */

    #include <iso646.h>
    #include <stdbool.h>
    #include <stdlib.h>
    #include <stdio.h>

    typedef void (*permute_action)
    (
    unsigned int nrwords,
    const char * const * words,
    void * arg
    );

    struct permute_ctx
    {
    unsigned int nrwords;
    const char * const * words;
    permute_action action;
    void * action_arg;
    const char ** permbuf;
    bool * used;
    } /*permute_ctx*/;

    static void permute1
    (
    struct permute_ctx * ctx,
    unsigned int depth
    )
    {
    if (depth < ctx->nrwords)
    {
    for (unsigned int i = 0; i < ctx->nrwords; ++i)
    {
    if (not ctx->used[i])
    {
    ctx->permbuf[depth] = ctx->words[i];
    ctx->used[i] = true;
    permute1(ctx, depth + 1);
    ctx->used[i] = false;
    } /*if*/
    } /*for*/
    }
    else
    {
    ctx->action(ctx->nrwords, ctx->permbuf, ctx->action_arg);
    } /*if*/
    } /*permute1*/

    void permute
    (
    unsigned int nrwords,
    const char * const * words,
    permute_action action,
    void * action_arg
    )
    {
    if (nrwords > 0)
    {
    struct permute_ctx ctx;
    ctx.nrwords = nrwords;
    ctx.words = words;
    ctx.action = action;
    ctx.action_arg = action_arg;
    ctx.permbuf = (const char **)malloc(nrwords * sizeof(char *));
    ctx.used = (bool *)malloc(nrwords);
    for (unsigned int i = 0; i < nrwords; ++i)
    {
    ctx.used[i] = false;
    } /*for*/

    permute1(&ctx, 0);

    free(ctx.permbuf);
    free(ctx.used);
    } /*if*/
    } /*permute*/

    [...]

    More complicated than it needs to be. Besides that, in a beauty
    contest this coding style would lose out even to Frankenstein.

    Simpler:

    #include <stdlib.h>
    #include <string.h>

    typedef void *Voidp;
    typedef struct array_CB_s ArrayCB;
    struct array_CB_s {
    void (*run)( ArrayCB *cb, unsigned n, const Voidp[ n ] );
    };

    static void permute_r( unsigned n, Voidp[n], unsigned, ArrayCB * );
    static void voidp_exchange( Voidp [], unsigned, unsigned );

    void
    permute( unsigned n, const Voidp in_array[n], ArrayCB *cb ){
    for( Voidp *pva = malloc( n * sizeof *pva ); pva; pva = 0 ){
    memcpy( pva, in_array, n * sizeof *pva );
    permute_r( n, pva, n, cb );
    free( pva );
    }
    }

    void
    permute_r( unsigned n, Voidp values[n], unsigned k, ArrayCB *cb ){
    for( unsigned i = 0; i < k; i++ ){
    voidp_exchange( values, n-k, n-k+i );
    permute_r( n, values, k-1, cb );
    voidp_exchange( values, n-k, n-k+i );
    }
    if( k == 1 ) cb->run( cb, n, values );
    }

    void
    voidp_exchange( Voidp values[], unsigned i, unsigned j ){
    if( i != j ){
    Voidp pi = values[i], pj = values[j];
    values[i] = pj, values[j] = pi;
    }
    }

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Thu Apr 11 03:19:31 2024
    On 2024-04-10, David Brown <david.brown@hesbynett.no> wrote:
    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    Except pass that lambda to another module that expects a simple function pointer.

    That's not just C libraries. There is a foreign function ecosystem based
    on the conventions of C. If you have a function pointer, you can pass it
    into any high level language that supports C interpo, and it can call
    that thing.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Thu Apr 11 05:19:09 2024
    On 10/04/2024 19:19, Kaz Kylheku wrote:
    On 2024-04-10, David Brown <david.brown@hesbynett.no> wrote:
    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    Except pass that lambda to another module that expects a simple function pointer.

    If the lambda has no captures, then you can do that. (I don't know if
    this is supported by the C++ standards, or merely works in practice.)


    That's not just C libraries. There is a foreign function ecosystem based
    on the conventions of C. If you have a function pointer, you can pass it
    into any high level language that supports C interpo, and it can call
    that thing.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Thu Apr 11 10:16:26 2024
    On Wed, 10 Apr 2024 14:23:38 GMT, Scott Lurndal wrote:

    The Burroughs Algol systems used something called lex levels to handle
    nested functions.

    There are two techniques used in languages that allow nested scopes with lexical binding: they are called “static link” and “display array”.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Thu Apr 11 10:18:31 2024
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()” instead.

    Does the latest C spec now also take “()” to mean “no args” (synonymous
    with “(void)”) instead of “unspecified args”?

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Thu Apr 11 10:54:41 2024
    On 2024-04-11, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()”
    instead.

    That is backwards. (void) is the dud that C++ "allows"; () is the
    "native" empty parameter list in C++, and has been from the early
    beginning.

    C++ (originally C With Classes) introduced () as a prototyped, declared parameter list. At that time there was no (void) in C, and therefore not
    in C++ either.

    In C, the () list, in a declaration, didn't say anything about the
    number of parameters.

    ANSI C came along and invented (void) in order not to change the meaning
    of () for compatibility.

    Then C++ adopted (void) for ANSI C compatibility.

    I.e. (void) is concession that that C++ "allows", and did not always;
    () is the "native" empty parameter list it always had.

    It's monumentally stupid when you see (void) on feature that only exists
    in C++ and therefore C compatibility is not involved:

    myclass::myclass(void)
    {
    // default constructor
    }

    Does the latest C spec now also take “()” to mean “no args” (synonymous
    with “(void)”) instead of “unspecified args”?

    The N3320 working draft says:

    "The special case of an unnamed parameter of type void as the only item
    in the list specifies that the function has no parameters."
    (6.7.7.4 Function declarators)

    "For a function declarator without a parameter type list: the effect is
    as if it were declared with a parameter type list consisting of the
    keyword void. A function declarator provides a prototype for the
    function." (ibid.)

    The last sentence assures us that function declartions are now protypes;
    there are no function declarators that do not prototype the parameters.

    (Apologies to Kenny for the "ibid.")

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Thu Apr 11 10:54:54 2024
    On Wed, 10 Apr 2024 11:18:12 +0200, David Brown wrote:

    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    Has anybody suggested adding coroutines to C++ yet?

    E.g. Python equivalent to the permute function that uses a recursive generator:

    def permute(seq) :
    if len(seq) == 0 :
    yield ()
    else :
    for i in range(len(seq)) :
    for rest in permute(seq[:i] + seq[i + 1:]) :
    yield (seq[i],) + rest
    #end for
    #end for
    #end if
    #end permute

    Example use:

    for x in permute(("a", "b", "c")) :
    print(x)
    #end for

    Output:

    ('a', 'b', 'c')
    ('a', 'c', 'b')
    ('b', 'a', 'c')
    ('b', 'c', 'a')
    ('c', 'a', 'b')
    ('c', 'b', 'a')

    And then there’s the fun you can have with async/await ...

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Thu Apr 11 12:06:00 2024
    On 2024-04-11, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    Has anybody suggested adding coroutines to C++ yet?

    Your training data isn't very recent, evidently.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kenny McCormack@3:633/280.2 to All on Thu Apr 11 18:00:13 2024
    In article <20240410174139.914@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    ....
    (Apologies to Kenny for the "ibid.")

    Heh heh. Got that one. Had to think for a minute or so, though.

    Anyway, just for the record, I was specifically talking about things like
    "ad hominem" - i.e., all those terms for supposedly bad arguments. Like
    "ad populem" (Or however you spell it...) and so on.

    Basically, I am saying that when someone uses the term "ad hominem", they
    are conceding that they have lost the argument.

    --
    Trump has normalized hate.

    The media has normalized Trump.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: The official candy of the new Millennium (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Thu Apr 11 18:15:48 2024
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()” instead.

    I do that sometimes, yes. I could argue that I think it is better to be explicit than implicit, but it's just my fingers on automatic. The
    kinds of programs I work on can rarely be pure C++ - there's always lots
    of C code too, and it's often useful to write code in a way that is
    suitable for both C and C++.

    In this particular case, of course, there's not much advantage to having
    a C-style function declaration!


    Does the latest C spec now also take “()” to mean “no args” (synonymous
    with “(void)”) instead of “unspecified args”?

    It does, yes, in C23. If you want to declare a function with no
    information about the number or type of parameters, you can now write
    "void foo(...);". (Prior to C23 you needed at least one normal typed parameter before the ellipsis.)


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Thu Apr 11 18:23:03 2024
    On 11/04/2024 02:54, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 11:18:12 +0200, David Brown wrote:

    Basically, anything you could do with a nested function in gcc C you can
    do in C++:

    Has anybody suggested adding coroutines to C++ yet?


    It's in C++20.

    <https://en.cppreference.com/w/cpp/language/coroutines>

    I have not looked at them myself, or checked the status of support in
    C++ compilers and standard libraries.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Thu Apr 11 18:34:37 2024
    On Thu, 11 Apr 2024 10:23:03 +0200, David Brown wrote:

    On 11/04/2024 02:54, Lawrence D'Oliveiro wrote:

    Has anybody suggested adding coroutines to C++ yet?

    It's in C++20.

    <https://en.cppreference.com/w/cpp/language/coroutines>

    Can’t have variadic arguments, and can’t be constructors.

    In Python, “__init__()” cannot be async, but “__new__()” can. So you can
    do async class instantiation, after all.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Scott Lurndal@3:633/280.2 to All on Fri Apr 12 00:36:42 2024
    Reply-To: slp53@pacbell.net

    David Brown <david.brown@hesbynett.no> writes:
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()”
    instead.

    I do that sometimes, yes. I could argue that I think it is better to be >explicit than implicit, but it's just my fingers on automatic.

    I would use the same argument. Make it explicit.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: UsenetServer - www.usenetserver.com (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Apr 12 01:04:24 2024
    On 2024-04-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()”
    instead.

    I do that sometimes, yes. I could argue that I think it is better to be >>explicit than implicit, but it's just my fingers on automatic.

    I would use the same argument. Make it explicit.

    (void) is a dongle intoduced in ANSI C so that () could continue
    to mean "unknown number of parameters". It didn't exist in C++
    until ANSI C invented it.

    () is a perfectly explicit empty list. (void) does not look empty;
    it looks like it's declaring one parameter of type void.

    An actual implicit empty parameter list might look like this:

    function foo
    {
    }

    Once you have empty parentheses, that is explicit.

    Countless programming languages have only (); no such thing as (void) or similar ugly hack, due to not having a quirky history that would have
    caused such a thing to be required.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Apr 12 01:13:53 2024
    On 2024-04-11, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    In article <20240410174139.914@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    ...
    (Apologies to Kenny for the "ibid.")

    Heh heh. Got that one. Had to think for a minute or so, though.

    Anyway, just for the record, I was specifically talking about things like
    "ad hominem" - i.e., all those terms for supposedly bad arguments. Like
    "ad populem" (Or however you spell it...) and so on.

    Basically, I am saying that when someone uses the term "ad hominem", they
    are conceding that they have lost the argument.

    Wait, isn't that "ad hit"? (adolf hitlerum)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri Apr 12 04:47:48 2024
    On 11/04/2024 17:04, Kaz Kylheku wrote:
    On 2024-04-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code: >>>>>
    void foo(void) {
    [](){}();
    }

    C programmer still has habit of writing “(void)” when C++ allows “()”
    instead.

    I do that sometimes, yes. I could argue that I think it is better to be >>> explicit than implicit, but it's just my fingers on automatic.

    I would use the same argument. Make it explicit.

    (void) is a dongle intoduced in ANSI C so that () could continue
    to mean "unknown number of parameters". It didn't exist in C++
    until ANSI C invented it.

    I like history as much as the next person - probably more so, and thus I
    do appreciate you explaining where this comes from. But I don't base my programming choices or style on history. From the point of view of
    current programming, it's irrelevant when "(void)" was introduced to C
    and C++, it's only relevant what it means /now/ to the compiler, and
    what it means to someone reading the code.

    I write "(void)" in C code, or in C++ code that might be seen by a C
    compiler (such as in a header file that could be used with C or C++), or
    if it might be read by someone who is more familiar with C than C++. If
    the code is "pure C++", such as this example, I normally don't bother
    with "(void)". But I don't object to it either.


    () is a perfectly explicit empty list. (void) does not look empty;
    it looks like it's declaring one parameter of type void.

    "void" has always been a bit weird as a "type" or as a placeholder for
    an empty type.


    An actual implicit empty parameter list might look like this:

    function foo
    {
    }

    Once you have empty parentheses, that is explicit.


    "(void)" stands out more.

    Countless programming languages have only (); no such thing as (void) or similar ugly hack, due to not having a quirky history that would have
    caused such a thing to be required.


    It has never struck me as "ugly", but it is arguably quirky.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kenny McCormack@3:633/280.2 to All on Fri Apr 12 05:06:08 2024
    In article <20240411081303.407@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    On 2024-04-11, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    In article <20240410174139.914@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    ...
    (Apologies to Kenny for the "ibid.")

    Heh heh. Got that one. Had to think for a minute or so, though.

    Anyway, just for the record, I was specifically talking about things like
    "ad hominem" - i.e., all those terms for supposedly bad arguments. Like
    "ad populem" (Or however you spell it...) and so on.

    Basically, I am saying that when someone uses the term "ad hominem", they
    are conceding that they have lost the argument.

    Wait, isn't that "ad hit"? (adolf hitlerum)

    I don't know what that means.

    (T. Brennan - Yes, I've started watching "Bones" again...)

    --
    The randomly chosen signature file that would have appeared here is more than 4 lines long. As such, it violates one or more Usenet RFCs. In order to remain in compliance with said RFCs, the actual sig can be found at the following URL:
    http://user.xmission.com/~gazelle/Sigs/Noam

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: The official candy of the new Millennium (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Fri Apr 12 05:37:11 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }
    C programmer still has habit of writing “(void)” when C++ allows
    “()”
    instead.

    I do that sometimes, yes. I could argue that I think it is better to
    be explicit than implicit, but it's just my fingers on automatic. The
    kinds of programs I work on can rarely be pure C++ - there's always
    lots of C code too, and it's often useful to write code in a way that
    is suitable for both C and C++.

    In this particular case, of course, there's not much advantage to
    having a C-style function declaration!

    Does the latest C spec now also take “()” to mean “no args”
    (synonymous
    with “(void)”) instead of “unspecified args”?

    It does, yes, in C23. If you want to declare a function with no
    information about the number or type of parameters, you can now write
    "void foo(...);". (Prior to C23 you needed at least one normal typed parameter before the ellipsis.)

    That's not quite the same. (...) declares a variadic function, whose
    arguments (if any) can be accessed using the macros in <stdarg.h>.
    Functions with an old-style () declaration can access their arguments by
    names given in the definition Variadic functions could have a different
    calling convention.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Fri Apr 12 09:04:00 2024
    scott@slp53.sl.home (Scott Lurndal) writes:

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    On Wed, 10 Apr 2024 14:23:38 GMT, Scott Lurndal wrote:

    The Burroughs Algol systems used something called lex levels to handle
    nested functions.

    There are two techniques used in languages that allow nested scopes with >>lexical binding: they are called "static link" and "display array".

    What does that have to do with the Burroughs hardware support for
    lexical levels?

    I don't know all Burroughs hardware, but the famous B5000 accessed lex
    levels using a set of 32 "D" registers. This is exactly what a "display
    array" is. I suspect the register set's name (D) is a nod to the
    conventional name for the technique.

    PS. Can you get your newsreader to at least leave UTF-8 posting headers un-changed? Passing the bytes through but removing the "charset"
    declaration leads to mojibake.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Fri Apr 12 12:31:53 2024
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    res = func(void);

    instead of

    res = func();

    ?

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Fri Apr 12 13:51:49 2024
    On 2024-04-12, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    Scott is getting burned by the current resident imbecile,
    how embarrassing.

    res = func(void);

    instead of

    res = func();

    Well, according to the eccentric coding conventions followed by your
    team of one, indeed, yes; shouldn't it be:

    res = func(/* void */);

    No, wait:

    res = func(
    /* void = nothing */
    ); /* func( */

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri Apr 12 15:32:33 2024
    On 12.04.2024 04:31, Lawrence D'Oliveiro wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    res = func(void);

    instead of

    res = func();

    ?

    Ideally it would be (without syntactic ballast) just

    res = func;

    (as many programming languages have it designed), in
    function definition and function call; no parameters,
    no unnecessary parenthesis.

    (Even Algol 68, where I've seen 'void' mentioned for
    the first time, does not use 'void' for an empty
    function argument list definition or function call.)

    But we use C here, so we have to take what's given.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri Apr 12 17:30:18 2024
    On 12/04/2024 07:32, Janis Papanagnou wrote:
    On 12.04.2024 04:31, Lawrence D'Oliveiro wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    res = func(void);

    instead of

    res = func();

    ?

    Ideally it would be (without syntactic ballast) just

    res = func;

    (as many programming languages have it designed), in
    function definition and function call; no parameters,
    no unnecessary parenthesis.

    (Even Algol 68, where I've seen 'void' mentioned for
    the first time, does not use 'void' for an empty
    function argument list definition or function call.)

    But we use C here, so we have to take what's given.


    I prefer the consistency of function calls using parenthesis. The
    consistent alternative, found in some other languages, is that they
    never need parenthesis - "foo a b" calls "foo" with parameters "a" and "b".

    (Of course you'd need some syntax for referring to the function itself,
    or its address - "fp = &foo;" would not be too onerous, IMHO.)




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Fri Apr 12 17:34:57 2024
    On Fri, 12 Apr 2024 07:32:33 +0200, Janis Papanagnou wrote:

    Ideally it would be (without syntactic ballast) just

    res = func;

    Then there is no way to express a reference to the function itself, as distinct from a call to it.

    Unless you do what Algol 68 did, and introduce the “deproceduring coercion”, analogous to “dereferencing” which allowed doing away with any
    explicit “address of x” and “the thingy whose address is in x” constructs.

    (Even Algol 68, where I've seen 'void' mentioned for the first time,
    does not use 'void' for an empty function argument list definition
    or function call.)

    I just rechecked the Revised Report (always got to do it before opening my mouth), and a “statement” is always “strong void”, which means any value
    it returns can always be thrown away without having to explicitly say so.

    But there are other contexts which are not “strong”, where fewer coercions happen automatically.

    Just did another check, and even in the weakest of syntactic contexts, “soft”, implicit deproceduring is still allowed (but not implicit dereferencing).

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Fri Apr 12 17:38:50 2024
    On 11/04/2024 21:37, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 11/04/2024 02:18, Lawrence D'Oliveiro wrote:
    On Wed, 10 Apr 2024 16:46:43 +0200, David Brown wrote:

    Just for your entertainment, with C++ lambdas this is now legal code:

    void foo(void) {
    [](){}();
    }
    C programmer still has habit of writing “(void)” when C++ allows
    “()”
    instead.

    I do that sometimes, yes. I could argue that I think it is better to
    be explicit than implicit, but it's just my fingers on automatic. The
    kinds of programs I work on can rarely be pure C++ - there's always
    lots of C code too, and it's often useful to write code in a way that
    is suitable for both C and C++.

    In this particular case, of course, there's not much advantage to
    having a C-style function declaration!

    Does the latest C spec now also take “()” to mean “no args”
    (synonymous
    with “(void)”) instead of “unspecified args”?

    It does, yes, in C23. If you want to declare a function with no
    information about the number or type of parameters, you can now write
    "void foo(...);". (Prior to C23 you needed at least one normal typed
    parameter before the ellipsis.)

    That's not quite the same. (...) declares a variadic function, whose arguments (if any) can be accessed using the macros in <stdarg.h>.
    Functions with an old-style () declaration can access their arguments by names given in the definition Variadic functions could have a different calling convention.


    Good point. Being able to write "T foo(...)" replaces any need to
    declare pre-C23 "T foo()", but it is not a direct replacement without
    changing code. (It might work as a replacement in practice for simple
    cases, even though it is not required by the C standards - I don't know
    how compatible ellipsis arguments are with declared arguments.)

    There should, hopefully, be very little code from the last generation
    that uses "T foo()" forms other than meaning "T foo(void)".


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Fri Apr 12 19:38:52 2024
    On 12/04/2024 06:32, Janis Papanagnou wrote:
    On 12.04.2024 04:31, Lawrence D'Oliveiro wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    res = func(void);

    instead of

    res = func();

    (What happens in Python when 'func' has multiple /optional/ parameters
    which have all been omitted; do you need to document them in the call to distinguish this from a call to a function which genuinely has no
    arguments?)


    ?

    Ideally it would be (without syntactic ballast) just

    res = func;

    (as many programming languages have it designed), in
    function definition and function call; no parameters,
    no unnecessary parenthesis.

    I used to allow 'func' to call a function with no args. Later I switched
    to using func() as being more informative, since just:

    func

    doesn't impart very much. Maybe it's a function call; maybe it's a goto
    to label 'func' (as I still allow); maybe it's a macro invocation; maybe
    it's just evaluating a variable 'func' then discarding the value.

    Using func() becomes more necessary with dynamic code; if P is a
    reference to a function, then what does this mean:

    Q := P

    Is this copying the reference to Q, or calling P() and copying the
    result? Using &P to disambiguate won't work here: that will create a
    reference to the reference!

    So F() is really doing CALL F; it's making it explicit.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri Apr 12 22:35:47 2024
    On 12.04.2024 09:34, Lawrence D'Oliveiro wrote:
    On Fri, 12 Apr 2024 07:32:33 +0200, Janis Papanagnou wrote:

    Ideally it would be (without syntactic ballast) just

    res = func;

    Then there is no way to express a reference to the function itself, as distinct from a call to it.

    Unless you do what Algol 68 did, and introduce the “deproceduring coercion”, analogous to “dereferencing” which allowed doing away with any
    explicit “address of x” and “the thingy whose address is in x” constructs.

    It seems that's one of the fundamental differences between (low-level) languages that want to provide such technical factors explicit to the
    user and between languages that want to provide a higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that
    approach.

    Languages syntactically derived from C or borrowed its syntax didn't.

    It's, BTW, one of the reasons I like all these languages and not only
    from a conceptual point of view, but by far not the only reason; the
    way that programmers are not forced to consider low-level technical
    stuff but can focus on the algorithm. Some things should be the task
    of the compiler, not the programmer. A lot of inherent issues with C
    stem from it's low-level design.


    (Even Algol 68, where I've seen 'void' mentioned for the first time,
    does not use 'void' for an empty function argument list definition
    or function call.)

    I just rechecked the Revised Report (always got to do it before opening my mouth), and a “statement” is always “strong void”, which means any value
    it returns can always be thrown away without having to explicitly say so.

    In Algol 68 everything is an expression. Statements are of type 'void'.
    You have, to keep it most simple here, for example,

    PROC f = INT : 3.1415

    PROC p = VOID : SKIP

    There's nothing "thrown away" (as in C). This is a completely different
    view, and a different path that C has chosen to go, where everything is
    a function (as they say).

    [ snip ]

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Dan Cross@3:633/280.2 to All on Fri Apr 12 22:51:55 2024
    In article <20240411204846.381@kylheku.com>,
    Kaz Kylheku <643-408-1753@kylheku.com> wrote:
    On 2024-04-12, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    Scott is getting burned by the current resident imbecile,
    how embarrassing.

    res = func(void);

    instead of

    res = func();

    Well, according to the eccentric coding conventions followed by your
    team of one, indeed, yes; shouldn't it be:

    res = func(/* void */);

    No, wait:

    res = func(
    /* void = nothing */
    ); /* func( */

    LOL. Despite punching down, that was very funny.

    - Dan C.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: PANIX Public Access Internet and UNIX, NYC (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri Apr 12 23:03:08 2024
    On 12.04.2024 09:30, David Brown wrote:

    I prefer the consistency of function calls using parenthesis. The
    consistent alternative, found in some other languages, is that they
    never need parenthesis - "foo a b" calls "foo" with parameters "a" and "b".

    Mind that this is just one "consistency" aspect.

    Another one is that you can write (e.g. in Algol 68 or many other
    higher level languages) the equivalent of - again a simple example -

    int x = 2 * pi * r

    without necessity to know whether pi is a constant, the result of a
    function (calculation), the result of a function implicitly called
    just once on demand, or whatever else. Conceptually as a programming
    language user you want the value.

    I don't say one or the other is "better", just that consistence is
    not an absolute property. (But also that Algol 68, WRT a consistent
    formal language design, is indeed very different from C in quality.)


    (Of course you'd need some syntax for referring to the function itself,
    or its address - "fp = &foo;" would not be too onerous, IMHO.)

    All repliers to my post mentioned references or addresses; I think,
    beyond personal preferences, it is a key observation that this is
    unnecessary if the language (and its compiler) handles that per its
    semantics. In Algol 68 you can define fp = foo but you don't need
    an "address value" introduced.

    Preferences are probably heavily influenced by the set of languages
    one started with, so it's not useful to dispute here. But I think it's noteworthy to anticipate that it's not a "legacy" thing with languages
    that evolved around 1967 to 1972; Eiffel and other important languages
    (I think also Ada) later also took that path. It's also noteworthy
    that these languages focus on programming safety; and I suppose that
    there's no dissent here about the programming safety in C as opposed
    to the languages mentioned in the other reply I just gave.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Fri Apr 12 23:07:49 2024
    On 12.04.2024 11:38, bart wrote:

    I used to allow 'func' to call a function with no args. Later I switched
    to using func() as being more informative, since just:

    func

    I wasn't aware that C allows that. (Or are you talking about your own language(s) here?)


    doesn't impart very much. Maybe it's a function call; maybe it's a goto
    to label 'func' (as I still allow); maybe it's a macro invocation; maybe
    it's just evaluating a variable 'func' then discarding the value.

    You seem to be writing that [since] you "allow" a lot of non-standard
    things thus needing some syntax to restrict that initial freedom again.

    Anyway; I have no connection to your tools (if that's what you were
    speaking about), so I abstain.

    Janis

    [...]



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sat Apr 13 00:51:28 2024
    On 12/04/2024 15:03, Janis Papanagnou wrote:
    On 12.04.2024 09:30, David Brown wrote:

    I prefer the consistency of function calls using parenthesis. The
    consistent alternative, found in some other languages, is that they
    never need parenthesis - "foo a b" calls "foo" with parameters "a" and "b".

    Mind that this is just one "consistency" aspect.

    True.


    Another one is that you can write (e.g. in Algol 68 or many other
    higher level languages) the equivalent of - again a simple example -

    int x = 2 * pi * r

    without necessity to know whether pi is a constant, the result of a
    function (calculation), the result of a function implicitly called
    just once on demand, or whatever else. Conceptually as a programming
    language user you want the value.


    But is that a good thing? For some programming, especially with
    higher-level languages, then it is fine. For other types of
    programming, you want to know if functions are called in order to have a better idea of the flow of control and the efficiency of the code, plus perhaps thread safety.


    I don't say one or the other is "better", just that consistence is
    not an absolute property.

    Fair enough, and I agree that this is not a matter that has a single
    correct answer. It is not uncommon that being consistent in one way necessitates being inconsistent in another way.


    Just for fun, this is a way to let you define a function-like object
    "pi" in C++ that is called automatically, without parentheses :


    #include <numbers>

    template <auto f>
    class Auto_executor {
    public :
    using T = decltype(f());
    operator T () {
    return f();
    }
    };

    static Auto_executor<[](){ return std::numbers::pi; }> pi;

    double circumferance(double r) {
    return 2 * pi * r;
    }


    I am not giving an opinion as to whether or not this is a good idea (and obviously it is completely redundant in the case of a compile-time
    constant). Some people might think C++ is great because it lets you use tricks like this, some people might think C++ is terrible because it
    lets you use tricks like this :-)





    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sat Apr 13 01:38:33 2024
    On 12/04/2024 14:07, Janis Papanagnou wrote:
    On 12.04.2024 11:38, bart wrote:

    I used to allow 'func' to call a function with no args. Later I switched
    to using func() as being more informative, since just:

    func

    I wasn't aware that C allows that. (Or are you talking about your own language(s) here?)

    In my language I used to allow 'func' for a function call with no args
    until I decided to require 'func()'.


    doesn't impart very much. Maybe it's a function call; maybe it's a goto
    to label 'func' (as I still allow); maybe it's a macro invocation; maybe
    it's just evaluating a variable 'func' then discarding the value.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sat Apr 13 01:42:00 2024
    On 12/04/2024 13:35, Janis Papanagnou wrote:
    On 12.04.2024 09:34, Lawrence D'Oliveiro wrote:
    On Fri, 12 Apr 2024 07:32:33 +0200, Janis Papanagnou wrote:

    Ideally it would be (without syntactic ballast) just

    res = func;

    Then there is no way to express a reference to the function itself, as
    distinct from a call to it.

    Unless you do what Algol 68 did, and introduce the “deproceduring
    coercion”, analogous to “dereferencing” which allowed doing away with any
    explicit “address of x” and “the thingy whose address is in x” constructs.

    It seems that's one of the fundamental differences between (low-level) languages that want to provide such technical factors explicit to the
    user and between languages that want to provide a higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that approach.

    Languages syntactically derived from C or borrowed its syntax didn't.

    You don't say anything about C itself. C sometimes is explicit and
    sometimes it isn't:

    &F; // both take the address of a function F
    F;

    P(); // both call a function via a pointer P
    (*P)();

    So you can use an explicit & or * operator, not not.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Sat Apr 13 10:10:25 2024
    On Fri, 12 Apr 2024 15:03:08 +0200, Janis Papanagnou wrote:

    Another one is that you can write (e.g. in Algol 68 or many other higher level languages) the equivalent of - again a simple example -

    int x = 2 * pi * r

    without necessity to know whether pi is a constant, the result of a
    function (calculation), the result of a function implicitly called just
    once on demand, or whatever else. Conceptually as a programming language
    user you want the value.

    In Python you can’t do this with an unqualified name, but you can with a qualified one, e.g.

    x = 2 * math.pi * r

    Which one of these might be true?

    * “math” is a module, and “pi” is a variable defined within it
    * “math” is a class, and “pi” is a variable defined within it
    * “math” is an instance of a class, and “pi” is a variable defined either
    within that instance or the class
    * “math” is an instance of a class, and “pi” is a “property”, which is
    computing a value, on demand, via an implicit method call

    Note that the fourth possibility does an implicit method call without
    argument parentheses.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Sat Apr 13 10:13:36 2024
    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between (low-level) languages that want to provide such technical factors explicit to the
    user and between languages that want to provide a higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Languages syntactically derived from C or borrowed its syntax didn't.

    Is there anything higher level than λ-calculus? In that notation, you
    always know whether you are referring to a function, or calling it.

    Lisp makes function calls explicit in the same way. And no one would
    accuse it of “deriving from C”, given that it originated about a decade earlier.

    In Algol 68 everything is an expression. Statements are of type 'void'.
    You have, to keep it most simple here, for example,

    PROC f = INT : 3.1415

    PROC p = VOID : SKIP

    There's nothing "thrown away" (as in C).

    Yes there is--it’s called “voiding”.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sat Apr 13 12:06:54 2024
    bart <bc@freeuk.com> writes:
    On 12/04/2024 06:32, Janis Papanagnou wrote:
    On 12.04.2024 04:31, Lawrence D'Oliveiro wrote:
    On Thu, 11 Apr 2024 15:15:35 GMT, Scott Lurndal wrote:

    As someone who cut his teeth on
    Unix V6, an empty parameter list is less self-documenting than an
    explicit (void).

    Should that apply when calling the function as well?

    res = func(void);

    instead of

    res = func();

    (What happens in Python when 'func' has multiple /optional/ parameters
    which have all been omitted; do you need to document them in the call
    to distinguish this from a call to a function which genuinely has no arguments?)

    Nothing special. Python requires parentheses on all function calls.
    res = func() assigns to res the value returned by the function func.
    res = func assigns to res a value of type "function".

    It's similar to what C does, except that in C there's no way res can be assignment-compatible with both func and func().

    In Ada, a procedure or function call with no arguments doesn't use
    parentheses:

    res := func;

    Ada has no values of function type (which is why it can get away with
    omitting parentheses for calls with no arguments), though it does have
    are the equivalent of pointers to functions. (The lack of parentheses
    on calls with no arguments is something that always annoyed me about
    Ada.)

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sun Apr 14 00:46:59 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    [properties of some other languages]

    But we use C here, so we have to take what's given.

    It would be nice if more participants in this newsgroup
    would follow that precept.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Sun Apr 14 03:17:53 2024
    On 12.04.2024 16:51, David Brown wrote:
    On 12/04/2024 15:03, Janis Papanagnou wrote:

    Another one is that you can write (e.g. in Algol 68 or many other
    higher level languages) the equivalent of - again a simple example -

    int x = 2 * pi * r

    without necessity to know whether pi is a constant, the result of a
    function (calculation), the result of a function implicitly called
    just once on demand, or whatever else. Conceptually as a programming
    language user you want the value.


    But is that a good thing?

    In my book it is a good thing (but I wouldn't overestimate it)...

    For some programming, especially with
    higher-level languages, then it is fine. For other types of
    programming, you want to know if functions are called in order to have a better idea of the flow of control and the efficiency of the code, plus perhaps thread safety.

    ....because the (in practice valid!) topics you mention _should_ not
    be of concern to the programmer. The "idea of the flow" should, IMO,
    certainly not depend on parenthesis. I'll try an example from a C++
    context... - Say, I want to get the "length" of a container. What I
    indeed care about is the complexity of that operation, but that
    should be part of the library specification. C++/STL provides me
    with O(1), O(N), O(x) information that I can count on. Though the
    procedure to determine some length() might depend on the type of
    the container; it may be just an attribute access, it might be a
    difference computation (last-first+1), or it might be an iteration.
    Despite that could be hidden in a function length() you need the
    O(x) information to be sure about efficiency. (Or to look into the
    function's source code of the function, if available, to analyze
    the algorithm.)

    What I'm basically trying to say is that length() doesn't provide
    the certainty or information that one needs or likes to have. All
    it provides is a syntactical, technical detail of the language.
    With it you get the uncertainty of "Uh-oh, there's parentheses;
    now does it mean that it's expensive to use it?" - But you don't
    get relevant information from the parentheses!

    For the [general, non-C] programmer it's (IMO) clearer to not put
    (unnecessary) syntactical burden on him. He wants a value 'len'?
    He just gets the value 'len' (and not len())!

    I think I've already said that it's to a large degree probably
    personal experience and preferences whether one comes to the
    conclusion that it's a good thing, a bad thing, or even "mostly
    harmless" (meaningless).

    (These are the thoughts from someone who did not start his CS life
    with C-like languages. From someone who has seen a lot of languages
    with good concepts that needed decades to only slowly - if at all -
    find their way into the modern, mainstream, or hyped languages. I
    thought that evolution in programming languages would benefit from
    good concepts. But "Not Invented Here" principle seems to dominate. <end-of-rant>)


    Just for fun, this is a way to let you define a function-like object
    "pi" in C++ that is called automatically, without parentheses :
    [...]
    I am not giving an opinion as to whether or not this is a good idea (and obviously it is completely redundant in the case of a compile-time
    constant). Some people might think C++ is great because it lets you use tricks like this, some people might think C++ is terrible because it
    lets you use tricks like this :-)

    (Nice example.) - Well, to me C++ is (or was) great because it
    supported what I've learned to love from using Simula; classes
    and inheritance, the whole OO paradigm, "living objects" and a
    lot more that's still "undiscovered" by other languages. What I
    found terrible was that it inherited the whole uncertainty from
    using a low level language like C.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Michael S@3:633/280.2 to All on Sun Apr 14 03:33:03 2024
    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between
    (low-level) languages that want to provide such technical factors
    explicit to the user and between languages that want to provide a
    higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.


    Of course, Ada has pointers. The are called access types. https://en.wikibooks.org/wiki/Ada_Programming/Types/access

    I never learned Algol-68, but considering your reputation I'd want to
    see confirmation from more reliable source.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Sun Apr 14 03:37:37 2024
    On 13.04.2024 02:13, Lawrence D'Oliveiro wrote:
    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between (low-level)
    languages that want to provide such technical factors explicit to the
    user and between languages that want to provide a higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that
    approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Pascal has pointers bound to the object (as opposed to C pointers).
    It's similar in Algol 68 were you model your complex linked object
    structures with "references" on the heap. (WRT Ada, I'm not deeply
    familiar with it, so cannot tell.)

    [...]

    In Algol 68 everything is an expression. Statements are of type 'void'.
    You have, to keep it most simple here, for example,

    PROC f = INT : 3.1415

    PROC p = VOID : SKIP

    There's nothing "thrown away" (as in C).

    Yes there is--it’s called “voiding”.

    What do you mean - mind to elaborate with examples? (Then it will
    be simpler for me to explain our probably different views.)

    For the moment I just want to expand on the samples above (and
    thereby fixing the INT/REAL typo)...

    PROC f = REAL : 3.1415;
    PROC p = VOID : SKIP;

    PROC n = VOID : 2.71828;
    PROC q = REAL : SKIP;

    REAL x = f;
    p;
    REAL y = n;
    q;
    REAL z = q;

    This is *not* functioning Algol 68 code! - But maybe you can explain
    where or how there's something "thrown away".

    I suppose you have something in mind like C's (void) f(); ?

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From David Brown@3:633/280.2 to All on Sun Apr 14 04:04:14 2024
    On 13/04/2024 19:17, Janis Papanagnou wrote:
    On 12.04.2024 16:51, David Brown wrote:
    On 12/04/2024 15:03, Janis Papanagnou wrote:

    Another one is that you can write (e.g. in Algol 68 or many other
    higher level languages) the equivalent of - again a simple example -

    int x = 2 * pi * r

    without necessity to know whether pi is a constant, the result of a
    function (calculation), the result of a function implicitly called
    just once on demand, or whatever else. Conceptually as a programming
    language user you want the value.


    But is that a good thing?

    In my book it is a good thing (but I wouldn't overestimate it)...

    For some programming, especially with
    higher-level languages, then it is fine. For other types of
    programming, you want to know if functions are called in order to have a
    better idea of the flow of control and the efficiency of the code, plus
    perhaps thread safety.

    ...because the (in practice valid!) topics you mention _should_ not
    be of concern to the programmer. The "idea of the flow" should, IMO, certainly not depend on parenthesis. I'll try an example from a C++ context... - Say, I want to get the "length" of a container. What I
    indeed care about is the complexity of that operation, but that
    should be part of the library specification. C++/STL provides me
    with O(1), O(N), O(x) information that I can count on. Though the
    procedure to determine some length() might depend on the type of
    the container; it may be just an attribute access, it might be a
    difference computation (last-first+1), or it might be an iteration.
    Despite that could be hidden in a function length() you need the
    O(x) information to be sure about efficiency. (Or to look into the
    function's source code of the function, if available, to analyze
    the algorithm.)


    For most C++ programming, if you read code :

    int len = xs.length;

    you'll assume that is O(1). But if you read :

    int len = xs.length();

    you won't make that assumption. If you need to know the complexity,
    you'll check exactly what type of container "xs" is, and look up the complexity guarantees. (And while I showed a way to make calls in C++
    without parentheses, I am not suggesting it's a good idea to invalidate
    the assumptions readers will typically make about code.)

    Again, this stuff is often not important, or at least not critical - but sometimes it is.

    What I'm basically trying to say is that length() doesn't provide
    the certainty or information that one needs or likes to have. All
    it provides is a syntactical, technical detail of the language.
    With it you get the uncertainty of "Uh-oh, there's parentheses;
    now does it mean that it's expensive to use it?" - But you don't
    get relevant information from the parentheses!


    Agreed. But it will often lead to assumptions.

    However, those assumptions are based on the language. People who are
    used to Pascal or Ada would have other assumptions (or lack of
    assumptions) when reading Pascal or Ada.

    For the [general, non-C] programmer it's (IMO) clearer to not put (unnecessary) syntactical burden on him. He wants a value 'len'?
    He just gets the value 'len' (and not len())!

    I think I've already said that it's to a large degree probably
    personal experience and preferences whether one comes to the
    conclusion that it's a good thing, a bad thing, or even "mostly
    harmless" (meaningless).

    (These are the thoughts from someone who did not start his CS life
    with C-like languages. From someone who has seen a lot of languages
    with good concepts that needed decades to only slowly - if at all -
    find their way into the modern, mainstream, or hyped languages. I
    thought that evolution in programming languages would benefit from
    good concepts. But "Not Invented Here" principle seems to dominate. <end-of-rant>)

    I started with BASIC (a half dozen varieties), then assembly and machine
    code on multiple systems, brief flirtations with Logo, Forth, and even
    Prolog and APL (/very/ briefly for these), then Pascal, then university
    with Orwell (very similar to Haskell), Modula 2, C, Occam, then real
    life with Pascal, assembly (many types), C, C++, Python, some hardware
    design languages, and bits and pieces of a number of other languages for
    fun or profit. It's nice to have a variety of experiences, even if the
    great majority of work is in only a few of these languages.



    Just for fun, this is a way to let you define a function-like object
    "pi" in C++ that is called automatically, without parentheses :
    [...]
    I am not giving an opinion as to whether or not this is a good idea (and
    obviously it is completely redundant in the case of a compile-time
    constant). Some people might think C++ is great because it lets you use
    tricks like this, some people might think C++ is terrible because it
    lets you use tricks like this :-)

    (Nice example.) - Well, to me C++ is (or was) great because it
    supported what I've learned to love from using Simula; classes
    and inheritance, the whole OO paradigm, "living objects" and a
    lot more that's still "undiscovered" by other languages. What I
    found terrible was that it inherited the whole uncertainty from
    using a low level language like C.


    A lot of C++'s poorer parts are due to compatibility with C.
    Compatibility with C is of course useful in many ways too, but it has
    always been a millstone around C++'s neck.



    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Sun Apr 14 09:23:44 2024
    Michael S <already5chosen@yahoo.com> writes:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between
    (low-level) languages that want to provide such technical factors
    explicit to the user and between languages that want to provide a
    higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took that
    approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers. The are called access types. https://en.wikibooks.org/wiki/Ada_Programming/Types/access

    I never learned Algol-68, but considering your reputation I'd want to
    see confirmation from more reliable source.

    I suppose it all depends on what constitutes a pointer, but Algol 68 has pointers to this extent:

    BEGIN
    INT i := 42;
    [3] INT a := (1, 2, 3);

    REF INT p := i; CO p is a "pointer" to i CO
    REF INT(p) := 99; CO changes i via p CO
    p := a[2]; CO p now points to the second element of array a CO
    REF INT(p) := 99; CO change that element via p CO

    print((i, a[1], a[2], a[3]))
    END

    I'd call p a pointer.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Sun Apr 14 10:28:44 2024
    On Sat, 13 Apr 2024 19:37:37 +0200, Janis Papanagnou wrote:

    Pascal has pointers bound to the object (as opposed to C pointers).

    Nothing fundamentally different. C pointers (at least as of ANSI C) are
    just as typesafe as Pascal ones. Don’t understand what you mean about “bound to the object”.

    But maybe you can explain where or how there's something "thrown away".

    This is a valid Algol 68 program (just tested with a68g):

    BEGIN PROC f = REAL : 3.1415; f END

    It calls f, which returns a real, which is then cast to VOID -- i.e.
    thrown away.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Michael S@3:633/280.2 to All on Sun Apr 14 10:29:02 2024
    On Sun, 14 Apr 2024 00:23:44 +0100
    Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between
    (low-level) languages that want to provide such technical factors
    explicit to the user and between languages that want to provide a
    higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took
    that approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers. The are called access types. https://en.wikibooks.org/wiki/Ada_Programming/Types/access

    I never learned Algol-68, but considering your reputation I'd want
    to see confirmation from more reliable source.

    I suppose it all depends on what constitutes a pointer, but Algol 68
    has pointers to this extent:

    BEGIN
    INT i := 42;
    [3] INT a := (1, 2, 3);

    REF INT p := i; CO p is a "pointer" to i
    CO REF INT(p) := 99; CO changes i via p
    CO p := a[2]; CO p now points to the second element of array a
    CO REF INT(p) := 99; CO change that element via p
    CO

    print((i, a[1], a[2], a[3]))
    END

    I'd call p a pointer.


    Thank you.
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.






    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Sun Apr 14 10:29:16 2024
    On Sat, 13 Apr 2024 20:33:03 +0300, Michael S wrote:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers.

    That’s why I said “explicit”.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sun Apr 14 13:05:21 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    On Sat, 13 Apr 2024 20:33:03 +0300, Michael S wrote:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers.

    That’s why I said “explicit”.

    How is "*" (C) more explicit than "access" (Ada)?

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Sun Apr 14 20:17:36 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Sat, 13 Apr 2024 19:37:37 +0200, Janis Papanagnou wrote:
    ....
    But maybe you can explain where or how there's something "thrown away".

    This is a valid Algol 68 program (just tested with a68g):

    BEGIN PROC f = REAL : 3.1415; f END

    It calls f, which returns a real, which is then cast to VOID -- i.e.
    thrown away.

    You are just arguing about how you want to use an informal term: namely
    to "throw away". In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other. The coercion
    called voiding converts a value of some other type to the single value
    of type void. You could describe this a "throwing away the value", or
    you could agree with Janis and say that the serial-clause you wrote
    yields the sole value of type void.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sun Apr 14 22:51:06 2024
    On 14/04/2024 11:17, Ben Bacarisse wrote:
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Sat, 13 Apr 2024 19:37:37 +0200, Janis Papanagnou wrote:
    ...
    But maybe you can explain where or how there's something "thrown away".

    This is a valid Algol 68 program (just tested with a68g):

    BEGIN PROC f = REAL : 3.1415; f END

    It calls f, which returns a real, which is then cast to VOID -- i.e.
    thrown away.

    You are just arguing about how you want to use an informal term: namely
    to "throw away". In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other. The coercion
    called voiding converts a value of some other type to the single value
    of type void. You could describe this a "throwing away the value", or
    you could agree with Janis and say that the serial-clause you wrote
    yields the sole value of type void.


    It looks to me as though it yields a type REAL, as would this:

    BEGIN 1.23 END

    If the context has no need for such a type, then it is discarded
    (although my own language would report an error for constructs like this
    that make no sense by themselves; most likely it is an actual mistake).

    But in one like this:

    REAL x := BEGIN 1.23 END

    Then that REAL value would be utilised.

    Maybe you mean that in the first case, that REAL value is coerced to
    VOID, and that process is what causes it to be discarded. To me that
    sounds unwieldy.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From fir@3:633/280.2 to All on Mon Apr 15 04:34:28 2024
    bart wrote:

    That's not an interesting use of a local function! You can move square() outside, and it would still work, putting aside any clashes with
    existing names called 'square'.

    The challenges of local functions are to do with accessing transient variables belonging to their enclosing function. Especially when a
    reference to the local function is called from outside the lexical scope
    of the enclosing function, with extra difficulties when the enclosing function is no longer active.


    very good point

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: i2pn2 (i2pn.org) (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Mon Apr 15 07:47:26 2024
    bart <bc@freeuk.com> writes:

    On 14/04/2024 11:17, Ben Bacarisse wrote:
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Sat, 13 Apr 2024 19:37:37 +0200, Janis Papanagnou wrote:
    ...
    But maybe you can explain where or how there's something "thrown away". >>>
    This is a valid Algol 68 program (just tested with a68g):

    BEGIN PROC f = REAL : 3.1415; f END

    It calls f, which returns a real, which is then cast to VOID -- i.e.
    thrown away.
    You are just arguing about how you want to use an informal term: namely
    to "throw away". In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other. The coercion
    called voiding converts a value of some other type to the single value
    of type void. You could describe this a "throwing away the value", or
    you could agree with Janis and say that the serial-clause you wrote
    yields the sole value of type void.

    It looks to me as though it yields a type REAL, as would this:

    BEGIN 1.23 END

    If the context has no need for such a type, then it is discarded (although
    my own language would report an error for constructs like this that make no sense by themselves; most likely it is an actual mistake).

    But in one like this:

    REAL x := BEGIN 1.23 END

    Then that REAL value would be utilised.

    But what post posted was a whole program. The context -- it being the top-level serial clause matters. I think. I say I think because I'd
    like to find the wording that makes this so. a68g says it, but a68g is
    an implementation and not the final word.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Mon Apr 15 08:44:25 2024
    On Sun, 14 Apr 2024 11:17:36 +0100, Ben Bacarisse wrote:

    In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other.

    It has only a single value, EMPTY. Therefore, the number of bits required
    to store a VOID value is ... zero.

    That’s what “throwing away” means.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Chris M. Thomasson@3:633/280.2 to All on Mon Apr 15 13:35:09 2024
    On 4/14/2024 3:44 PM, Lawrence D'Oliveiro wrote:
    On Sun, 14 Apr 2024 11:17:36 +0100, Ben Bacarisse wrote:

    In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other.

    It has only a single value, EMPTY. Therefore, the number of bits required
    to store a VOID value is ... zero.

    How to denote EMPTY?

    n0 := EMPTY
    n1 := EMPTY
    n2 := EMPTY

    So, this takes zero bits to store? I must be missing something here. Sorry.


    That’s what “throwing away” means.





    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Mon Apr 15 23:07:37 2024
    On 14.04.2024 02:29, Michael S wrote:
    On Sun, 14 Apr 2024 00:23:44 +0100
    Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between
    (low-level) languages that want to provide such technical factors
    explicit to the user and between languages that want to provide a
    higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took
    that approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers. The are called access types.
    https://en.wikibooks.org/wiki/Ada_Programming/Types/access

    I never learned Algol-68, but considering your reputation I'd want
    to see confirmation from more reliable source.

    I suppose it all depends on what constitutes a pointer, but Algol 68
    has pointers to this extent:

    BEGIN
    INT i := 42;
    [3] INT a := (1, 2, 3);

    REF INT p := i; CO p is a "pointer" to i
    CO REF INT(p) := 99; CO changes i via p
    CO p := a[2]; CO p now points to the second element of array a
    CO REF INT(p) := 99; CO change that element via p
    CO

    print((i, a[1], a[2], a[3]))
    END

    I'd call p a pointer.


    Thank you.
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68. WRT 'REF' you have to
    understand that it's a sort of "technical" reference item that
    is used to link structures as pointers do. And besides that you
    also have allocators for stack or heap objects. You combine the
    two to get what you have in other languages. Usually you don't
    see the stack allocator because Algol 68 allows abbreviations
    to write only brief declarations what we also know from other
    programming languages. For example the following are the same

    INT a := 42;

    REF INT a = LOC INT := 42;

    It says that a is an integer 'REF' ("lvalue"), identical to a
    local (stack) item - where LOC is the generator -, and finally
    the value assigned.

    You can replace the 'LOC' by 'HEAP' if you want the heap generator
    to allocate the memory. For example

    HEAP INT a;

    REF INT a = HEAP INT;

    In Algol 68 (as in Pascal) these references are bound to the type
    (in the above examples to 'INT').

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Tue Apr 16 01:46:17 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    On Sun, 14 Apr 2024 00:23:44 +0100
    Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Sat, 13 Apr 2024 00:13:36 -0000 (UTC)
    Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    On Fri, 12 Apr 2024 14:35:47 +0200, Janis Papanagnou wrote:

    It seems that's one of the fundamental differences between
    (low-level) languages that want to provide such technical factors
    explicit to the user and between languages that want to provide a
    higher abstraction.

    Algol 60, Pascal, Simula 67 and Algol 60, Eiffel, etc. all took
    that approach.

    Pascal had explicit pointers, though. Algol 68 and Ada did not.

    Of course, Ada has pointers. The are called access types.
    https://en.wikibooks.org/wiki/Ada_Programming/Types/access

    I never learned Algol-68, but considering your reputation I'd want
    to see confirmation from more reliable source.

    I suppose it all depends on what constitutes a pointer, but Algol 68
    has pointers to this extent:

    BEGIN
    INT i := 42;
    [3] INT a := (1, 2, 3);

    REF INT p := i; CO p is a "pointer" to i
    CO REF INT(p) := 99; CO changes i via p
    CO p := a[2]; CO p now points to the second element of array a
    CO REF INT(p) := 99; CO change that element via p
    CO

    print((i, a[1], a[2], a[3]))
    END

    I'd call p a pointer.


    Thank you.
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Tue Apr 16 02:50:03 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Sun, 14 Apr 2024 11:17:36 +0100, Ben Bacarisse wrote:

    In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other.

    It has only a single value, EMPTY. Therefore, the number of bits required
    to store a VOID value is ... zero.

    That’s what “throwing away” means.

    I did not have any trouble understanding what you meant by "throwing
    away". I was simply pointing out that there is another way to look at
    the voiding coercion that is entirely consistent with what Janis had
    said.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Tue Apr 16 02:50:45 2024
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:

    On 4/14/2024 3:44 PM, Lawrence D'Oliveiro wrote:
    On Sun, 14 Apr 2024 11:17:36 +0100, Ben Bacarisse wrote:

    In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other.
    It has only a single value, EMPTY. Therefore, the number of bits required
    to store a VOID value is ... zero.

    How to denote EMPTY?

    Algol 68 has no denotation for the empty value.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 16 04:36:58 2024
    On 15.04.2024 05:35, Chris M. Thomasson wrote:
    On 4/14/2024 3:44 PM, Lawrence D'Oliveiro wrote:
    On Sun, 14 Apr 2024 11:17:36 +0100, Ben Bacarisse wrote:

    In Algol 68, the mode (AKA type) "void" is,
    conceptually, a collection of values like any other.

    It has only a single value, EMPTY. Therefore, the number of bits required
    to store a VOID value is ... zero.

    How to denote EMPTY?

    n0 := EMPTY
    n1 := EMPTY
    n2 := EMPTY

    So, this takes zero bits to store? I must be missing something here. Sorry.

    I suppose it's just the way how one looks onto it. If I program in
    "C" I always seem to have some bits and bytes in mind, as opposed
    to Algol 68 where I seem to be thinking more abstract (high-level).

    I usually don't think about whether there's a -1, 0, 42, value used
    to represent 'EMPTY' in memory. This may stem from the CS education
    where I've learned not to assume a physical von Neumann architecture
    with registers etc. to be a precondition for an implementation. Such
    cases (while they likely are) need not be implemented using numbers
    in a memory [in theory].

    Other such values like 'EMPTY' are also known from other languages;
    like the Algol 68 terms 'NIL' ('null', 'NULL', 'nil'), and 'SKIP'.

    If any of these keywords will be internally represented by special
    values (like 0 or -1) and special CPU commands (like 'NOP') is not
    really important since you don't work with such low-level entities
    (in cases there are such low-level entities in the first place).
    If you use a 'NIL' you just test with 'ISNT NIL', for example.

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)

    Your question: "So, this takes zero bits to store?" - I can't tell.

    We could look into the Algol 68 Genie compiler, but you wouldn't
    get a general answer, just a very specific (useless) information.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Tue Apr 16 05:00:30 2024
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    <Algol 68 code elided>
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    int i = 42;
    memcpy(&i, &(float){3.14}, sizeof i);
    printf("%d\n", i);

    That looks like a pretty loose association between pointer and object
    type to me. This is not an accident or an unintended loophole. It's by design.

    Certainly every pointer value in C has an associated type, but the
    intention is that this association can be changed by pointer type
    conversion as needed.

    You often /have/ to make us of this. For example, when calling qsort,
    you will usually change the association between the pointer and the pointed-to type (void) in order to do the comparison. And C does not
    even insist that you change it back to the original pointed-to type as
    you can legally write a comparison function for, say, float data that
    uses the representation as "raw" bytes.

    OK. The way I'd describe it is that C (non-void) pointers *are*
    "closely bound to the type", but in addition C provides operations, particularly pointer casts and implicit conversions to and from void*,
    that can override that binding.

    Janis mentioned pointer arithmetic. I wouldn't say that overrides the
    type binding; it merely provides a set of operations, that some other
    languages lack, for constructing a pointer value from another pointer
    value. I don't know whether Janis meant that to be an example of not
    being closely bound to the type, or as a distinct statement.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Tue Apr 16 05:24:02 2024
    On 15/04/2024 20:00, Keith Thompson wrote:
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    <Algol 68 code elided>
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    int i = 42;
    memcpy(&i, &(float){3.14}, sizeof i);
    printf("%d\n", i);

    That looks like a pretty loose association between pointer and object
    type to me. This is not an accident or an unintended loophole. It's by
    design.

    Certainly every pointer value in C has an associated type, but the
    intention is that this association can be changed by pointer type
    conversion as needed.

    You often /have/ to make us of this. For example, when calling qsort,
    you will usually change the association between the pointer and the
    pointed-to type (void) in order to do the comparison. And C does not
    even insist that you change it back to the original pointed-to type as
    you can legally write a comparison function for, say, float data that
    uses the representation as "raw" bytes.

    OK. The way I'd describe it is that C (non-void) pointers *are*
    "closely bound to the type", but in addition C provides operations, particularly pointer casts and implicit conversions to and from void*,
    that can override that binding.

    Janis mentioned pointer arithmetic. I wouldn't say that overrides the
    type binding; it merely provides a set of operations, that some other languages lack, for constructing a pointer value from another pointer
    value. I don't know whether Janis meant that to be an example of not
    being closely bound to the type, or as a distinct statement.

    Implicit in every C non-void object pointer, is that it points to an
    element of an array, so that you access neighbouring elements via P+1,
    P-2, ++P and so on.

    That is the case whether or not P actually points within such a sequence.

    That assumption appears to be missing from those other languages.

    C itself doesn't seem that bothered; it just treats any isolated object
    that a pointer refers to, as though it was part of an imaginary
    1-element array.

    Then any attempt to refer beyond it via pointer arithmetic, is a mere
    bounds error. But it's a bit more serious than that. It's like treating
    *NULL as a bounds error, because it might be a few MB away from the
    nearest valid object.

    (My own lower level language allows similar pointer arithmetic. But it
    doesn't allow P[i] syntax; pointers and arrays are more distinct and
    therefore safer.

    With an older dynamic language, pointers had a length attribute
    attached. So that (IIRC) single targets had a length of 1 so pointer arithmetic was limited.)


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Tue Apr 16 06:37:35 2024
    bart <bc@freeuk.com> writes:

    On 15/04/2024 20:00, Keith Thompson wrote:
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    <Algol 68 code elided>
    It looks closer to C than to Pascal, i.e. pointer can point to any >>>>>> object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    int i = 42;
    memcpy(&i, &(float){3.14}, sizeof i);
    printf("%d\n", i);

    That looks like a pretty loose association between pointer and object
    type to me. This is not an accident or an unintended loophole. It's by >>> design.

    Certainly every pointer value in C has an associated type, but the
    intention is that this association can be changed by pointer type
    conversion as needed.

    You often /have/ to make us of this. For example, when calling qsort,
    you will usually change the association between the pointer and the
    pointed-to type (void) in order to do the comparison. And C does not
    even insist that you change it back to the original pointed-to type as
    you can legally write a comparison function for, say, float data that
    uses the representation as "raw" bytes.
    OK. The way I'd describe it is that C (non-void) pointers *are*
    "closely bound to the type", but in addition C provides operations,
    particularly pointer casts and implicit conversions to and from void*,
    that can override that binding.
    Janis mentioned pointer arithmetic. I wouldn't say that overrides the
    type binding; it merely provides a set of operations, that some other
    languages lack, for constructing a pointer value from another pointer
    value. I don't know whether Janis meant that to be an example of not
    being closely bound to the type, or as a distinct statement.

    Implicit in every C non-void object pointer, is that it points to an
    element of an array, so that you access neighbouring elements via P+1, P-2, ++P and so on.

    That is the case whether or not P actually points within such a
    sequence.

    No that is not implicit in every non-void object pointer. In fact, it
    is explicitly stated in the language standard that you /can't/ access /anything/ via the "neighbouring" pointers except when the original is a pointer into an array and those neighbouring elements are also in the
    same array.

    That assumption appears to be missing from those other languages.

    It's missing in C too. Address arithmetic and access is permitted only
    within an array.

    C itself doesn't seem that bothered; it just treats any isolated object
    that a pointer refers to, as though it was part of an imaginary 1-element array.

    This is true, and it's not implicit -- it is explicitly stated in the
    language standard.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 16 06:39:37 2024
    On 15.04.2024 21:00, Keith Thompson wrote:
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    <Algol 68 code elided>
    It looks closer to C than to Pascal, i.e. pointer can point to any
    object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    int i = 42;
    memcpy(&i, &(float){3.14}, sizeof i);
    printf("%d\n", i);

    That looks like a pretty loose association between pointer and object
    type to me. This is not an accident or an unintended loophole. It's by
    design.

    Certainly every pointer value in C has an associated type, but the
    intention is that this association can be changed by pointer type
    conversion as needed.

    You often /have/ to make us of this. For example, when calling qsort,
    you will usually change the association between the pointer and the
    pointed-to type (void) in order to do the comparison. And C does not
    even insist that you change it back to the original pointed-to type as
    you can legally write a comparison function for, say, float data that
    uses the representation as "raw" bytes.

    OK. The way I'd describe it is that C (non-void) pointers *are*
    "closely bound to the type", but in addition C provides operations, particularly pointer casts and implicit conversions to and from void*,
    that can override that binding.

    Janis mentioned pointer arithmetic. I wouldn't say that overrides the
    type binding; it merely provides a set of operations, that some other languages lack, for constructing a pointer value from another pointer
    value. I don't know whether Janis meant that to be an example of not
    being closely bound to the type, or as a distinct statement.

    It's no "lacking operations". It's a deliberate design for safety.

    Rutishauser spoke (for Algol 60) about "leaving the Can of Pandora
    closed". And F. L. Bauer noted that "Pascal opened the Can of Pandora
    but put a fly screen over it". (Pascal can here be seen as an example
    of one language, other HLLs have done it basically the same way.)

    The rest can probably be derived from inspecting how various HLL do
    support that and how C does it. Basically,
    - memory of appropriate size is allocated
    - the reference is assigned to the variable
    - access is done without address modification possible
    You may consider that from a low level perspective a missing feature,
    but I call it not unnecessarily opening a can of issues. - Examples...

    Pascal: var p : ^T; new (p); p^.x := ... ;

    Algol 68: REF T p = HEAP T; x OF p := ... ;

    Simula 67: REF (T) p; p :- new T; p.x := ... ;


    C: T * p = (T *) malloc (sizeof(T)); (p+42) = ... ;

    Yes, you would not write that, but you can.
    And all sorts of casts makes it even worse.
    In the other languages mentioned you can't.

    C++: with it's 'new' (and also 'malloc') is somewhat ambivalent.

    (Disclaimers:
    1. Code snippets may contain errors.
    2. I don't know of newer C standards, just suppose that nothing
    substantial has changed in this respect to fix the inherent
    original C design criteria.)

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 16 06:54:45 2024
    On 15.04.2024 22:39, Janis Papanagnou wrote:

    C: T * p = (T *) malloc (sizeof(T)); (p+42) = ... ;

    (Disclaimers:
    1. Code snippets may contain errors.

    One error I made...

    *(p+42) = ... ;

    or
    p[42] = ... ;


    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Tue Apr 16 07:25:00 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 15.04.2024 21:00, Keith Thompson wrote:
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 14.04.2024 02:29, Michael S wrote:
    <Algol 68 code elided>
    It looks closer to C than to Pascal, i.e. pointer can point to any >>>>>> object rather than just to dynamically allocated object.

    The major difference is that they are closely bound to the type.
    In that respect they are more like Pascal pointers. C pointers
    open the can of issues with arithmetic on pointer values. You
    don't find that in Pascal or Algol 68.
    [...]

    How are C pointers not "closely bound to the type"?

    int i = 42;
    memcpy(&i, &(float){3.14}, sizeof i);
    printf("%d\n", i);

    That looks like a pretty loose association between pointer and object
    type to me. This is not an accident or an unintended loophole. It's by >>> design.

    Certainly every pointer value in C has an associated type, but the
    intention is that this association can be changed by pointer type
    conversion as needed.

    You often /have/ to make us of this. For example, when calling qsort,
    you will usually change the association between the pointer and the
    pointed-to type (void) in order to do the comparison. And C does not
    even insist that you change it back to the original pointed-to type as
    you can legally write a comparison function for, say, float data that
    uses the representation as "raw" bytes.

    OK. The way I'd describe it is that C (non-void) pointers *are*
    "closely bound to the type", but in addition C provides operations,
    particularly pointer casts and implicit conversions to and from void*,
    that can override that binding.

    Janis mentioned pointer arithmetic. I wouldn't say that overrides the
    type binding; it merely provides a set of operations, that some other
    languages lack, for constructing a pointer value from another pointer
    value. I don't know whether Janis meant that to be an example of not
    being closely bound to the type, or as a distinct statement.

    It's no "lacking operations". It's a deliberate design for safety.

    My use of the word "lack" wasn't meant as a value judgement.

    Certainly C allows the association of a pointer with the type of the
    object it points to be overridden. I'd still say (again) that C
    non-void pointers are "closely bound to the type" -- closely, but not irrevocably.

    Many more type-strict languages permit similar overriding of that
    association, perhaps with a more explicit syntax (see "unsafe" in Rust, "Unchecked_Conversion" in Ada).

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Tue Apr 16 08:30:16 2024
    On Mon, 15 Apr 2024 17:50:45 +0100, Ben Bacarisse wrote:

    Algol 68 has no denotation for the empty value.

    Section 8.1.5.1 of the Revised Report:

    void denotation : empty symbol.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Tue Apr 16 08:32:45 2024
    On Mon, 15 Apr 2024 20:36:58 +0200, Janis Papanagnou wrote:

    I usually don't think about whether there's a -1, 0, 42, value used to represent 'EMPTY' in memory. This may stem from the CS education where
    I've learned not to assume a physical von Neumann architecture with
    registers etc. ...

    The concept of “bits” comes from information theory. It has nothing to do with von Neumann or Paul Newman or Gary Oldman or anything else. If you
    have zero information to convey, then the number of bits of information is zero.

    Other such values like 'EMPTY' are also known from other languages; like
    the Algol 68 terms 'NIL' ('null', 'NULL', 'nil'), and 'SKIP'.

    Those are all entirely different concepts in Algol 68.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Michael S@3:633/280.2 to All on Wed Apr 17 06:11:34 2024
    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)


    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.
    Both languages originated from common ancestor (Algol-60) and changed
    it in similar directions, e.g. blurring the line between operators and expression, making function pointers first class citizen, allowing
    declaration of variables at block scope.
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Thu Apr 18 03:09:06 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Mon, 15 Apr 2024 17:50:45 +0100, Ben Bacarisse wrote:

    Algol 68 has no denotation for the empty value.

    Section 8.1.5.1 of the Revised Report:

    void denotation : empty symbol.

    Thanks. If I had ever known that, I had forgotten. It's almost never
    used because values are coerced to void all the contexts where it might
    be otherwise required. I'm pretty sure I've never written it.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Lawrence D'Oliveiro@3:633/280.2 to All on Thu Apr 18 08:12:25 2024
    On Wed, 17 Apr 2024 18:09:06 +0100, Ben Bacarisse wrote:

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Mon, 15 Apr 2024 17:50:45 +0100, Ben Bacarisse wrote:

    Algol 68 has no denotation for the empty value.

    Section 8.1.5.1 of the Revised Report:

    void denotation : empty symbol.

    Thanks. If I had ever known that, I had forgotten. It's almost never
    used because values are coerced to void all the contexts where it might
    be otherwise required. I'm pretty sure I've never written it.

    I also did a quick test in a68g that “BEGIN EMPTY END” is a valid program (at least compiles without errors). But the last time I posted such a
    thing, someone claimed that a68g was not necessarily a fully conforming
    Algol 68 implementation ...

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Apr 20 01:26:44 2024
    Michael S <already5chosen@yahoo.com> writes:

    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)

    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.
    Both languages originated from common ancestor (Algol-60) and changed
    it in similar directions, e.g. blurring the line between operators and expression, making function pointers first class citizen, allowing declaration of variables at block scope.
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.

    Algol 60 already had block scope declarations.

    Algol 60 may not have had (pointer to) function/procedure variables,
    but it did allow procedure identifiers as arguments to a procedure
    call, and procedure variables are an obvious generalization.

    Relative to Algol 60, C slightly expanded what forms are allowed in expressions, but mainly as a way to simplify the language syntax:

    * no separate cases for assignment / function call statements

    * so for()'s are more general and don't need specializing

    In contrast, in Algol 68 the notion of "expression" is expanded
    to allow the possibility of arbitrary variable declarations and
    loops (IIANM; certainly I am not an expert on Algol 68).

    Furthermore there are some significant differences between C and
    Algol 60:

    * Algol allows nested functions (aka procedures), but C doesn't

    * Algol has call by name, C is strictly call by value

    * Arrays are first class types in Algol, but not in C (and C
    has pointer arithmetic as an essential part of the language,
    which TTBOMK is not the case in any Algol-derived language)

    * Algol is "strict" whereas C is "lax" - for example, in Algol
    the controlling expression of an 'if' statement must be a
    'Boolean expression', whereas in C it's just any expression

    To me, Algol 68 represents an expansion and extension of the
    Algol 60 language philosophy, whereas C represents a deliberate
    departure from that philosophy; not necessarily a radical
    departure, but a departure nonetheless. Certainly C has some
    similarities to Algol 68, but I wouldn't say C and Algol 68
    are similar languages, only that they have a few similarities.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From bart@3:633/280.2 to All on Sat Apr 20 04:34:40 2024
    On 19/04/2024 16:26, Tim Rentsch wrote:
    Michael S <already5chosen@yahoo.com> writes:

    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)

    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.
    Both languages originated from common ancestor (Algol-60) and changed
    it in similar directions, e.g. blurring the line between operators and
    expression, making function pointers first class citizen, allowing
    declaration of variables at block scope.
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.

    Algol 60 already had block scope declarations.

    Algol 60 may not have had (pointer to) function/procedure variables,
    but it did allow procedure identifiers as arguments to a procedure
    call, and procedure variables are an obvious generalization.

    Relative to Algol 60, C slightly expanded what forms are allowed in expressions, but mainly as a way to simplify the language syntax:

    * no separate cases for assignment / function call statements

    * so for()'s are more general and don't need specializing

    In contrast, in Algol 68 the notion of "expression" is expanded
    to allow the possibility of arbitrary variable declarations and
    loops (IIANM; certainly I am not an expert on Algol 68).

    Furthermore there are some significant differences between C and
    Algol 60:

    * Algol allows nested functions (aka procedures), but C doesn't

    * Algol has call by name, C is strictly call by value

    * Arrays are first class types in Algol, but not in C (and C
    has pointer arithmetic as an essential part of the language,
    which TTBOMK is not the case in any Algol-derived language)

    * Algol is "strict" whereas C is "lax" - for example, in Algol
    the controlling expression of an 'if' statement must be a
    'Boolean expression', whereas in C it's just any expression

    To me, Algol 68 represents an expansion and extension of the
    Algol 60 language philosophy, whereas C represents a deliberate
    departure from that philosophy; not necessarily a radical
    departure, but a departure nonetheless. Certainly C has some
    similarities to Algol 68, but I wouldn't say C and Algol 68
    are similar languages, only that they have a few similarities.


    I can't see any connection between Algol68 and C; I'm surprised at
    people who say they do.

    C was put together together as a systems language to do a job, not to implemement some esoteric concept of a language that few could understand.

    I understood it was based on languages like B and BCPL.

    Actually there is a closer connection between my systems language,
    created 10 years after C, which was also created for purely practical purposes, and Algol68, largely because I borrowed much of its syntax (including the interchangeability of statements and expressions).

    But I would be first to admit that the similarity stops there.




    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sat Apr 20 05:15:52 2024
    scott@slp53.sl.home (Scott Lurndal) writes:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Michael S <already5chosen@yahoo.com> writes:

    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)

    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.
    Both languages originated from common ancestor (Algol-60) and changed
    it in similar directions, e.g. blurring the line between operators and
    expression, making function pointers first class citizen, allowing
    declaration of variables at block scope.
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.

    Algol 60 already had block scope declarations.

    Algol 60 may not have had (pointer to) function/procedure variables,
    but it did allow procedure identifiers as arguments to a procedure
    call, and procedure variables are an obvious generalization.

    Call-by-name.

    Yes, the article you replied to already mentioned call by name, in text
    that you snipped.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sat Apr 20 05:18:19 2024
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    * Algol is "strict" whereas C is "lax" - for example, in Algol
    the controlling expression of an 'if' statement must be a
    'Boolean expression', whereas in C it's just any expression

    Small quibble: the controlling expression in C must be of scalar type.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Ben Bacarisse@3:633/280.2 to All on Sat Apr 20 11:11:15 2024
    bart <bc@freeuk.com> writes:

    On 19/04/2024 16:26, Tim Rentsch wrote:
    ....
    ... Certainly C has some
    similarities to Algol 68, but I wouldn't say C and Algol 68
    are similar languages, only that they have a few similarities.

    I can't see any connection between Algol68 and C; I'm surprised at people
    who say they do.

    "Connection" is vague, but Dennis Ritchie has written about the
    influence of Algol 68 on C.

    C was put together together as a systems language to do a job, not to implemement some esoteric concept of a language that few could understand.

    I understood it was based on languages like B and BCPL.

    BCPL had assignment statements but B followed Algol 68 and made
    assignment an expression operator (along with the +=, -= etc versions).
    This, of course, fed directly into C.

    DMR also cites Algol 68's type composition as an influence on C's type semantics, and casts come straight from Algol 68.

    --
    Ben.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Keith Thompson@3:633/280.2 to All on Sat Apr 20 11:24:49 2024
    Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
    bart <bc@freeuk.com> writes:
    On 19/04/2024 16:26, Tim Rentsch wrote:
    ...
    ... Certainly C has some
    similarities to Algol 68, but I wouldn't say C and Algol 68
    are similar languages, only that they have a few similarities.

    I can't see any connection between Algol68 and C; I'm surprised at people
    who say they do.

    "Connection" is vague, but Dennis Ritchie has written about the
    influence of Algol 68 on C.

    See <https://www.bell-labs.com/usr/dmr/www/chist.pdf, which has multiple references to Algol 68.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    Working, but not speaking, for Medtronic
    void Void(void) { Void(); } /* The recursive call of the void */

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: None to speak of (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Apr 20 22:55:17 2024
    scott@slp53.sl.home (Scott Lurndal) writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Michael S <already5chosen@yahoo.com> writes:

    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)

    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.
    Both languages originated from common ancestor (Algol-60) and changed
    it in similar directions, e.g. blurring the line between operators and
    expression, making function pointers first class citizen, allowing
    declaration of variables at block scope.
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.

    Algol 60 already had block scope declarations.

    Algol 60 may not have had (pointer to) function/procedure variables,
    but it did allow procedure identifiers as arguments to a procedure
    call, and procedure variables are an obvious generalization.

    Call-by-name.

    Algol 60 also had call by name (already mentioned in my previous
    post), but that is unrelated to allowing procedure identifiers
    as arguments.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Tim Rentsch@3:633/280.2 to All on Sat Apr 20 23:03:32 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]

    * Algol is "strict" whereas C is "lax" - for example, in Algol
    the controlling expression of an 'if' statement must be a
    'Boolean expression', whereas in C it's just any expression

    Small quibble: the controlling expression in C must be of scalar type.

    Yes but my comment was only about syntax. In Algol 60 a
    'Boolean expression' is a syntactic category unrelated
    to any semantic analysis. In C there is no separate
    syntactic catgory for "boolean" expressions; there is
    only 'expression'.

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Kaz Kylheku@3:633/280.2 to All on Sun Apr 21 01:35:26 2024
    On 2024-04-20, Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
    bart <bc@freeuk.com> writes:

    On 19/04/2024 16:26, Tim Rentsch wrote:
    ...
    ... Certainly C has some
    similarities to Algol 68, but I wouldn't say C and Algol 68
    are similar languages, only that they have a few similarities.

    I can't see any connection between Algol68 and C; I'm surprised at people
    who say they do.

    "Connection" is vague, but Dennis Ritchie has written about the
    influence of Algol 68 on C.

    I was looking at both Algols recently; they have a go to statement
    with local statement labels, very similar to C.

    C was put together together as a systems language to do a job, not to
    implemement some esoteric concept of a language that few could understand. >>
    I understood it was based on languages like B and BCPL.

    BCPL had assignment statements but B followed Algol 68 and made
    assignment an expression operator (along with the +=, -= etc versions).
    This, of course, fed directly into C.

    Algol 60 included a selection operator, suggested by John MacCarthy
    of Lisp. That's likely how C ended up with the A ? B : C syntax.

    That's actually funny because MacCarthy first invented a three-operand
    IF operator, which he simulated via a function in Fortran. He used
    that to simplify his chess program and other programs. (This account
    is given in his History of Lisp article). His Fortran XIF, being
    a function, didn't have the evaluation semantics he wanted such that
    XIF(A, B, C) would evaluate only one of A and B. He somehow evolved this three-way if into the multi-branch conditional which he then favored; it
    became the Lisp cond with the M-expression surface syntax, and that
    being similar to what he proposed for Algol. Eventually Lisp people
    also went full circle, providing a two/three argument if operator or
    macro, alongside cond.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 23 01:41:09 2024
    On 16.04.2024 00:32, Lawrence D'Oliveiro wrote:
    On Mon, 15 Apr 2024 20:36:58 +0200, Janis Papanagnou wrote:

    I usually don't think about whether there's a -1, 0, 42, value used to
    represent 'EMPTY' in memory. This may stem from the CS education where
    I've learned not to assume a physical von Neumann architecture with
    registers etc. ...

    The concept of “bits” comes from information theory. It has nothing to do
    with von Neumann or Paul Newman or Gary Oldman or anything else. If you
    have zero information to convey, then the number of bits of information is zero.

    Thanks for trying to teach me CS.

    I'm not speaking about "concepts" I'm speaking either about "units"
    or about "[physical] representations"; here about the latter.

    Concerning language - and I cannot speak for English, but suppose
    there's something similar there as in my native language - we
    differentiate between entities and units; we have the unit "bit"
    (not "bits"; e.g. "8 bit") and the concrete physical or whatever
    entity that hereabouts you refer to as "Bit". In cases where I
    speak about memory _representations_ the word to choose is "Bit",
    like "Bytes", "Octets", etc.


    Other such values like 'EMPTY' are also known from other languages; like
    the Algol 68 terms 'NIL' ('null', 'NULL', 'nil'), and 'SKIP'.

    Those are all entirely different concepts in Algol 68.

    Yes, sure. That's why I wrote "like". - Not sure why you make
    so many meaningless posts.

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 23 01:49:06 2024
    On 16.04.2024 22:11, Michael S wrote:
    On Mon, 15 Apr 2024 20:36:58 +0200
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    Algol 68 and C are so different that mutual understanding might be
    difficult depending on personal background, focus, and fantasy. :-)


    Interesting take.
    I never learned Algol-68, but from pieces of info that I occasionally
    got I was always thinking of it as rather similar to 'C'.

    Well, they're from the same language family. But there's some
    fundamental differences, also fundamental different approaches,
    and different design principles.

    At some abstraction point we can say that, say, Fortran and C
    or Algol 68, have all for-loops and run on von Neumann systems
    but from a design perspective all three sample languages here
    are very different.

    [...]
    I think, in the past, when I remembered more about Algol-68, I had seen
    more similarities.

    It's the criterons you choose that make us see differences or
    similarities (or even equalities). And the set of criterions
    is typically chosen from the specific point of view (personal
    or analytical or requirements or...).

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)
  • From Janis Papanagnou@3:633/280.2 to All on Tue Apr 23 02:06:53 2024
    On 18.04.2024 00:12, Lawrence D'Oliveiro wrote:
    On Wed, 17 Apr 2024 18:09:06 +0100, Ben Bacarisse wrote:

    Lawrence D'Oliveiro <ldo@nz.invalid> writes:

    On Mon, 15 Apr 2024 17:50:45 +0100, Ben Bacarisse wrote:

    Algol 68 has no denotation for the empty value.

    Section 8.1.5.1 of the Revised Report:

    void denotation : empty symbol.

    Thanks. If I had ever known that, I had forgotten. It's almost never
    used because values are coerced to void all the contexts where it might
    be otherwise required. I'm pretty sure I've never written it.

    I also did a quick test in a68g that “BEGIN EMPTY END” is a valid program
    (at least compiles without errors). But the last time I posted such a
    thing, someone claimed that a68g was not necessarily a fully conforming Algol 68 implementation ...

    I don't see why it shouldn't be valid program; I mean you can write
    programs like "BEGIN END", "BEGIN EMPTY END", "BEGIN SKIP END",
    "BEGIN NIL END", "BEGIN 3.14 END", or, "BEGIN 42 END".
    I wouldn't expect that these do anything; only in the latter cases
    I'd at least see a point in returning that value to the environment
    (which isn't done, though, with the Genie compiler).

    Janis


    --- MBSE BBS v1.0.8.4 (Linux-x86_64)
    * Origin: A noiseless patient Spider (3:633/280.2@fidonet)