f



Functions and functional programming.

Consider this.

double foo( double (*callback_a)(), double (*callback_b)())
{
   double a, b;

   a = (*callback_a)();
   b = (*callback_b)();

  return a + b;
}

double bar( double (*callback_a)(), double (*callback_b)())
{
   double a, b;

   b = callback_b();
   a = callback_a();

   return a + b;
}

There's no way in C to guarantee that foo shall return the same value as bar, which is awful.

How's the best way to enforce this property?
0
Malcolm
6/2/2014 1:24:12 PM
comp.lang.c 30656 articles. 5 followers. spinoza1111 (3246) is leader. Post Follow

57 Replies
887 Views

Similar Articles

[PageSpeed] 52

Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>There's no way in C to guarantee that foo shall return the
>same value as bar, which is awful.

  In which application of C is this a problem?

0
ram
6/2/2014 1:32:17 PM
On Monday, June 2, 2014 2:32:17 PM UTC+1, Stefan Ram wrote:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
> 
> >There's no way in C to guarantee that foo shall return the
> 
> >same value as bar, which is awful.
> 
>   In which application of C is this a problem?
>
When you're trying to support a functional programming paradigm in C, and so the callbacks
no longer remain as fairly trivial bits of missing functionality (like the comparator to qsort()),
but are passed to each other possibly multiple times, and the structure exists at another
level than the call-tree.
0
Malcolm
6/2/2014 1:50:39 PM
On 2014-06-02, Malcolm McLean <malcolm.mclean5@btinternet.com> wrote:
> Consider this.
>
> double foo( double (*callback_a)(), double (*callback_b)())
> {
>    double a, b;
>
>    a = (*callback_a)();
>    b = (*callback_b)();
>
>   return a + b;
> }
>
> double bar( double (*callback_a)(), double (*callback_b)())
> {
>    double a, b;
>
>    b = callback_b();
>    a = callback_a();
>
>    return a + b;
> }

This isn't really functional programming. Functions in functional programming
are capsules of code plus lexical environment, not simply pointers to code.

> There's no way in C to guarantee that foo shall return the same value as bar,
> which is awful.

Sure there is; don't have any side effects in the functions: an important
attribute of functional programming.

In functional languages, which banish most side effects, the evaluation order
is even more loose than in C: not only can the constitutents of an expression
be evaluated in multiple orders, the arguments of a function call do not have
to be evaluated prior to that call taking place. An expression like f(g(x), y)
can call f before g is called; f can call g.  (And since g carries its lexical
environment, x is properly resolved).
0
Kaz
6/2/2014 1:51:27 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>On Monday, June 2, 2014 2:32:17 PM UTC+1, Stefan Ram wrote:
>>Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>>In which application of C is this a problem?
>When you're trying to support a functional programming
>paradigm in C

  C indeed supports functional programming, because many
  implementations of functional languages are written in C!

  However, C is not a functional language itself. When 
  someone expects C to be one, then I do not see this
  to be a problem with C.


0
ram
6/2/2014 2:06:47 PM
On Monday, June 2, 2014 3:06:47 PM UTC+1, Stefan Ram wrote:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
> 
>   C indeed supports functional programming, because many
>   implementations of functional languages are written in C!
> 
You can implement any language in any other.
You can also use most paradigms (state machines, structured programming, 
object-orientation, functional programming etc) in most languages, without
actually building what s effectively an interpreter for another language.
That's what I mean by trying to support a functional programming paradigm
in C. 
> 
0
Malcolm
6/2/2014 2:27:17 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:

> Consider this.
>
> double foo( double (*callback_a)(), double (*callback_b)())
> {
>    double a, b;
>
>    a = (*callback_a)();
>    b = (*callback_b)();
>
>   return a + b;
> }
>
> double bar( double (*callback_a)(), double (*callback_b)())
> {
>    double a, b;
>
>    b = callback_b();
>    a = callback_a();

(Note that this is the better syntax for an indirect call -- the other
one comes from a version of C of purely archaeological interest).

>    return a + b;
> }
>
> There's no way in C to guarantee that foo shall return the same value
> as bar, which is awful.

The same is true if the function calls are not indirect.  I see no
connection with callbacks.  Why you call it awful, I don't know.  It's
how C does things with any function call.

> How's the best way to enforce this property?

Ironically, the best solution is to do function programming (this isn't
FP).  In a purely function paradigm, the order of evaluation does not
effect the value of the result (though it can be very significant in
other ways).

But you mean in C.  I don't see how you can.  Code order matters in C.

-- 
Ben.
0
Ben
6/2/2014 2:29:29 PM
On Mon, 02 Jun 2014 07:27:17 -0700, Malcolm McLean saw fit to publish the
following:

> On Monday, June 2, 2014 3:06:47 PM UTC+1, Stefan Ram wrote:
>> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>> 
>>   C indeed supports functional programming, because many
>>   implementations of functional languages are written in C!
>> 
> You can implement any language in any other.

Probably true, theoretically. In practical terms it's probably not useful 
to implement C in BrainF*ck. The other way around, though, is not that 
difficult and may be a good exercise.

> You can also use most paradigms (state machines, structured programming,
> object-orientation, functional programming etc) in most languages,
> without actually building what s effectively an interpreter for another
> language.

Hmmm.... I'm not sure if that's true even theoretically.

> That's what I mean by trying to support a functional
> programming paradigm in C.

If that's your intent, it should show up more clearly in the code 
fragment you showed earlier. Depending on the functions provided, foo() 
and bar() could legitimately return anything.

If you want constraints on that, using C, your should implement those 
constraints explicitly, or better, incorporate them in the basic design 
of your product.
0
Kleuske
6/2/2014 2:56:07 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:

> On Monday, June 2, 2014 3:06:47 PM UTC+1, Stefan Ram wrote:
>> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>> 
>>   C indeed supports functional programming, because many
>>   implementations of functional languages are written in C!
>> 
> You can implement any language in any other.
> You can also use most paradigms (state machines, structured programming, 
> object-orientation, functional programming etc) in most languages, without
> actually building what s effectively an interpreter for another language.
> That's what I mean by trying to support a functional programming paradigm
> in C. 

It's almost impossible without a full-blown interpreter.  C lacks the
basic mechanisms to do the job.  Take my posted fib list to see the
scale of the problem.

-- 
Ben.
0
Ben
6/2/2014 3:15:21 PM

"Stefan Ram" <ram@zedat.fu-berlin.de> wrote in message 
news:functional-20140602160337@ram.dialup.fu-berlin.de...
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>>On Monday, June 2, 2014 2:32:17 PM UTC+1, Stefan Ram wrote:
>>>Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>>>In which application of C is this a problem?
>>When you're trying to support a functional programming
>>paradigm in C
>
>  C indeed supports functional programming, because many
>  implementations of functional languages are written in C!

By the same argument it also also supports keyword parameters, and 
practically any feature of any language that happens to be, or could be, 
implemented in C.

And so does assembler...

-- 
Bartc 

0
BartC
6/2/2014 3:44:10 PM
On Monday, June 2, 2014 3:29:29 PM UTC+1, Ben Bacarisse wrote:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
> 
> > double foo( double (*callback_a)(), double (*callback_b)())
> > {
> 
> >    double a, b;
> 
> >    a = (*callback_a)();
> >    b = (*callback_b)();
> 
> >   return a + b;
> > }
> 
> >
> 
> > double bar( double (*callback_a)(), double (*callback_b)())
> > {
> >    double a, b;
> 
> >    b = callback_b();
> >    a = callback_a();
> 
> (Note that this is the better syntax for an indirect call -- the other
> one comes from a version of C of purely archaeological interest).
> 
> 
> 
> >    return a + b;
> 
> > }
> 
> > There's no way in C to guarantee that foo shall return the same value
> > as bar, which is awful.
> 
> The same is true if the function calls are not indirect.  I see no
> connection with callbacks.  Why you call it awful, I don't know.  It's
> how C does things with any function call.
> 
If we are calling a and b directly, we know their properties. We know whether
the order of calls matters or not. If we don't know the properties of a and b, it 
becomes difficult to ensure that foo() is correct, it's impossible to know if we will 
break it by replacing it with bar().
So we can no longer "refactor" code.
0
Malcolm
6/2/2014 4:09:17 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:

> On Monday, June 2, 2014 3:29:29 PM UTC+1, Ben Bacarisse wrote:
>> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>> 
>> > double foo( double (*callback_a)(), double (*callback_b)())
>> > {
>> >    double a, b;
>> >    a = (*callback_a)();
>> >    b = (*callback_b)();
>> >   return a + b;
>> > }
>> >
>> > double bar( double (*callback_a)(), double (*callback_b)())
>> > {
>> >    double a, b;
>> >    b = callback_b();
>> >    a = callback_a();
<snip>
>> >    return a + b;
>> > }
>> 
>> > There's no way in C to guarantee that foo shall return the same value
>> > as bar, which is awful.
>> 
>> The same is true if the function calls are not indirect.  I see no
>> connection with callbacks.  Why you call it awful, I don't know.  It's
>> how C does things with any function call.
>> 
> If we are calling a and b directly, we know their properties. We know
> whether the order of calls matters or not.  If we don't know the
> properties of a and b, it becomes difficult to ensure that foo() is
> correct, it's impossible to know if we will break it by replacing it
> with bar().  So we can no longer "refactor" code.

OK, then you are complaining about a normal property of arguments.  You
can't re-write a function without knowing what constraints, if any, may
be assumed in regard to its arguments.  If you know nothing at all about
what may or may not be passed you have to assume the general case.

-- 
Ben.
0
Ben
6/2/2014 4:46:23 PM
On Monday, June 2, 2014 5:46:23 PM UTC+1, Ben Bacarisse wrote:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
> 
> OK, then you are complaining about a normal property of arguments.  You
> can't re-write a function without knowing what constraints, if any, may
> be assumed in regard to its arguments.  If you know nothing at all about
> what may or may not be passed you have to assume the general case.
> 
In C you can't actually check pointer arguments, but that's not true in most
C-like languages, and it's usually pretty easy to say what the constraints
on a pointer are, and usually if passed invalid arguments the function will
crash or obviously fail.
Other arguments are usually checkable within the function.

But callback arguments?

0
Malcolm
6/2/2014 5:22:08 PM
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>> Consider this.
>>
>> double foo( double (*callback_a)(), double (*callback_b)())
>> {
>>    double a, b;
>>
>>    a = (*callback_a)();
>>    b = (*callback_b)();
>>
>>   return a + b;
>> }
>>
>> double bar( double (*callback_a)(), double (*callback_b)())
>> {
>>    double a, b;
>>
>>    b = callback_b();
>>    a = callback_a();
>
> (Note that this is the better syntax for an indirect call -- the other
> one comes from a version of C of purely archaeological interest).

That's debatable.  Both forms are valid in modern C, and one could argue
that the explicit dereference, even though it's largely meaningless to
the compiler, documents the fact that it's an indirect call.

On the other hand, the fact that the compiler doesn't enforce the
distinction makes the convention less useful than it might otherwise be.

Also, the parameter declarations should use "(void)" rather than the
obsolescent "()".

>>    return a + b;
>> }
>>
>> There's no way in C to guarantee that foo shall return the same value
>> as bar, which is awful.
>
> The same is true if the function calls are not indirect.  I see no
> connection with callbacks.  Why you call it awful, I don't know.  It's
> how C does things with any function call.
>
>> How's the best way to enforce this property?
>
> Ironically, the best solution is to do function programming (this isn't
> FP).  In a purely function paradigm, the order of evaluation does not
> effect the value of the result (though it can be very significant in
> other ways).
>
> But you mean in C.  I don't see how you can.  Code order matters in C.

Right, and C provides no way to state that a function, or a particular
call to a function, has no side effects.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Keith
6/2/2014 5:42:17 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>You can implement any language in any other.

  This is wrong, both from a theoretical POV and from
  a practical POV.

  In theory, you erroneously assumed that the concept of
  �language� implied Turing completeness. (I have posted
  references here recently.)

  In practice, people write LISP implementations in C, 
  not C implementations in LISP. Yes, you could do the
  latter, but it's not done. And that's why I wrote:

|C indeed supports functional programming, because many
|implementations of functional languages are written in C!

0
ram
6/2/2014 10:06:24 PM
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>It's almost impossible without a full-blown interpreter.

  One problem with functional programming in C is the lack of
  automatic dynamic memory management. In a functional
  language I can write,

g( print( f( cons( 1, 2 ))))

  where �cons� creates a dotted pair

..---.---.
| 1 | 2 |
'---'---'

  . In C, however, I have to start thinking:
  �cons� uses malloc ... What if malloc, and thereby �cons�,
  returns 0, while �f� expects a dotted pair? And when and how
  exactly is the memory freed later? Above, it might be suited
  when some of the functions frees the pair, but what if such
  a function is being called in another context, where freeing
  its argument would not be appropriate, such as when it is
  being called with a dotted pair in automatic memory instead
  of dynamic memory at another place?

  Using C with a GC (there are GCs for C) might help in this
  regard. But then it's not C C anymore, but GC C!

0
ram
6/2/2014 10:25:58 PM
On 2014-06-02, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>>It's almost impossible without a full-blown interpreter.
>
>   One problem with functional programming in C is the lack of
>   automatic dynamic memory management. In a functional
>   language I can write,
>
> g( print( f( cons( 1, 2 ))))
>
>   where »cons« creates a dotted pair
>
> .---.---.
>| 1 | 2 |
> '---'---'
>
>   . In C, however, I have to start thinking:
>   »cons« uses malloc ... What if malloc, and thereby »cons«,
>   returns 0, while »f« expects a dotted pair? And when and how
>   exactly is the memory freed later? Above, it might be suited
>   when some of the functions frees the pair, but what if such
>   a function is being called in another context, where freeing
>   its argument would not be appropriate, such as when it is
>   being called with a dotted pair in automatic memory instead
>   of dynamic memory at another place?
>
>   Using C with a GC (there are GCs for C) might help in this
>   regard. But then it's not C C anymore, but GC C!

I suspect that interpreted languages written C which are written in strictly
conforming ISO C (or strictly conforming ISO C plus only library extensions)
are few.

Language projects use C as a high level assembly language.

Their test cases are written in the language that is being processed, and if
those pass on all supported platforms, nobody cares too much about undefined
behavior in the underlying C. They have a working binary.

As far as GC goes, C is fairly unfriendly toward garbage collectors,
in sneaky ways---not just the obvious ways.

Using your functional library for C (FCC?) suppose you do this:

  var = NIL; /* suitably defined NIL constant */

your hope here is that the last reference to an object is held
in var, and is being obliterated, so the object becomes garbage.

Alas, the optimizing C compiler performs flow analysis and realizes, "hey,
var has no next use at any node reachable from the assignment in
the flow graph, in the flow graph; it is a dead assignment!".

Poof, the assignment optimized away, no nowhere to be seen in the machine code.

And so, you have a spurious retention bug: some memory or register still holds
on to the object, preventing GC.

The next line of code could be big_long_computation(); and so the object
is held all across that.

Perhaps volatile will solve the problem (but will you stick volatile
everywhere? And there go useful optimizations!)

Or, you can attack it on a case by case basis:

  variable = NIL; /* suitably defined NIL constant */
  dummy_external_function(&variable); /* does nothing */

Properly, precisely implemented languages that have garbage collection have
compilers which are aware of garbage collection.
0
Kaz
6/2/2014 10:58:03 PM
Malcolm McLean <malcolm.mclean5@btinternet.com> writes:

> On Monday, June 2, 2014 5:46:23 PM UTC+1, Ben Bacarisse wrote:
>> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
>> 
>> OK, then you are complaining about a normal property of arguments.  You
>> can't re-write a function without knowing what constraints, if any, may
>> be assumed in regard to its arguments.  If you know nothing at all about
>> what may or may not be passed you have to assume the general case.
>> 
> In C you can't actually check pointer arguments, but that's not true in most
> C-like languages, and it's usually pretty easy to say what the constraints
> on a pointer are, and usually if passed invalid arguments the function will
> crash or obviously fail.
> Other arguments are usually checkable within the function.
>
> But callback arguments?

No, in C all you know is the type (and that can changed with a cast).
But this generality is also why the are so very useful (though less so
in C due it's very understandable restrictions).  With great power
comes... careful coding.

In portable code, int parameters have to be thought of as representing
any value in a range of possible types (but always whole numbers).  You
need to take care with the code, but it's not that hard.  If C had a
"numeric" type it would would allow more abstract code to be written
(summing ints, or floats or complex numbers, for example) but at the
expense of having to be much more careful about the code, because you
know less about the set of possible values.  In C, function pointers are
just about the most extreme example of this.

Functional languages usually have very powerful type systems that permit
much greater control over what functions can be passed and returned, but
you can never get away from the fact that functions are more flexible
than "smaller" data types.  (Theoretically, when you introduce functions
over some other type, you've introduced a power set with larger
cardinality.)

-- 
Ben.
0
Ben
6/2/2014 10:59:14 PM
Keith Thompson <kst-u@mib.org> writes:

> Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
>> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
<snip>
>>>    a = (*callback_a)();
>>>    b = (*callback_b)();
<snip>
>>>    b = callback_b();
>>>    a = callback_a();
>>
>> (Note that this is the better syntax for an indirect call -- the other
>> one comes from a version of C of purely archaeological interest).
>
> That's debatable.  Both forms are valid in modern C, and one could argue
> that the explicit dereference, even though it's largely meaningless to
> the compiler, documents the fact that it's an indirect call.

That's true, but I find in practice that it's usually obvious from the
name.  Non-indirect functions will usually have rather specific names,
but function pointers often have rather generic names (e.g.
"add_numbers" vs. "binary_op").  This may be due to the kind of use I
have typically made of them, but it feels like it might be a bit more
general than that.

> On the other hand, the fact that the compiler doesn't enforce the
> distinction makes the convention less useful than it might otherwise
> be.

The thing I don't like about is rather obsessive.  The language (now)
defines a call via a pointer -- a function designator decays to a
pointer, so (*callback)() de-references a pointer only to have it be
converted back.  (I know this does not actually happen, and I know that
it doesn't matter -- I did say it was rather obsessive.)

<snip>
>> But you mean in C.  I don't see how you can.  Code order matters in C.
>
> Right, and C provides no way to state that a function, or a particular
> call to a function, has no side effects.

A new use for restrict-qualified pointers, maybe?

-- 
Ben.
0
Ben
6/2/2014 11:12:18 PM
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Keith Thompson <kst-u@mib.org> writes:
[...]
>> Right, and C provides no way to state that a function, or a particular
>> call to a function, has no side effects.
>
> A new use for restrict-qualified pointers, maybe?

Obviously what we need is a new meaning for "static".  8-)}

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
0
Keith
6/3/2014 12:17:23 AM
Kaz Kylheku <kaz@kylheku.com> writes:

> On 2014-06-02, Stefan Ram <ram@zedat.fu-berlin.de> wrote:
>>   One problem with functional programming in C is the lack of
>>   automatic dynamic memory management.
<snip>

(GC = garbage collecton)

> As far as GC goes, C is fairly unfriendly toward garbage collectors,
> in sneaky ways---not just the obvious ways.
>
> Using your functional library for C (FCC?) suppose you do this:
>
>   var = NIL; /* suitably defined NIL constant */
>
> your hope here is that the last reference to an object is held
> in var, and is being obliterated, so the object becomes garbage.
>
> Alas, the optimizing C compiler performs flow analysis and realizes, "hey,
> var has no next use at any node reachable from the assignment in
> the flow graph, in the flow graph; it is a dead assignment!".

Some garbage collectors (many? most? all? I don't know) provide a way to
manually "free" a pointer's memory.

<snip>
-- 
Ben.
0
Ben
6/3/2014 12:19:19 AM
On 2014-06-02 15:24, Malcolm McLean wrote:
> Consider this.
>
> double foo( double (*callback_a)(), double (*callback_b)())
> {
>     double a, b;
>
>     a = (*callback_a)();
>     b = (*callback_b)();
>
>    return a + b;
> }
>
> double bar( double (*callback_a)(), double (*callback_b)())
> {
>     double a, b;
>
>     b = callback_b();
>     a = callback_a();
>
>     return a + b;
> }
>
> There's no way in C to guarantee that foo shall return the same value as bar, which is awful.
>
> How's the best way to enforce this property?
>

As you mention it cannot be enforced by the language. The programmer can 
however adhere to the sound principle of "command query separation" 
which states that a subroutine should be either

* a procedure which is invoked for its side effects only and which does 
not return a result or

* a function which computes a result and has no side effects.

As far as I understand this principle is not too popular in the C 
programming community.


-- August
0
August
6/3/2014 6:48:16 PM
On 06/03/2014 02:48 PM, August Karlstrom wrote:
....
> As you mention it cannot be enforced by the language. The programmer can 
> however adhere to the sound principle of "command query separation" 
> which states that a subroutine should be either
> 
> * a procedure which is invoked for its side effects only and which does 
> not return a result or ...

Many subroutines that have side-effects can fail. In the C standard
library, all such subroutines return values that can be tested to
determine whether or not they might have failed. How would a programmer
following the principle that you describe design such routines?


> As far as I understand this principle is not too popular in the C 
> programming community.

I can imagine why.

0
James
6/3/2014 7:09:06 PM
Ben Bacarisse <ben.usenet@bsb.me.uk> writes:
> Malcolm McLean <malcolm.mclean5@btinternet.com> writes:
> 
> > Consider this.
> >
> > double foo( double (*callback_a)(), double (*callback_b)())
> > {
> >    double a, b;
> >
> >    a = (*callback_a)();
> >    b = (*callback_b)();
> >
> >   return a + b;
> > }
> >
> > double bar( double (*callback_a)(), double (*callback_b)())
> > {
> >    double a, b;
> >
> >    b = callback_b();
> >    a = callback_a();
> 
> (Note that this is the better syntax for an indirect call -- the other
> one comes from a version of C of purely archaeological interest).

"Better" is a judgement call, based on personal tastes,
rather than an absolute.  I personally quite like that
anachronism, as it implies more strongly that it's a 
call via a function pointer. (And AFAIK, I've never used
any archaic dialect of C where such a syntax was actually
necessary, so I've not been tainted by being forced to use
it, a la Stroustrup^H^H^H^H^H^H^H^Hockholm Syndrome.)

Phil
(Hi Ian!)
-- 
Religion is too important a matter to its devotees to be a subject of 
ridicule. If they indulge in absurdities, they are to be pitied rather
than ridiculed. -- Immanuel Kant (1724-1804), lecture at Konigsberg, 1775
0
Phil
6/3/2014 9:52:31 PM
On Tue, 2014-06-03, James Kuyper wrote:
> On 06/03/2014 02:48 PM, August Karlstrom wrote:
> ...
>> As you mention it cannot be enforced by the language. The programmer can 
>> however adhere to the sound principle of "command query separation" 
>> which states that a subroutine should be either
>> 
>> * a procedure which is invoked for its side effects only and which does 
>> not return a result or ...
>
> Many subroutines that have side-effects can fail. In the C standard
> library, all such subroutines return values that can be tested to
> determine whether or not they might have failed. How would a programmer
> following the principle that you describe design such routines?

Googling suggests Bertrand Meyer coined the term "command query
separation", so maybe she's supposed to use Eiffel's exception
mechanism ...

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .
0
Jorgen
6/3/2014 10:05:42 PM
On 2014-06-03 21:09, James Kuyper wrote:
> Many subroutines that have side-effects can fail. In the C standard
> library, all such subroutines return values that can be tested to
> determine whether or not they might have failed. How would a programmer
> following the principle that you describe design such routines?

The programmer would add an output parameter to the parameter list which 
indicates success or failure, for instance

     void foo(int x, int *error);

which would be used as

     int error;

     foo(37, &error);
     if (! error) {
         /*do something*/
     } else {
         /*do something else*/
     }


-- August
0
August
6/4/2014 8:59:44 AM
On 2014-06-04 00:05, Jorgen Grahn wrote:
> Googling suggests Bertrand Meyer coined the term "command query
> separation", so maybe she's supposed to use Eiffel's exception
> mechanism ...

Yes, the term was coined by Bertrand in the context of Eiffel and object 
oriented programming but it is equally applicable to procedural programming.

-- August

0
August
6/4/2014 9:10:05 AM
Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:

> Kaz Kylheku <kaz@kylheku.com> writes:
> 
> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
> > in sneaky ways---not just the obvious ways.

> Some garbage collectors (many? most? all? I don't know) provide a way to
> manually "free" a pointer's memory.

Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
should say, especially) only some times, does IMO constitute "fairly
unfriendly". The point about GC is that you should be able to forget
about it. If you can't, IMAO, 90% of the point of GC is lost.

Richard
0
raltbos
6/4/2014 11:06:46 AM
On 06/03/2014 02:48 PM, August Karlstrom wrote:
> As you mention it cannot be enforced by the language. The programmer can 
> however adhere to the sound principle of "command query separation" 
> which states that a subroutine should be either
> 
> * a procedure which is invoked for its side effects only and which does 
> not return a result or
> 
> * a function which computes a result and has no side effects.


On 06/04/2014 04:59 AM, August Karlstrom wrote:
> On 2014-06-03 21:09, James Kuyper wrote:
>> Many subroutines that have side-effects can fail. In the C standard
>> library, all such subroutines return values that can be tested to
>> determine whether or not they might have failed. How would a programmer
>> following the principle that you describe design such routines?
> 
> The programmer would add an output parameter to the parameter list which 
> indicates success or failure, for instance
> 
>      void foo(int x, int *error);
> 
> which would be used as
> 
>      int error;
> 
>      foo(37, &error);
>      if (! error) {
>          /*do something*/
>      } else {
>          /*do something else*/
>      }

And what is the advantage that is achieved by doing this rather than
returning a value?
-- 
James Kuyper
0
James
6/4/2014 11:48:11 AM
On 2014-06-04, Richard Bos <raltbos@xs4all.nl> wrote:
> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>
>> Kaz Kylheku <kaz@kylheku.com> writes:
>> 
>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>> > in sneaky ways---not just the obvious ways.
>
>> Some garbage collectors (many? most? all? I don't know) provide a way to
>> manually "free" a pointer's memory.
>
> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
> should say, especially) only some times, does IMO constitute "fairly
> unfriendly". The point about GC is that you should be able to forget
> about it. If you can't, IMAO, 90% of the point of GC is lost.

Setting

    ptr = NIL;

in order to let the garbage collector free the memory pointed to by ptr
may be considered _manually_ GC-ing your memory as well.
0
Ike
6/4/2014 12:11:09 PM
Richard Bos wrote:

> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
> 
>> Kaz Kylheku <kaz@kylheku.com> writes:
>> 
>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>> > in sneaky ways---not just the obvious ways.
> 
>> Some garbage collectors (many? most? all? I don't know) provide a way to
>> manually "free" a pointer's memory.
> 
> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
> should say, especially) only some times, does IMO constitute "fairly
> unfriendly". The point about GC is that you should be able to forget
> about it. If you can't, IMAO, 90% of the point of GC is lost.
> 
> Richard

And this would add unpredictability to the program. The GC can step at any 
inappropriate time, rendering real time behavior a total mess.
In real world applications there are more requirements than just computing 
the correct results. 
Even if no hard real time constraints have to be met having an application 
"hang" at undefinable moments for unpredictable time is a at least annoying 
for the user.

-- 
Reinhardt Behm

0
Reinhardt
6/4/2014 1:19:39 PM
August Karlstrom <fusionfile@gmail.com> wrote:

> On 2014-06-03 21:09, James Kuyper wrote:
> > Many subroutines that have side-effects can fail. In the C standard
> > library, all such subroutines return values that can be tested to
> > determine whether or not they might have failed. How would a programmer
> > following the principle that you describe design such routines?
> 
> The programmer would add an output parameter to the parameter list which 
> indicates success or failure, for instance
> 
>      void foo(int x, int *error);

A rose by any other name would smell as sweet, but this one stinks of
horse manure.

Richard
0
raltbos
6/4/2014 1:20:51 PM
On Wednesday, June 4, 2014 2:20:51 PM UTC+1, Richard Bos wrote:
> 
> > The programmer would add an output parameter to the parameter list which 
> > indicates success or failure, for instance
> 
> >      void foo(int x, int *error);
> 
> A rose by any other name would smell as sweet, but this one stinks of
> horse manure.
> 
In C you're not capturing anything special in the distinction between

void foo(int x, int *error);

and

int foo(int x)

That's not true in other languages which make it easy to return multiple results and don't
have pointers.

0
Malcolm
6/4/2014 2:46:54 PM
On 2014-06-04 13:48, James Kuyper wrote:
> On 06/03/2014 02:48 PM, August Karlstrom wrote:
>> The programmer would add an output parameter to the parameter list which
>> indicates success or failure, for instance
>>
>>       void foo(int x, int *error);
>>
>> which would be used as
>>
>>       int error;
>>
>>       foo(37, &error);
>>       if (! error) {
>>           /*do something*/
>>       } else {
>>           /*do something else*/
>>       }
>
> And what is the advantage that is achieved by doing this rather than
> returning a value?

One advantage is that the emphasis is put on the program state changing 
function rather than on the assignment of the return value. Another 
advantage is that the error status cannot be completely ignored. A third 
advantage is that the function return value (of which there is none) 
cannot be used in an expression, which as mentioned by the original 
poster can give rise to unexpected behavior.


-- August
0
August
6/4/2014 3:07:51 PM
On 2014-06-04 16:46, Malcolm McLean wrote:
> On Wednesday, June 4, 2014 2:20:51 PM UTC+1, Richard Bos wrote:
>>
>>> The programmer would add an output parameter to the parameter list which
>>> indicates success or failure, for instance
>>
>>>       void foo(int x, int *error);
>>
>> A rose by any other name would smell as sweet, but this one stinks of
>> horse manure.
>>
> In C you're not capturing anything special in the distinction between
>
> void foo(int x, int *error);
>
> and
>
> int foo(int x)
>
> That's not true in other languages which make it easy to return multiple results and don't
> have pointers.
>

Regarding the error status the former function declaration is self 
explaining whereas the latter is not.


-- August
0
August
6/4/2014 3:12:47 PM
On 06/04/2014 11:07 AM, August Karlstrom wrote:
> On 2014-06-04 13:48, James Kuyper wrote:
>> On 06/03/2014 02:48 PM, August Karlstrom wrote:
>>> The programmer would add an output parameter to the parameter list which
>>> indicates success or failure, for instance
>>>
>>>       void foo(int x, int *error);
>>>
>>> which would be used as
>>>
>>>       int error;
>>>
>>>       foo(37, &error);
>>>       if (! error) {
>>>           /*do something*/
>>>       } else {
>>>           /*do something else*/
>>>       }
>>
>> And what is the advantage that is achieved by doing this rather than
>> returning a value?
> 
> One advantage is that the emphasis is put on the program state changing 
> function rather than on the assignment of the return value. ...

That's a fairly subjective issue; and not one I'd consider important
enough to outweigh the substantially greater convenience of using the
return value, rather than a pointer argument.

> ... Another 
> advantage is that the error status cannot be completely ignored. ...

It seems to me to be perfectly feasible to drop the if-else statement
from the above code.

> ... A third 
> advantage is that the function return value (of which there is none) 
> cannot be used in an expression, which as mentioned by the original 
> poster can give rise to unexpected behavior.

I find the ability to use the return value in an expression to be one of
the advantages of that approach. This has been a fairly long thread, and
during a quick review backwards through the thread, I didn't recognize
any of the earlier messages in this thread as having demonstrated any
such problem. Could you identify more precisely the problem you're
talking about?

0
James
6/4/2014 5:11:06 PM
On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
> On 2014-06-04 16:46, Malcolm McLean wrote:
>> On Wednesday, June 4, 2014 2:20:51 PM UTC+1, Richard Bos wrote:
>>>
>>>> The programmer would add an output parameter to the parameter list which
>>>> indicates success or failure, for instance
>>>
>>>>       void foo(int x, int *error);
>>>
>>> A rose by any other name would smell as sweet, but this one stinks of
>>> horse manure.
>>>
>> In C you're not capturing anything special in the distinction between
>>
>> void foo(int x, int *error);
>>
>> and
>>
>> int foo(int x)
>>
>> That's not true in other languages which make it easy to return multiple results and don't
>> have pointers.
>>
>
> Regarding the error status the former function declaration is self 
> explaining whereas the latter is not.

Even if the declaration is clearer, declarations tend to be outnumbered
by uses. If the calls are clear, we don't have to look at declarations.
Uses not only have to be clear, but convenient.

Is the int *error version clearer from its declaration? Hardly.

- Is it okay for error to be null if I'm not interested in the error,
  or must it always point to a valid location?

- Does foo always store a result in *error, or only if there is an error?
  I.e. must I initialize foo to some value (say zero) and then test
  for nonzero? Or can I leave it uninitialized and test it afterward?

nobody in their right mind would prefer this interface when the
function returns nothing.

Then there is the minor point that when you're developing foo, perhaps the
compiler has a useful diagnostic like "not all paths return a value"
or "return with no value in a fucntion returning int".

No compiler I know of will tell you "not all paths store a value into *error".
0
Kaz
6/4/2014 6:05:17 PM
On 2014-06-04, Richard Bos <raltbos@xs4all.nl> wrote:
> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>
>> Kaz Kylheku <kaz@kylheku.com> writes:
>> 
>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>> > in sneaky ways---not just the obvious ways.
>
>> Some garbage collectors (many? most? all? I don't know) provide a way to
>> manually "free" a pointer's memory.
>
> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
> should say, especially) only some times, does IMO constitute "fairly
> unfriendly".

That workaround was inapplicable in the situation where I ran into
the error, ebcause I had no assurance that the variable in question
was the last reference to the object. Just that there were cases when
it in fact was, and the compiler optimized away the "var = nil"
dead assignment, leading to spurious retention.

The fix was not "gc_free(var)", but "gc_hint(&var)": a hint to the
compiler that the assignment is not dead, but visible to some
outside agency (the garbage collector).

> The point about GC is that you should be able to forget
> about it. If you can't, IMAO, 90% of the point of GC is lost.

Once you take the object lifetime computation into your own hands, you have the
responsibility to get it right.  There are cases where it is obvious, so it
could be a useful optimization, but you don't want ot be sprinking unsafe
things throughout the code to compensate for what the C compiler is or isn't
doing.
0
Kaz
6/4/2014 6:08:54 PM
On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
> On 2014-06-04, Richard Bos <raltbos@xs4all.nl> wrote:
>> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>>
>>> Kaz Kylheku <kaz@kylheku.com> writes:
>>> 
>>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>>> > in sneaky ways---not just the obvious ways.
>>
>>> Some garbage collectors (many? most? all? I don't know) provide a way to
>>> manually "free" a pointer's memory.
>>
>> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
>> should say, especially) only some times, does IMO constitute "fairly
>> unfriendly". The point about GC is that you should be able to forget
>> about it. If you can't, IMAO, 90% of the point of GC is lost.
>
> Setting
>
>     ptr = NIL;
>
> in order to let the garbage collector free the memory pointed to by ptr
> may be considered _manually_ GC-ing your memory as well.

No, it may not, because if there are other copies of the object,
it is not reclaimed. The ptr = nil assignment is innocent; it doesn't
take on the full responsibility of computing the lifetime of the object;
it only contributes to that information.

Also, the C compiler may also cause spurious retention in cases like this:

  {
    var ptr = big_object();
  }

  computation();

here instead of ptr = nil, we ended the scope of ptr, so it doesn't exist.
(Is that also considered manual GC?)

But the C compiler may well leave a memory location on the stack which
still contains a pointer to big_object across computation.

In a garbage collected (by design, from the ground up) language, the garbage
collector has information from the compiler about where the live objects are;
it doesn't have to just scan every location in the stack conservatively.  Or
else, if the collector does just scan the stack, the compiler generates code
which tidies up dead references.
0
Kaz
6/4/2014 6:14:43 PM
On 2014-06-04, Reinhardt Behm <rbehm@hushmail.com> wrote:
> Richard Bos wrote:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>> 
>>> Kaz Kylheku <kaz@kylheku.com> writes:
>>> 
>>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>>> > in sneaky ways---not just the obvious ways.
>> 
>>> Some garbage collectors (many? most? all? I don't know) provide a way to
>>> manually "free" a pointer's memory.
>> 
>> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
>> should say, especially) only some times, does IMO constitute "fairly
>> unfriendly". The point about GC is that you should be able to forget
>> about it. If you can't, IMAO, 90% of the point of GC is lost.
>> 
>> Richard
>
> And this would add unpredictability to the program. The GC can step at any 
> inappropriate time, rendering real time behavior a total mess.

This isn't much different from an interrupt. An interrupt can happen at any
time, adding hundreds of cycles to some operation that should take a
predictable number of microseconds. Solution: disable interrupts.
Or prioritize everything with real time threads.

The same thing can be applied to GC. GC can be momentarily disabled,
and prioritized.

There are techniques for real time GC also; don't implement naive full mark and
sweep, if there is a real time requirement.

> In real world applications there are more requirements than just computing 
> the correct results.

There are scads of applications where this doesn't matter. Garbage collection
is everywhere.

> Even if no hard real time constraints have to be met having an application 
> "hang" at undefinable moments for unpredictable time is a at least annoying 
> for the user.

All modern interactive applications and systems hang in this manner.
I can't remember the last time I used anything that didn't have variable
response time. Certain dedicated devices are responsive like that, for sure;
microwave ovens or alarm systems don't hang when you punch buttons.

If you want hard real time constraints, you don't do dynamic memory
allocation, period. The accurate computation of object lifetimes and their
disposal introduces unpredictable pauses, no matter how it is done.

Manual freeing is predictable? Hardly:

   void free_tree(tree *tree)
   {
      if (tree == NULL)
         return;

      free_tree(tree->left);
      free_tree(tree->right);
      free_data(tree->data);
      free(tree);
   }

How long does this take? Depends on the size of the tree.
0
Kaz
6/4/2014 6:24:25 PM
On Wednesday, June 4, 2014 7:05:17 PM UTC+1, Kaz Kylheku wrote:
>
> nobody in their right mind would prefer this interface [pass an error
> flag in as a pointer] when the function returns nothing.
> 
If you've got a convention that error shall always be a pointer and
shall always be the last parameter, it's best to stick to it.
The convention has some value when functions naturally return a
result, in some libraries results are returned through pointers,
purely to preserve the convention that the return type shall be
the error status. 

  
0
Malcolm
6/4/2014 7:35:35 PM
On Wed, 04 Jun 2014 07:19:39 -0600, Reinhardt Behm
<rbehm@hushmail.com> wrote:

>And this would add unpredictability to the program. The GC can step at any 
>inappropriate time, rendering real time behavior a total mess.
>In real world applications there are more requirements than just computing 
>the correct results. 
>Even if no hard real time constraints have to be met


And far fewer such applications with hard real-time requirements
actually exist than there are discussions of why GC is inappropriate
for real-time applications.

And for many that do, there is often only a small part of the system
with the hard real-time requirement, and you can set that up to run
without storage allocations, etc.

Of course there are a ton of things to mess up the nice "predictable"
model of computation on larger CPUs anyway, from OoO execution to
caches to interrupts...



>having an application 
>"hang" at undefinable moments for unpredictable time is a at least annoying 
>for the user.


GC, at least any semi-competent implementation, should not be the
cause of that problem.  There are a bunch of incremental or
non-blocking garbage collectors, more than a few in common use.
0
Robert
6/4/2014 9:08:56 PM
On 2014-06-04 20:05, Kaz Kylheku wrote:
> On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
>> On 2014-06-04 16:46, Malcolm McLean wrote:
>>> void foo(int x, int *error);
[...]
> - Is it okay for error to be null if I'm not interested in the error,
>    or must it always point to a valid location?
>
> - Does foo always store a result in *error, or only if there is an error?
>    I.e. must I initialize foo to some value (say zero) and then test
>    for nonzero? Or can I leave it uninitialized and test it afterward?

The parameter `error' is an output parameter so it must point to a valid 
location which is always written to.

> nobody in their right mind would prefer this interface when the
> function returns nothing.

I don't understand what you mean here.

> Then there is the minor point that when you're developing foo, perhaps the
> compiler has a useful diagnostic like "not all paths return a value"
> or "return with no value in a fucntion returning int".
>
> No compiler I know of will tell you "not all paths store a value into *error".

It is common practice to initialize an output parameter like this to 
either zero or one depending on whether successful or unsuccessful paths 
dominate the implementation.


-- August

0
August
6/4/2014 9:25:58 PM
On 2014-06-04, Reinhardt Behm <rbehm@hushmail.com> wrote:
> Richard Bos wrote:
>
>> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>> 
>>> Kaz Kylheku <kaz@kylheku.com> writes:
>>> 
>>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>>> > in sneaky ways---not just the obvious ways.
>> 
>>> Some garbage collectors (many? most? all? I don't know) provide a way to
>>> manually "free" a pointer's memory.
>> 
>> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
>> should say, especially) only some times, does IMO constitute "fairly
>> unfriendly". The point about GC is that you should be able to forget
>> about it. If you can't, IMAO, 90% of the point of GC is lost.
>> 
>> Richard
>
> And this would add unpredictability to the program. The GC can step at any 
> inappropriate time, rendering real time behavior a total mess.
> In real world applications there are more requirements than just computing 
> the correct results. 

What is the difference between an application and a real world application?
Can you name an application that is not a real world application?
0
Ike
6/4/2014 9:46:59 PM
On 2014-06-04, Kaz Kylheku <kaz@kylheku.com> wrote:
> On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
>> On 2014-06-04, Richard Bos <raltbos@xs4all.nl> wrote:
>>> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>>>
>>>> Kaz Kylheku <kaz@kylheku.com> writes:
>>>> 
>>>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>>>> > in sneaky ways---not just the obvious ways.
>>>
>>>> Some garbage collectors (many? most? all? I don't know) provide a way to
>>>> manually "free" a pointer's memory.
>>>
>>> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
>>> should say, especially) only some times, does IMO constitute "fairly
>>> unfriendly". The point about GC is that you should be able to forget
>>> about it. If you can't, IMAO, 90% of the point of GC is lost.
>>
>> Setting
>>
>>     ptr = NIL;
>>
>> in order to let the garbage collector free the memory pointed to by ptr
>> may be considered _manually_ GC-ing your memory as well.
>
> No, it may not, because if there are other copies of the object,
> it is not reclaimed. The ptr = nil assignment is innocent; it doesn't
> take on the full responsibility of computing the lifetime of the object;
> it only contributes to that information.
>
> Also, the C compiler may also cause spurious retention in cases like this:
>
>   {
>     var ptr = big_object();
>   }
>
>   computation();
>
> here instead of ptr = nil, we ended the scope of ptr, so it doesn't exist.
> (Is that also considered manual GC?)
>
> But the C compiler may well leave a memory location on the stack which
> still contains a pointer to big_object across computation.
>
> In a garbage collected (by design, from the ground up) language, the garbage
> collector has information from the compiler about where the live objects are;
> it doesn't have to just scan every location in the stack conservatively.  Or
> else, if the collector does just scan the stack, the compiler generates code
> which tidies up dead references.

If the 'ptr=NIL;' assignment is not meant to be a manual GC request,
then what would be the reason to perform the assignment at all?
Remember we're talking about an assignment that might be optimized
away (see elsethread), which gives the impression that it apparently
serves no other purpose than to trigger garbage collection.
0
Ike
6/4/2014 9:57:30 PM
On Wed, 2014-06-04, August Karlstrom wrote:
> On 2014-06-04 20:05, Kaz Kylheku wrote:
>> On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
>>> On 2014-06-04 16:46, Malcolm McLean wrote:
>>>> void foo(int x, int *error);
> [...]
>> - Is it okay for error to be null if I'm not interested in the error,
>>    or must it always point to a valid location?
>>
>> - Does foo always store a result in *error, or only if there is an error?
>>    I.e. must I initialize foo to some value (say zero) and then test
>>    for nonzero? Or can I leave it uninitialized and test it afterward?
>
> The parameter `error' is an output parameter so it must point to a valid 
> location which is always written to.

C doesn't have output parameters, so that would have to be a
(documented) convention.  I have personally used all three variants he
lists (may be NULL, indicates error/no error, accumulates errors) but
I had to choose and then document carefully.

>> nobody in their right mind would prefer this interface when the
>> function returns nothing.
>
> I don't understand what you mean here.

I think he's talking in a C context.  For one thing, such a construct
is almost unheard of in C. You said it yourself upthread --
C programmers generally don't accept that principle you're following.

/Jorgen

-- 
  // Jorgen Grahn <grahn@  Oo  o.   .     .
\X/     snipabacken.se>   O  o   .
0
Jorgen
6/4/2014 10:06:17 PM
On Wed, 04 Jun 2014 16:08:56 -0500
Robert Wessel <robertwessel2@yahoo.com> wrote:

> 
> 
> >having an application 
> >"hang" at undefinable moments for unpredictable time is a at least
> >annoying for the user.
> 
> 
> GC, at least any semi-competent implementation, should not be the
> cause of that problem.  There are a bunch of incremental or
> non-blocking garbage collectors, more than a few in common use.

GC is problematic on large heaps, that is causes non stop swapping.
That is why it is not advisable to use GC at virtual machines.
Other thing is that  non blocking GC-s require much more processing
time than stop the world collectors. Eg IBM concurrent collector
requires complex access to pointer/reference - that is membar and
some pointer chasing...

-- 
Click OK to continue...

0
Melzzzzz
6/4/2014 10:11:58 PM
Le 04/06/2014 21:35, Malcolm McLean a �crit :
> On Wednesday, June 4, 2014 7:05:17 PM UTC+1, Kaz Kylheku wrote:
>>
>> nobody in their right mind would prefer this interface [pass an error
>> flag in as a pointer] when the function returns nothing.
>>
> If you've got a convention that error shall always be a pointer and
> shall always be the last parameter, it's best to stick to it.
> The convention has some value when functions naturally return a
> result, in some libraries results are returned through pointers,
> purely to preserve the convention that the return type shall be
> the error status.
>
>
>

In the containers library I used another convention. Error return (an 
integer mostly) in the result, and parameters by pointers.

Functions that return pointers are also boolean: either it is a valid 
pointer or NULL. Obviously when ANY error happens, the standard 
callbacks are called, before any other action.

The library comes with default callbacks that put something in stderr 
and go on.

The library user can change this as he/she wants by changing the 
callbacks and putting other functions.

So:

int errorCode = Interface.functionXXX(Object *,...);

Positive: OK
Zero: OK with warnings
Negative: hard error

if (List.Append(memberslist,newSubscriptions ) >= 0)
      // OK;
else return NULL;



To do:

Extensible. You should be able to add your own error types to the table. 
I think that a utility could inspect the executable between two UUIDs, 
modifying parameters in the style:

What do you want to do

when there is no more memory?
When a bad pointer is discovered?
Array bounds exceeded?
Other internal inconsistencies?

Do you want to log all errors?
If yes where (path)?

Binary layout would be:

static uuid start = { };
/* Modifiable parameters */
size_t size;
char strict=0; // Go on by default
.... // Other parameters
statuc uuid end = { };

The size data field means the number of bytes to skip to find the end uuid.

A simple utility scans the executable for the start uuid, reads the size 
parameter, skips that number of bytes and verifies that the end uuid is 
also there.

Now, the default callback of the no memory error would look at the value 
of strict. If non zero it will exit the program. Other callbacks may or 
may not follow that convention.

This gives great flexibility to classify errors. For instance some 
memory-hog program crashes but the main programis not affected, it is an 
optional functionality, available only when more memory is there.


0
jacob
6/4/2014 10:12:50 PM
August Karlstrom <fusionfile@gmail.com> wrote:

> On 2014-06-04 20:05, Kaz Kylheku wrote:
> > On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
> >> On 2014-06-04 16:46, Malcolm McLean wrote:
> >>> void foo(int x, int *error);
> [...]
> > - Is it okay for error to be null if I'm not interested in the error,
> >    or must it always point to a valid location?
> >
> > - Does foo always store a result in *error, or only if there is an error?
> >    I.e. must I initialize foo to some value (say zero) and then test
> >    for nonzero? Or can I leave it uninitialized and test it afterward?
> 
> The parameter `error' is an output parameter so it must point to a valid 
> location which is always written to.

There is no such thing as an "output parameter" in C. There are only
pointers. Pointers can be null. It is the function's documentation's job
to declare whether this pointer _may_ also be null, or not. This is a
very different case from real output parameters, as used in, e.g.,
Pascal, which _cannot_ be null.
And yes, there are real reasons to have a function where an *error
pointer _may_ be null. And yes, there are real examples of functions
where this is the case. strtol() does something very similar.

Richard
0
raltbos
6/4/2014 10:16:19 PM
On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
> On 2014-06-04 20:05, Kaz Kylheku wrote:
>> On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
>>> On 2014-06-04 16:46, Malcolm McLean wrote:
>>>> void foo(int x, int *error);
> [...]
>> - Is it okay for error to be null if I'm not interested in the error,
>>    or must it always point to a valid location?
>>
>> - Does foo always store a result in *error, or only if there is an error?
>>    I.e. must I initialize foo to some value (say zero) and then test
>>    for nonzero? Or can I leave it uninitialized and test it afterward?
>
> The parameter `error' is an output parameter so it must point to a valid 
> location which is always written to.

Where is it declared that it's an output parameter?

The int foo(int x) return value is obviously nothing else but an output
from the semantics of returning.

>> nobody in their right mind would prefer this interface when the
>> function returns nothing.
>
> I don't understand what you mean here.

I.e. we are not using the return value for anything and have made it void, and
then are using a pointer to return the status.

We are ignoring the right tool for the job: the return mechanism.

>
>> Then there is the minor point that when you're developing foo, perhaps the
>> compiler has a useful diagnostic like "not all paths return a value"
>> or "return with no value in a fucntion returning int".
>>
>> No compiler I know of will tell you "not all paths store a value into *error".
>
> It is common practice to initialize an output parameter like this to 
> either zero or one depending on whether successful or unsuccessful paths 
> dominate the implementation.

It's not common *compiler* practice to enforce this, compared to useful
warnings related to return values.
0
Kaz
6/4/2014 11:58:36 PM
On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
> On 2014-06-04, Kaz Kylheku <kaz@kylheku.com> wrote:
>> On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
>>> On 2014-06-04, Richard Bos <raltbos@xs4all.nl> wrote:
>>>> Ben Bacarisse <ben.usenet@bsb.me.uk> wrote:
>>>>
>>>>> Kaz Kylheku <kaz@kylheku.com> writes:
>>>>> 
>>>>> > As far as GC goes, C is fairly unfriendly toward garbage collectors,
>>>>> > in sneaky ways---not just the obvious ways.
>>>>
>>>>> Some garbage collectors (many? most? all? I don't know) provide a way to
>>>>> manually "free" a pointer's memory.
>>>>
>>>> Yeah. But requiring you to _manually_ GC your memory, even (or perhaps I
>>>> should say, especially) only some times, does IMO constitute "fairly
>>>> unfriendly". The point about GC is that you should be able to forget
>>>> about it. If you can't, IMAO, 90% of the point of GC is lost.
>>>
>>> Setting
>>>
>>>     ptr = NIL;
>>>
>>> in order to let the garbage collector free the memory pointed to by ptr
>>> may be considered _manually_ GC-ing your memory as well.
>>
>> No, it may not, because if there are other copies of the object,
>> it is not reclaimed. The ptr = nil assignment is innocent; it doesn't
>> take on the full responsibility of computing the lifetime of the object;
>> it only contributes to that information.
>>
>> Also, the C compiler may also cause spurious retention in cases like this:
>>
>>   {
>>     var ptr = big_object();
>>   }
>>
>>   computation();
>>
>> here instead of ptr = nil, we ended the scope of ptr, so it doesn't exist.
>> (Is that also considered manual GC?)
>>
>> But the C compiler may well leave a memory location on the stack which
>> still contains a pointer to big_object across computation.
>>
>> In a garbage collected (by design, from the ground up) language, the garbage
>> collector has information from the compiler about where the live objects are;
>> it doesn't have to just scan every location in the stack conservatively.  Or
>> else, if the collector does just scan the stack, the compiler generates code
>> which tidies up dead references.
>
> If the 'ptr=NIL;' assignment is not meant to be a manual GC request,
> then what would be the reason to perform the assignment at all?

Note that in a properly designed garbage collected language we wouldn't need
this assignment.

The garbage collector, somehow in cooperation with the compiler, already knows
that ptr is a dead variable: a variable with no next use in the data flow
graph! (For instance, the compiler might publish liveness information whereby
for a given instruction pointer, there is a table of stack offsets where
variables live in the current frame, and perhaps a bitmask of registers which
contain live values. The register or memory location containing ptr is not
listed. Or, the compiler could generate the "ptr = nil" code for dead
variables, and somehow mark it so that later optimizations don't remove it.)

So, yes, this assignment this is a manual "something". It's not exactly a GC
request because it doesn't necessarily end the lifetime of the object,
nor does it trigger GC. It's a manual attempt way to propagate the end-of-life
of the variable to GC.

Except that it doesn't work without some additional trick due to the
optimization that is carried out without any GC integration.

> Remember we're talking about an assignment that might be optimized
> away (see elsethread), which gives the impression that it apparently
> serves no other purpose than to trigger garbage collection.

Yes; it is written in hopes that if ptr has the last reference then
the object becomes garbage.
0
Kaz
6/5/2014 12:12:50 AM
On 2014-06-04, Melzzzzz <mel@zzzzz.invalid> wrote:
> On Wed, 04 Jun 2014 16:08:56 -0500
> Robert Wessel <robertwessel2@yahoo.com> wrote:
>
>> 
>> 
>> >having an application 
>> >"hang" at undefinable moments for unpredictable time is a at least
>> >annoying for the user.
>> 
>> 
>> GC, at least any semi-competent implementation, should not be the
>> cause of that problem.  There are a bunch of incremental or
>> non-blocking garbage collectors, more than a few in common use.
>
> GC is problematic on large heaps, that is causes non stop swapping.
> That is why it is not advisable to use GC at virtual machines.

Without massive qualifications, this is simply nonsense.

"Virtual machine" is not the same as "virtual memory". (Why wouldn't
you use GC on a virtual machine that is fully backed by adequate RAM.)

Generational GC avoids making passes over old data that wastefully confirm that
old data that was previously reachable is still reachable.

> Other thing is that  non blocking GC-s require much more processing
> time than stop the world collectors.

You don't get something for nothing.

Here, we can make the broad observation that there is a tradeoff between
achieving low latency and high throughput.

If a system's throughput is well optimized, you will hardly be able to
improve its latency without giving up some throughput, and vice versa.
0
Kaz
6/5/2014 12:22:06 AM
On 04-Jun-14 19:12, Kaz Kylheku wrote:
> On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
>> If the 'ptr=NIL;' assignment is not meant to be a manual GC
>> request, then what would be the reason to perform the assignment at
>> all?
> 
> Note that in a properly designed garbage collected language we
> wouldn't need this assignment.
> 
> The garbage collector, somehow in cooperation with the compiler,
> already knows that ptr is a dead variable: a variable with no next
> use in the data flow graph!

Even an ideal garbage collector and compiler cannot always prove that a
object is never referenced again, especially if the reference is in a
static or global variable.

And current garbage collectors and compilers are far from ideal.  For
instance, many garbage collectors can't free circular references; you
have to clear one of the references for the circle to be collected.

> So, yes, this assignment this is a manual "something". It's not
> exactly a GC request because it doesn't necessarily end the lifetime
> of the object, nor does it trigger GC. It's a manual attempt way to
> propagate the end-of-life of the variable to GC.

Exactly, plus it may also convey information to other programmers, even
if it's not useful to the GC system (or future, better GC systems).

S

-- 
Stephen Sprunk         "God does not play dice."  --Albert Einstein
CCIE #3723         "God is an inveterate gambler, and He throws the
K5SSS        dice at every possible opportunity." --Stephen Hawking
0
Stephen
6/5/2014 3:05:27 AM
On Wed, 04 Jun 2014 22:05:27 -0500, Stephen Sprunk
<stephen@sprunk.org> wrote:

>On 04-Jun-14 19:12, Kaz Kylheku wrote:
>> On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
>>> If the 'ptr=NIL;' assignment is not meant to be a manual GC
>>> request, then what would be the reason to perform the assignment at
>>> all?
>> 
>> Note that in a properly designed garbage collected language we
>> wouldn't need this assignment.
>> 
>> The garbage collector, somehow in cooperation with the compiler,
>> already knows that ptr is a dead variable: a variable with no next
>> use in the data flow graph!
>
>Even an ideal garbage collector and compiler cannot always prove that a
>object is never referenced again, especially if the reference is in a
>static or global variable.
>
>And current garbage collectors and compilers are far from ideal.  For
>instance, many garbage collectors can't free circular references; you
>have to clear one of the references for the circle to be collected.


Are you saying this from a C perspective?  Because it's just not true
for most GC'd languages.  Even the most basic mark-and-sweep
collectors will handle circular references.
0
Robert
6/5/2014 3:33:09 AM
On 2014-06-05 01:58, Kaz Kylheku wrote:
> On 2014-06-04, August Karlstrom <fusionfile@gmail.com> wrote:
>> The parameter `error' is an output parameter so it must point to a valid
>> location which is always written to.
>
> Where is it declared that it's an output parameter?

It is semantically an output parameter in the same sense that the return 
value of the previously mentioned `int foo(int x)' is semantically an 
error flag.

>>> nobody in their right mind would prefer this interface when the
>>> function returns nothing.
>>
>> I don't understand what you mean here.
>
> I.e. we are not using the return value for anything and have made it void, and
> then are using a pointer to return the status.
>
> We are ignoring the right tool for the job: the return mechanism.

In my opinion the return mechanism is the right tool for the job only 
when we are writing a pure (side effect free) function. To me there is 
something inherently ugly about expressions with side effects. They also 
make it harder to reason about program correctness.


-- August
0
August
6/5/2014 8:31:23 AM
On Thursday, June 5, 2014 9:31:23 AM UTC+1, August Karlstrom wrote:
> 
> In my opinion the return mechanism is the right tool for the job only 
> when we are writing a pure (side effect free) function. To me there is 
> something inherently ugly about expressions with side effects. They also 
> make it harder to reason about program correctness.
> 
> 
if( foo(x, y, z) == 0)
   bar();
else
  bar();

Looks like it ought to be replaceable by

bar();

or, more subtly 

if(foo(x, z, z) == 0 || bar(x, y, z) == 0)

looks like it ought to be replaceable with

if (bar(x, y, z) == 0 || foo(x, y, z) == 0)
0
Malcolm
6/5/2014 9:24:27 AM
On 2014-06-05, Robert Wessel <robertwessel2@yahoo.com> wrote:
> On Wed, 04 Jun 2014 22:05:27 -0500, Stephen Sprunk
><stephen@sprunk.org> wrote:
>
>>On 04-Jun-14 19:12, Kaz Kylheku wrote:
>>> On 2014-06-04, Ike Naar <ike@iceland.freeshell.org> wrote:
>>>> If the 'ptr=NIL;' assignment is not meant to be a manual GC
>>>> request, then what would be the reason to perform the assignment at
>>>> all?
>>> 
>>> Note that in a properly designed garbage collected language we
>>> wouldn't need this assignment.
>>> 
>>> The garbage collector, somehow in cooperation with the compiler,
>>> already knows that ptr is a dead variable: a variable with no next
>>> use in the data flow graph!
>>
>>Even an ideal garbage collector and compiler cannot always prove that a
>>object is never referenced again, especially if the reference is in a
>>static or global variable.
>>
>>And current garbage collectors and compilers are far from ideal.  For
>>instance, many garbage collectors can't free circular references; you
>>have to clear one of the references for the circle to be collected.
>
>
> Are you saying this from a C perspective?  Because it's just not true
> for most GC'd languages.  Even the most basic mark-and-sweep
> collectors will handle circular references.

Indeed, it is specifically reference counting which has that problem.

To recognize reference counting as a form of GC is to be charitably
open-minded.
0
Kaz
6/5/2014 1:29:28 PM
On Thu, 5 Jun 2014 02:24:27 -0700 (PDT), Malcolm McLean wrote:
>On Thursday, June 5, 2014 9:31:23 AM UTC+1, August Karlstrom wrote:
>> 
>> In my opinion the return mechanism is the right tool for the job only 
>> when we are writing a pure (side effect free) function. To me there is 
>> something inherently ugly about expressions with side effects. They also 
>> make it harder to reason about program correctness.
>> 
>> 
>if( foo(x, y, z) == 0)
>   bar();
>else
>  bar();
>
>Looks like it ought to be replaceable by
>
>bar();

not if foo() change some global variable that bar() use...

>or, more subtly 
>
>if(foo(x, z, z) == 0 || bar(x, y, z) == 0)
>
>looks like it ought to be replaceable with
>
>if (bar(x, y, z) == 0 || foo(x, y, z) == 0)

not if foo() change some global variable that bar() use...
0
Rosario193
6/5/2014 4:27:28 PM
Reply: