Tuesday, March 14, 2023

Digest for comp.lang.c++@googlegroups.com - 25 updates in 4 topics

gst <g.starck@gmail.com>: Mar 13 11:49PM -0700

Le lundi 13 mars 2023 à 18:02:00 UTC-4, Paavo Helde a écrit :
 
> For example, a string variable in Python is kind of immutable AFAIK.
 
Pure strings, as floats, ints,.. in Python *are* totally immutable. not kind of. totally.
 
> atomic/scalar value and any references to it only serve as a tool for
> memory optimization and cannot be used for modification of data visible
> via other references.
 
they don't acts like immutable, they are. You cannot modify a string/immutabe objects. they do not have any method with side effect.
 
you can assign a string to something, then that thing can be passed down the line/anywhere, the same string can be assigned to many different things/names by many assignements (many references to them yes). But you cannot, never, modify a string in-place, with any reference to it. It has no such method (even private)/feature/possibility. You could only (try to) modify it by accessing C low level internal of CPython say, the implementation, not the language. but by then it's not anymore Python you're doing and that could/would break at some point your/any Python code obviously.
David Brown <david.brown@hesbynett.no>: Mar 14 09:08AM +0100

On 13/03/2023 23:01, Paavo Helde wrote:
>>> code if the values of variables never changes.
 
>> That I didn't know. I thought immutable variables were a pretty
>> unique part of Erlang.
 
No. It is like mathematics - a mathematician will be happy with "x =
1", but get really upset if you say "x = x + 1" :-)
 
Keeping things immutable makes it far easier to reason about what code
does, and what transformations or optimisations are safe. You get to
move things around, do things in parallel, and get much more structured
and modularised code when you can't have different bits of the code all
affecting the values of bits of data. Of course it also leads to
limitations if you can never change the value of a variable.
 
Applied to C++ programming, it is often good to aim where possible to
have your data and your methods "const", or even "constexpr". But C++
supports a wide range of programming paradigms, so use what suits best
for the task in hand.
 
(There are "impure" functional languages, like ocaml, that allow mutable
variables.)
 
> memory optimization and cannot be used for modification of data visible
> via other references.
 
> At least that's what I've gathered, I'm no expert in Python.
 
The key to understanding Python, IMHO, is to realise that there are no
variables. There are only named references - and most of the things
they point to, are immutable.
 
So in Python, if you write :
 
x = 1
 
you are /not/ creating an integer variable "x". You are creating an
immutable object with type "integer" and value "1", unless such an
object already exists (in which case you are increasing its reference
count). You then have the reference named "x" that points to it.
 
When you then write :
 
x += 1
 
you are not changing "x". You are (roughly speaking) applying the
methods x.__assign__(x.__add__(1)). This results in an object of
integer type and value 2 being created, and "x" is now pointed to it.
The reference count of the object "1" is decreased - if it is 0, the
object can be garbage collected.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 01:17AM -0700


> The standard defines "undefined behavior" as "behavior for which
> this document [the standard] imposes no requirements". I recommend
> not using that particular phrase with a different meaning.
 
Wholeheartedly agree. A lot of people think "undefined behavior" is
synonymous with "might behave in an unpredictable fashion". That
supposed equivalence is simply wrong, and there should never be any
ambiguity about its wrongness.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 01:21AM -0700

> immutable atomic/scalar value and any references to it only serve as a
> tool for memory optimization and cannot be used for modification of
> data visible via other references.
 
String values are immutable, but a string value is not a variable.
The variable can be changed, only the string value is immutable.
 
> At least that's what I've gathered, I'm no expert in Python.
 
Ditto.
David Brown <david.brown@hesbynett.no>: Mar 14 09:55AM +0100

On 13/03/2023 20:53, Keith Thompson wrote:
 
> The standard defines "undefined behavior" as "behavior for which this
> document [the standard] imposes no requirements". I recommend not using
> that particular phrase with a different meaning.
 
I had thought my meaning was clear, but you are absolutely right to
recommend being more precise here. There's been a certain amount of
misunderstanding and miscommunication in this thread already - I should
not make it worse by lazy wording!
 
Muttley@dastardlyhq.com: Mar 14 09:26AM

On Mon, 13 Mar 2023 18:43:11 -0000 (UTC)
>to reason about the code's correctness. For example, concerning recursion,
>I have heard arguments that claim it helps to avoid off-by-one mistakes
>that can happen pretty easily in languages such as C.
 
Quite possibly, but recursion has its own issues. The main one being that
the dev forgets to code in the correct stop checks and one day it recurses
forever. Sure that can happen with a standard for loop, but its more likely to
be spotted.
 
>Erlang processes are isolated from each other, and can communicate only
>using message passing. I remember reading about some kind of Actor Model
>and maybe it's also supposed make code correctness easier to achieve. But
 
Message passing is theoretically pure but horribly inefficient.
 
>book about algorithms, look up what you want, and then implement it
>in Erlang. Erlang is of course Turing complete, so it *is* doable
>but not straightforward.
 
Its difficult to know when one has been steeped in traditional development
style since god was a boy, but I think traditional programming is much closer
to how people think. Eg "Add 1 to this, go around again. Put this value in a
box and put the box in that other box" etc. Functional (and declarative)
programming requires you to learn to think in a different way.
Muttley@dastardlyhq.com: Mar 14 09:31AM

On Mon, 13 Mar 2023 20:17:41 +0100
>C++ never need to be changed after their initialisation or first
>assignment, and it is much easier to reason about the correctness of the
>code if the values of variables never changes.
 
One argument in favour of using #define over const vars.
 
 
>The motto for Erlang is "write once, run forever". It's most fun
>feature, IMHO, is the extent to which you can replace code while
>everything is still running. The idea is that you don't have to stop
 
Which is probably about as accurate as javas write once run everywhere motto.
kalevi@kolttonen.fi (Kalevi Kolttonen): Mar 14 09:51AM

>>feature, IMHO, is the extent to which you can replace code while
>>everything is still running. The idea is that you don't have to stop
 
> Which is probably about as accurate as javas write once run everywhere motto.
 
Concerning "write once": if we are realists, we must admit that
no large program can be written "once" without any need for
later bugfixes or enhancements. So "once" in this context
cannot be taken quite literally.
 
However, hot code updating feature certainly contributes to
the latter part of the motto.
 
Concerning "run forever": I tried to find evidence, but did not find
any. Anyway, I have heard a rumour of some Erlang service being
available for 10 years straight.
 
br,
KK
Muttley@dastardlyhq.com: Mar 14 09:56AM

On Tue, 14 Mar 2023 09:51:08 -0000 (UTC)
 
>Concerning "run forever": I tried to find evidence, but did not find
>any. Anyway, I have heard a rumour of some Erlang service being
>available for 10 years straight.
 
Possibly, but I'm just naturally suspicious of any provider who boasts of
24/7/365 uptime because in the real world that simply isn't possible. Whether
its erlang, Tandom NonStop or cloud, eventually things go wrong and the
services goes offline even if only for a short time.
"Alf P. Steinbach" <alf.p.steinbach@gmail.com>: Mar 14 11:04AM +0100

On 2023-03-14 7:49 AM, gst wrote:
>> memory optimization and cannot be used for modification of data visible
>> via other references.
 
> they don't acts like immutable, they are. You cannot modify a string/immutabe objects. they do not have any method with side effect.
 
Oh they do. In particular CPython concatenation modifies the string's
buffer when it is exclusively owned, reference count 1. That reduces n
concatenations from O(n^2) to O(n), which every Python programmer should
know about, not to mention programmers in general.
 
 
> you can assign a string to something, then that thing can be passed down the line/anywhere, the same string can be assigned to many different things/names by many assignements (many references to them yes). But you cannot, never, modify a string in-place, with any reference to it. It has no such method (even private)/feature/possibility.
 
See above.
 
But if you modify your assertions just a little, and talk about
/observable buffer changes/, i.e. not even time consumption, then they
could be OK.
 
 
> of CPython say, the implementation, not the language. but by then
> it's not anymore Python you're doing and that could/would break at
> some point your/any Python code obviously.
That's right, but that's not necessary; ordinary concatenation will do.
 
 
- Alf
gst <g.starck@gmail.com>: Mar 14 03:26AM -0700

Le mardi 14 mars 2023 à 06:05:04 UTC-4, Alf P. Steinbach a écrit :
> buffer when it is exclusively owned, reference count 1. That reduces n
> concatenations from O(n^2) to O(n), which every Python programmer should
> know about, not to mention programmers in general.
 
heuh yes, ok, but this an implementation detail of CPython which does not allow/enable you to modify an already assigned string.
 
 
> But if you modify your assertions just a little, and talk about
> /observable buffer changes/, i.e. not even time consumption, then they
> could be OK.
 
what could be ok ? I don't modify any of the above assertions, there will be never buffer changes to any Python assigned string :
 
```
orig_ref = "foobar"
any_function(orig_ref)
assert orig_ref == "foobar"
# always
```
 
kalevi@kolttonen.fi (Kalevi Kolttonen): Mar 14 10:30AM

> 24/7/365 uptime because in the real world that simply isn't possible. Whether
> its erlang, Tandom NonStop or cloud, eventually things go wrong and the
> services goes offline even if only for a short time.
 
VMS operating system has had stable clustering available for decades now,
and I suppose the initial releases were long before Unix had any kind
of clustering. Check this out:
 
https://en.wikipedia.org/wiki/VMScluster
 
"Cluster uptimes are frequently measured in years with the current
longest uptime being at least sixteen years"
 
Whether the services running on top of VMS were also continuously up is
not clear based on that article.
 
br,
KK
David Brown <david.brown@hesbynett.no>: Mar 14 01:30PM +0100

> the dev forgets to code in the correct stop checks and one day it recurses
> forever. Sure that can happen with a standard for loop, but its more likely to
> be spotted.
 
It is rarely difficult to be sure your recursion ends - you just have to
make sure you have a loop variant that gets smaller with each round, and
ends at 0. Very often you are looping over a data structure (such as a
list), and this happens naturally.
 
Of course, you might /want/ infinite recursion. Many (not all)
functional programming languages support lazy evaluation, and so
infinite lists are commonly used.
 
>> using message passing. I remember reading about some kind of Actor Model
>> and maybe it's also supposed make code correctness easier to achieve. But
 
> Message passing is theoretically pure but horribly inefficient.
 
That depends heavily on the implementation, and the efficiency you are
considering. Message passing can give you lock-free algorithms, or
close to lock-free, and can significantly simplify a lot of the rest of
the code in comparison to shared memory systems. It can scale better,
and work smoothly across different levels of parallelism (light-weight
fibres, threads, processes, multiple cores, multiple computers). But it
can be costly if you need to share large blocks of data back and forth
between multiple actors.
 
> style since god was a boy, but I think traditional programming is much closer
> to how people think. Eg "Add 1 to this, go around again. Put this value in a
> box and put the box in that other box" etc.
 
It is far more natural to think "Do this for each box", or "Do this for
the first box. Then do the same for all the other boxes" than to think
"First count the number of boxes. Then make a new counter, starting at
1. Repeatedly look at the counter, find the box that matches that
number, and do this it. Then add one to the counter, and check if now
matches the count of the boxes - if so, stop."
 
> Functional (and declarative)
> programming requires you to learn to think in a different way.
 
Yes.
 
 
Functional programming is often quite natural for people with a stronger
background in mathematics. It has more of a focus on describing the
results you want, rather than the details of how you get there (for
/efficient/ implementations in functional programming, you often want to
describe intermediary results along the way rather than hoping the
compiler is a genius!).
 
I think that the more experienced you are with one style, the more it
will feel "natural" for you and the harder it is to get used to an
alternative. That applies to all sorts of things, not just programming.
 
But there's little doubt that some kinds of algorithms or programming
problems are easier and clearer in one style of programming language,
while others are easier in a different style.
David Brown <david.brown@hesbynett.no>: Mar 14 01:41PM +0100

>> assignment, and it is much easier to reason about the correctness of the
>> code if the values of variables never changes.
 
> One argument in favour of using #define over const vars.
 
No. You can't change a variable declared "const" after initialisation.
(There are undefined behaviours that can appear to change the value,
such as via pointer casts, but then it's still easy to reason about the
code correctness - it is incorrect.)
 
 
>> feature, IMHO, is the extent to which you can replace code while
>> everything is still running. The idea is that you don't have to stop
 
> Which is probably about as accurate as javas write once run everywhere motto.
 
I can't answer for Java, but Erlang's code replacement works if you
write your code in the appropriate manner - as people do, for serious
Erlang applications.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 08:57AM -0700


> Personally I understood the concepts of OO within probably 10 mins
> of encountering it, functional programming didn't take much longer
> to grok [...]
 
That's incredible!
 
> I must have programmed in at least 10 languages in my life but the
> one I could never get my head around was Prolog and I still can't.
> [...]
 
On the other hand this statement is very credible.
Muttley@dastardlyhq.com: Mar 14 04:16PM

On Tue, 14 Mar 2023 13:30:15 +0100
>make sure you have a loop variant that gets smaller with each round, and
>ends at 0. Very often you are looping over a data structure (such as a
>list), and this happens naturally.
 
Its not that simple especially when head vs tail recursion matters.
 
>Of course, you might /want/ infinite recursion. Many (not all)
>functional programming languages support lazy evaluation, and so
>infinite lists are commonly used.
 
If you can infinite recursion then its not recursion, its a disguised loop.
 
>fibres, threads, processes, multiple cores, multiple computers). But it
>can be costly if you need to share large blocks of data back and forth
>between multiple actors.
 
Generally its more complex and slower than a monolithic approach which is why
despite all the endless academic papers and books written about the wonders of
message passing kernels, few actually exist in the wild.
 
>> box and put the box in that other box" etc.
 
>It is far more natural to think "Do this for each box", or "Do this for
>the first box. Then do the same for all the other boxes" than to think
 
Except its not that. Its do this for the first box, then jump in the box and
do it for the 2nd box. Then jump in that box. And when you get to the end you
have to jump out of all the boxes in reverse russian doll style. Whereas
iterating through some kind of list is done in everyday life.
 
 
>But there's little doubt that some kinds of algorithms or programming
>problems are easier and clearer in one style of programming language,
>while others are easier in a different style.
 
No doubt true.
Muttley@dastardlyhq.com: Mar 14 04:20PM

On Tue, 14 Mar 2023 08:57:25 -0700
>> of encountering it, functional programming didn't take much longer
>> to grok [...]
 
>That's incredible!
 
Why? When I first learnt C++ it was analogous to C with structs with
function pointers in them. Inheritance is hardly complicated and neither
is polymorphism. Though if I'd encountered C++17 having never seen C++ before
I probably wouldn't have bothered and would have gone down the Python or Java
route instead.
 
As for functional programming - its recursion + value based polymorphism.
 
>> one I could never get my head around was Prolog and I still can't.
>> [...]
 
>On the other hand this statement is very credible.
 
Go patronise someone else you plank.
David Brown <david.brown@hesbynett.no>: Mar 14 05:49PM +0100

>> ends at 0. Very often you are looping over a data structure (such as a
>> list), and this happens naturally.
 
> Its not that simple especially when head vs tail recursion matters.
 
"Tail recursion" is when the end of a function is a recursive call that
can be replaced by a jump to the start (or near the start) of the
function. It's an optimisation which is extremely important to
efficiently implementing linear recursive code. I'm not sure what you
mean by "head recursion", nor why you think optimisation affects the
likelihood of a developer accidentally writing unbounded recursion.
Maybe you mean something different by the terms?
 
>> functional programming languages support lazy evaluation, and so
>> infinite lists are commonly used.
 
> If you can infinite recursion then its not recursion, its a disguised loop.
 
In terms of computational theory, all loops are disguised recursion -
loops can /always/ be re-written in terms of recursion. And /simple/
recursion can be re-written in terms of loops (but more complicated
cases cannot).
 
A functional programming definition such as :
 
nums = 1 : map (+1) nums
 
is infinite recursion, generating an infinite list. It doesn't matter
if it could be replaced by a loop or not - it is still infinite recursion.
 
Or you could write :
 
data T = Branch T T
ts = Branch ts ts
 
That defines an infinite recursive binary tree structure - not a useful
one, but if that is a disguised loop, it is well disguised!
 
 
 
> Generally its more complex and slower than a monolithic approach which is why
> despite all the endless academic papers and books written about the wonders of
> message passing kernels, few actually exist in the wild.
 
What, exactly, is a "monolithic approach" to synchronisation of actors,
or to sharing of data?
 
Message passing systems of many kinds are found all over the place - it
is not a concept for kernels. Some languages (like Erlang, Go, occam)
have it as fundamental aspects of the language. There are libraries and
frameworks using it. There is hardware that is based around message
passing (such as XMOS devices). There are OS kernels build around it -
it is the foundation for FreeRTOS, the most popular real-time OS.
Pretty much everything that is considered "distributed" works by message
passing.
 
I suspect you are thinking of a few specific use-cases where shared
memory with locks is more efficient than message passing might be (and
such cases certainly exist), and generalising from there.
 
 
> do it for the 2nd box. Then jump in that box. And when you get to the end you
> have to jump out of all the boxes in reverse russian doll style. Whereas
> iterating through some kind of list is done in everyday life.
 
You are seriously abusing the analogies here. Perhaps you should have
spent more than 10 minutes looking at functional programming languages,
because you seem a bit confused.
 
>> problems are easier and clearer in one style of programming language,
>> while others are easier in a different style.
 
> No doubt true.
 
There's always /something/ we can agree on :-)
Muttley@dastardlyhq.com: Mar 14 05:14PM

On Tue, 14 Mar 2023 17:49:20 +0100
>function. It's an optimisation which is extremely important to
>efficiently implementing linear recursive code. I'm not sure what you
>mean by "head recursion", nor why you think optimisation affects the
 
Head recursion:
 
int func(int val)
{
ret = func(val);
.. do something ..
return ret;
}
 
 
Tail recursion:
int func(int val)
{
... do something ...
return func(val);
}
 
 
I'm sure there are plenty of examples in google.
 
>loops can /always/ be re-written in terms of recursion. And /simple/
>recursion can be re-written in terms of loops (but more complicated
>cases cannot).
 
Sounds unlikely. All recursion AFAIK can be emulated using a loop and one or
more stacks.
 
>A functional programming definition such as :
 
> nums = 1 : map (+1) nums
 
No idea what thats doing.
 
> ts = Branch ts ts
 
>That defines an infinite recursive binary tree structure - not a useful
>one, but if that is a disguised loop, it is well disguised!
 
I imagine the interpreter is looping internally so unless it uses magic how
does it do it?
 
>> message passing kernels, few actually exist in the wild.
 
>What, exactly, is a "monolithic approach" to synchronisation of actors,
>or to sharing of data?
 
Shared memory and locking obv.
 
>Message passing systems of many kinds are found all over the place - it
>is not a concept for kernels. Some languages (like Erlang, Go, occam)
 
Occam was a joke though sadly it lived long enough to give Guido bad ideas
about whitespace.
 
>passing (such as XMOS devices). There are OS kernels build around it -
>it is the foundation for FreeRTOS, the most popular real-time OS.
 
If you say so. Every embedded OS I've ever worked on has been linux, usually
BusyBox.
 
>Pretty much everything that is considered "distributed" works by message
>passing.
 
Err yes, but they're generally not in a single application binary are they
which is what we're discussing.
 
 
>You are seriously abusing the analogies here. Perhaps you should have
>spent more than 10 minutes looking at functional programming languages,
>because you seem a bit confused.
 
I'm not confused at all. I know how recursion works thanks. Perhaps you don't?
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 09:47AM -0700


> Malcolm McLean <malcolm.arthur.mclean@gmail.com> writes:
 
[what does it mean for a function to "meet its specification"?]
 
 
> So lets' call them all "procedures" for the moment and get back to
> the question at hand. Should a specification be taken to be
> essentially minimal? I would say so.
 
I don't exactly agree with this view. Certainly there are some
kinds of behaviors (in the general sense of the word) that are
implied and meant to be understood as part of what is required
for how a function behaves (again in the general sense of the
word), but not so much that the specification should be taken to
be "essentially minimal". Consider for example the C library
function qsort(), as specified in the ISO C standard. I'm sure
most people expect that qsort() mustn't do "anything more" than
sort the array, e.g., it must not change the state of an input
file, or change an unrelated byte of memory, etc. But there are
no requirements on "invisible" behaviors such as how fast the
code runs or how much memory is taken. We might be disappointed
if qsort() uses an O( N**3 ) algorithm, but that disappointment
is not because the specification is not met. Also compare with
the C++ standard. When there are runtime requirements of O(1)
or O(N) or O(N log N), etc, the C++ standard states these bounds
explicitly; they are not implied or tacitly assumed.
 
By the way, qsort() is an interesting example because of expected
memory usage. I think most people would not be surprised to learn
that qsort() uses (in some implementations) O(log N) stack space.
But this amount of memory is not necessary. There are, IIANM,
sorting algorithms that are O(N log N) in runtime and O(1) in
memory usage. Does a qsort() implementation that uses a standard
quicksort algorithm violate the "essentially minimal" threshold?
I think most people would agree that it meets the specification
given in the C standard.
 
I guess my stance on this is that the burden falls on whoever is
responsible for writing the specification. Any needed conditions
must be stated explicitly, somewhere, at least informally. We
should never have to rely on the reader having the same set of
builtin assumptions that the author has.
Tim Rentsch <tr.17687@z991.linuxsc.com>: Mar 14 08:48AM -0700

> has undefined behavior is what makes storing multiple string
> literals in overlapping memory workable. We're really saying the
> same thing, from two different points of view.
 
Your earlier statement has causality going in one direction, and
mine has it going in the opposite direction. I'm hard pressed to
see how those two views can be thought of as the same thing.
Mr Flibble <flibble2@reddwarf.jmc.corp>: Mar 14 08:01AM

On 13/03/2023 20:27, Bonita Montero wrote:
 
>> The code is not wrong; ...
 
> It is because your type is not conditionally used as with my example.
> Your both compilers are just more tolerant than MSVC.
Nope. g++ and clang correctly discard the statement (as it is a
template) so the code compiles. By not discarding the statement MSVC is
not compliant with standard C++ so the defect report is valid.
 
/Flibble
Mr Flibble <flibble2@reddwarf.jmc.corp>: Mar 14 08:02AM

On 13/03/2023 20:27, Bonita Montero wrote:
>> that doesn't alter the fact that the code in my defect report is well
>> formed and VS2022 is wrong to reject it. ...
 
> MSVC is corrent because your denendent type doesn't exist.
 
Nope. g++ and clang correctly discard the statement (as it is a
template) so the code compiles. By not discarding the statement MSVC is
not compliant with standard C++ so the defect report is valid.
 
/Flibble
Bonita Montero <Bonita.Montero@gmail.com>: Mar 14 03:35PM +0100

Am 14.03.2023 um 09:01 schrieb Mr Flibble:
 
>  Nope. g++ and clang correctly discard the statement (as it is a
> template) ...
 
The error is thrown when the template is instntiated.
Bonita Montero <Bonita.Montero@gmail.com>: Mar 14 03:35PM +0100

Am 14.03.2023 um 09:02 schrieb Mr Flibble:
 
> Nope. g++ and clang correctly discard the statement
> (as it is a template) so the code compiles. ...
 
The error is thrown when the template is instntiated.
You received this digest because you're subscribed to updates for this group. You can change your settings on the group membership page.
To unsubscribe from this group and stop receiving emails from it send an email to comp.lang.c+++unsubscribe@googlegroups.com.

No comments: