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
If folks learned coding from imperative languages they have more
problems with a functional view (and also with recursion, it seems).
/*
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.
*/
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 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.
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 ...
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).
[*] 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_ }
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.
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.
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
...
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 ...
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.
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 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.
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.
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);
}
int sum_square(int x, int y) {
auto square = [](int z) {
return z * z;
}
return square(x) + square(y);
}
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.
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.
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.
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?
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.
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*/
[...]
Basically, anything you could do with a nested function in gcc C you can
do in C++:
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.
The Burroughs Algol systems used something called lex levels to handle
nested functions.
Just for your entertainment, with C++ lambdas this is now legal code:
void foo(void) {
[](){}();
}
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”?
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?
(Apologies to Kenny for the "ibid.")
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”?
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?
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>
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.
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.
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.
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.
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)
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:C programmer still has habit of writing “(void)” when C++ allows
void foo(void) {
[](){}();
}
“()”
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.)
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?
As someone who cut his teeth on
Unix V6, an empty parameter list is less self-documenting than an
explicit (void).
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();
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();
?
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.
Ideally it would be (without syntactic ballast) just
res = func;
(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.)
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:C programmer still has habit of writing “(void)” when C++ allows
void foo(void) {
[](){}();
}
“()”
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.
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.
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.
[ snip ]
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( */
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.)
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.
[...]
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.
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.
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.
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.
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.
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).
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?)
[properties of some other languages]
But we use C here, so we have to take what's given.
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?
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.
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 :-)
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.
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.
[...]
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”.
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.
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.
Pascal has pointers bound to the object (as opposed to C pointers).
But maybe you can explain where or how there's something "thrown away".
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.
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.
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”.
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.
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.
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.
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:...
You are just arguing about how you want to use an informal term: namelyBut 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.
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.
In Algol 68, the mode (AKA type) "void" is,
conceptually, a collection of values like any other.
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.
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.
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.
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.
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,It has only a single value, EMPTY. Therefore, the number of bits required
conceptually, a collection of values like any other.
to store a VOID value is ... zero.
How to denote EMPTY?
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.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:<Algol 68 code elided>
On 14.04.2024 02:29, Michael S wrote:
[...]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.
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:<Algol 68 code elided>
On 14.04.2024 02:29, Michael S wrote:
[...]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.
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:OK. The way I'd describe it is that C (non-void) pointers *are*
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:<Algol 68 code elided>
On 14.04.2024 02:29, Michael S wrote:
[...]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.
"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.
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:<Algol 68 code elided>
On 14.04.2024 02:29, Michael S wrote:
[...]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.
C: T * p = (T *) malloc (sizeof(T)); (p+42) = ... ;
(Disclaimers:
1. Code snippets may contain errors.
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:<Algol 68 code elided>
On 14.04.2024 02:29, Michael S wrote:
[...]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.
Algol 68 has no denotation for the empty value.
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. ...
Other such values like 'EMPTY' are also known from other languages; like
the Algol 68 terms 'NIL' ('null', 'NULL', 'nil'), and 'SKIP'.
Algol 68 and C are so different that mutual understanding might be
difficult depending on personal background, focus, and fantasy. :-)
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.
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.
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.
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.
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 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
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.
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.
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.
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.
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.
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.
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.
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'.
[...]
I think, in the past, when I remembered more about Algol-68, I had seen
more similarities.
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 ...
Sysop: | Tetrazocine |
---|---|
Location: | Melbourne, VIC, Australia |
Users: | 7 |
Nodes: | 8 (0 / 8) |
Uptime: | 128:17:14 |
Calls: | 46 |
Files: | 21,492 |
Messages: | 64,840 |