May 02, 2012
You can replace "signed integer overflow" with pretty much whatever 'guarantee' in C that suits your fancy.
See below.


"Jonathan M Davis"  wrote in message news:mailman.207.1335944070.24740.digitalmars-d@puremagic.com...
> I don't follow.
Let's see if this is better.

> The D compiler guarantees that as long as you don't cast away const, const will never be mutated.
The C compiler guarantees that as long as signed integers don't overflow, their results will be correct.

> If you _do_ cast away const and then mutate the variable, you are doing something which is undefined.
If you _do_ overflow signed integers, their results will be undefined.

> As it is undefined behavior, _anything_ could happen if you do it, and the compiler is free to assume that it will never happen.
As it is undefined behavior, the result could be _anything_ if you do it, and the compiler is free to assume that it will never happen.

> So, it effectively has a guarantee that a const variable will never be mutated (save by another, mutable reference to the same data). It then uses that guarantee for optimizations.
So, it effectively has a guarantee that a signed integer will never be overflown. It then uses that guarantee for optimizations.

> To make it 100% iron-clan, casting away const would have to be illegal, but that would be unacceptable in a systems language - particularly when you want to be able to call C functions which may not be properly annotated.
To make it 100% iron-clan, signed overflow would have to be illegal, but that would be unacceptable in a systems language - particularly when you need to depend on system-dependent behavior.

> So instead, the compiler assumes that its guarantee holds in the case where you cast away const, and is still able to use it for optimizations.
So instead, the compiler assumes that its guarantee holds in the case where overflow a signed integer, and is still able to use it for optimizations.

> C++ _does_ define what happens when you cast away const and mutate a variable, so it guarantees that that behavior will be safe.
D _does_ define what happens when you overflow a signed integer, so it guarantees that that behavior will be safe.

> In so doing however, it is then unable to assume that casting away const will not result in the variable being mutated and is therefore unable to use const for much in the way of optimizations.
In so doing however, it is then unable to assume that a signed integer will never be overflown, and is therefore unable to use signed integer overflow for much in the way of optimizations.

- Jonathan M Davis
Mehrdad

May 02, 2012
> > Wouldn't "physical" const-ness be an implementation detail of the object, and therefore, impossible to determine by the user of the object?
> No, it's not an implementation detail. When you mark an object as being physically const, then you're guaranteeing that you will not alter it through that reference or pointer.

I think you misunderstood my question.

Yes, __IF__ you mark an object as physically const, then the world is beautiful...

My question is, __WHEN__ can you ever do that, except in the most trivial situations?
As soon as you try to add an extra layer of indirection (be it a proxy, implementing a method in an interface, overriding a base class method, etc.), there is NO WAY for the caller to know what obj.foo() does on obj. How can it possibly know whether obj will stay physically const? 

May 02, 2012
On 05/02/2012 08:48 AM, Mehrdad wrote:
> In the world of OOP,

interface Readonly{
    auto read(){ ... }
}

class Mutable{
    auto read(){ ... }
    void write(int x){ ... }

    Readonly getReadonly(){ ... }
}

private class Adapter: Readonly{
    Mutable field;
    auto read(){ return field.read(); }
}


> when would "guarantee"ing (so to speak) 'physical'
> const-ness ever be handy?
>

Concurrency?

> Wouldn't "physical" const-ness be an implementation detail of the
> object, and therefore,

If it is, don't use the const qualifier.

> impossible to determine by the user of the object?

It is possible because 'const' is part of the method interface.
May 02, 2012
On Wednesday, May 02, 2012 01:12:15 Mehrdad wrote:
> > > Wouldn't "physical" const-ness be an implementation detail of the object, and therefore, impossible to determine by the user of the object?
> > 
> > No, it's not an implementation detail. When you mark an object as being physically const, then you're guaranteeing that you will not alter it through that reference or pointer.
> 
> I think you misunderstood my question.
> 
> Yes, __IF__ you mark an object as physically const, then the world is beautiful...
> 
> My question is, __WHEN__ can you ever do that, except in the most trivial
> situations?
> As soon as you try to add an extra layer of indirection (be it a proxy,
> implementing a method in an interface, overriding a base class method,
> etc.), there is NO WAY for the caller to know what obj.foo() does on obj.
> How can it possibly know whether obj will stay physically const?h

Because foo must be const, or if can't be called on a const object. And if it's const, then it can't call any non-const functions or mutate any of its member variables. If you have

interface I
{
    int foo() const;
}

class C : I
{
    int foo() const
    {...}
}

C's foo _must_ be const, or it's not implementing I's foo, and it won't compile. And if C's foo is const, then it can't call a non-const function or mutate any of its member variables. If it were pure on top of that, then it couldn't mutate any global or class variables either. The same goes for any derived class which overrides foo. const is part of foo's signature, and no derived class can escape that. So, _every_ class which implements I, and _every_ class derived from C will have a const foo which will be unable to mutate the state of that object.

If instead you did

class D
{
    int bar() const
    {
        return e.bar();
    }

    E e;
}

then E's bar would have to be const, or it wouldn't be callable from D's bar, since the e member variable is const inside of D's bar, since D's bar is const, and you can't call a non-const function on a const variable.

An extra layer of indirection doesn't escape const at all, because that layer of indirection must use const or it won't be usable by the outer layer. So, the type system is able to guarantee that when you call a const function, the object it's being called on - as well as any object that it contains directly or indirectly - will not be mutated.

- Jonathan M Davis
May 02, 2012
On Wednesday, May 02, 2012 01:06:47 Mehrdad wrote:
> You can replace "signed integer overflow" with pretty much whatever
> 'guarantee' in C that suits your fancy.
> See below.
> 
> 
> "Jonathan M Davis"  wrote in message news:mailman.207.1335944070.24740.digitalmars-d@puremagic.com...
> 
> > I don't follow.
> 
> Let's see if this is better.
> 
> > The D compiler guarantees that as long as you don't cast away const, const will never be mutated.
> 
> The C compiler guarantees that as long as signed integers don't overflow, their results will be correct.
> 
> > If you _do_ cast away const and then mutate the variable, you are doing something which is undefined.
> 
> If you _do_ overflow signed integers, their results will be undefined.
> 
> > As it is undefined behavior, _anything_ could happen if you do it, and the compiler is free to assume that it will never happen.
> 
> As it is undefined behavior, the result could be _anything_ if you do it, and the compiler is free to assume that it will never happen.
> 
> > So, it effectively has a guarantee that a const variable will never be mutated (save by another, mutable reference to the same data). It then uses that guarantee for optimizations.
> 
> So, it effectively has a guarantee that a signed integer will never be overflown. It then uses that guarantee for optimizations.
> 
> > To make it 100% iron-clan, casting away const would have to be illegal, but that would be unacceptable in a systems language - particularly when you want to be able to call C functions which may not be properly annotated.
> 
> To make it 100% iron-clan, signed overflow would have to be illegal, but that would be unacceptable in a systems language - particularly when you need to depend on system-dependent behavior.
> 
> > So instead, the compiler assumes that its guarantee holds in the case where you cast away const, and is still able to use it for optimizations.
> 
> So instead, the compiler assumes that its guarantee holds in the case where overflow a signed integer, and is still able to use it for optimizations.
> 
> > C++ _does_ define what happens when you cast away const and mutate a variable, so it guarantees that that behavior will be safe.
> 
> D _does_ define what happens when you overflow a signed integer, so it guarantees that that behavior will be safe.
> 
> > In so doing however, it is then unable to assume that casting away const will not result in the variable being mutated and is therefore unable to use const for much in the way of optimizations.
> 
> In so doing however, it is then unable to assume that a signed integer will never be overflown, and is therefore unable to use signed integer overflow for much in the way of optimizations.

All of that might hold if the compiler could actually make optimizations based on the knowledge that an integer wouldn't overflow. But what optimizations could it make?

Regardless, I don't see how it's particularly relevant. Even if there were optimizations which C/C++ could make which D can't (which may or may not be the case), that wouldn't have any bearing on how D's const works.

D's const and C/C++'s const have different goals and different pros and cons. D's const is physical const, whereas C/C++'s const is an attempt at logical const (though it isn't really logical const either, because it makes no guarantees that the logical state of the object remains the same - only that you don't directly mutate any const variables).

- Jonathan M Davis
May 02, 2012
On 05/02/2012 10:26 AM, Jonathan M Davis wrote:
> All of that might hold if the compiler could actually make optimizations based
> on the knowledge that an integer wouldn't overflow. But what optimizations
> could it make?
>

x+1 > x ==> true
May 02, 2012
On 05/02/2012 04:14 AM, Mehrdad wrote:
> Yes, that non-global issue was the exact issue I was referring to. It
> drives me nuts whenever I try to give in and use templates.
>
>
> Regarding your "fix":
> Is it *really* intended that user should say
> arr.filter((typeof(some_random_expression) x) => x < y);
> instead of
> arr.filter(x => x < y);
> ?
>
> I think it's pretty obvious why that doesn't really work in practice...

I agree that it should be fixed, but it is technically not a bug in the implementation.
May 02, 2012
On 05/02/12 04:11, Mehrdad wrote:
> More problems... similar, but this time related to templates.
> 
>  struct Filter(R) { this(R) { } }
>  template filter(R) { alias Filter!(R).__ctor filter; }
>  void main() { filter([1, 2, 3]); }
> 
> Error: template linq.filter(R) is not a function template
> 
> 
> Why?

Because it isn't? Where would 'R' come from in your example?...

   auto filter(R)(R r) { return Filter!R(r); }


artur
May 02, 2012
On Wednesday, 2 May 2012 at 06:10:04 UTC, Mehrdad wrote:
> I don't think you answered my question.
>
> What I said (or meant to ask) was this:
> - OK, FINE, let's say I don't know what D's const() means, then. Maybe it isn't suitable for what I need. I just want to know:
>  _How do you specify that the function process1() won't modify its 'student' parameter_?

If you want just to specify that (and not ask the compiler to check for you), documentation will do.

>
> Your response was this:
> - "Does const(Student) mean that it shouldn't write to the server?"
>
> To which my answer is:
> - I don't know, you tell me. (?) _Should_ I use 'const' to specify this? Or should I use something else?

I can't tell you what you want. You have to tell me. But no, you shouldn't use const to specify this. const means you're object won't be changed by you. Here's a decision tree for if you can use const and immutable

Will the object (including all of its fields) ever change?
no -> immutable (END)
yes ->
Will the object (including all of its fields) be changed by you?
no -> const (END)
yes -> No qualifier

That's _any_ kind of modification of state. If you change state, you can't use immutable/const (nor would you really want to).

> The problem with your answer is that it implies D DOESN'T SUPPORT O.O.P. with const!!!
>
> If D supported OOP, then why the heck would process1() know (or _care_) how Student works internally?
> Why (or how) should it know (or care) that Student uses a database connection?

When you say "const(Student)" you're saying that whatever Student does, you want to know whatever you do won't change its internal state.

I'm not sure you're using OOP properly. If you want to use OOP to solve the problem, my suggestion is closer to OOP than using const. If you don't want to talk about database connections and everything (i.e., you don't want to use D's outrageously nice templates), you can just have it be an interface instead. In fact an interface would probably be more appropriate.

interface ROStudent {
   getName();
   getAddress();
   ...
}

process1(ROStudent student);

Effectively, though, it's the same sort of thing.

D's const/immutable require a different way of thinking of it. What are they useful for? Consider a multithreaded program where some threads depend on information that others have. They need the information but they won't change state (which makes it much safer/faster to work with because locking isn't as necessary ... with immutable locking is actually silly). That's what const/immutable is for.

In C++, you'd be foolish to not have locks set up even for const variables because they can change at any time for any reason. It's hardly a guarantee and it's so common to violate (look at yourself, for instance) that it means _nothing_. I liked how it was described as a glorified comment, because that's precisely how I think of it.

> Wasn't that information an implementation detail?
>
>
> All process1() needs to know is the *PUBLIC INTERFACE* of Student.
> And all it cares to say is: "According to the public interface of Student, none of the methods I will use will modify the object."
> process1() has NO IDEA what goes on internally! It has NO IDEA that Student uses a database connection. Maybe it's a network connnection instead. Or maybe it's being unit-tested, and it's actually a dummy object, with no relation to the outside world. Or maybe it's a wrapper/proxy for another object.
>
> The point is: _why should the code for process1() depend on this_?!

If that's what you want, then that's precisely what D does. "According to the public interface of Student, none of the methods I will use will modify the object." As opposed to C++'s method of "According to the public interface of student, these methods have comments that say that they say they won't change the object, but I can't tell if it does or not."

Again, using an interface ROStudent/StudentReader/etc is really your "OOP solution".

>
>
>
>
> So, I'll ask my question again:
>
> How should process1() tell the outside world that it will not be asking its parameter to manipulate itself, WITHOUT breaking the abstraction barrier and 'peeking' into the private world of Student?

I'll respond again: It depends on what you want. If you won't be changing the object, then const/immutable does that. If you will be changing the object, but you want guarantees that it won't write to some external source/data structure, you'll have to come up with the more precise way you want to define that. I gave you two potential approaches.


----

Really though, the C++ way is a complete and total minefield in this regard anyway. Consider Student defined like so:

Student {
name, id, address, phone_number;
connection;
getName() const {
    if(!name) {
        // cast away const
        thisnonconst.name = connection.get("name");
    }
    return name;
}
writeName() {
    connection.set("name", name);
}
}

Now this follows one particular understanding of const (the one I think you're getting at). However, imagine another person comes along and sees that "writeName" doesn't write to Student's state. Helpfully, he adds const to the end of writeName and breaks your type system. In fact, it's actually _easier_ to violate "that kind of const" than it is to use it, because your "getName" method has to do fancy casting away of const.

In fact, D's way, if you specify that Student can only use a ReadOnlyConnection (or if you use ROStudent/StudentReader interface), then the compiler will guarantee that it has a ReadOnlyConnection. If ReadOnlyConnection doesn't write to the database, then that Student is _guaranteed_ to not write to the database.

If you want the compiler to check that, the D offers the ability to. C++, on the other hand (using const), can barely suggest that possibility, since it's so easy to violate.


May 02, 2012
On Tue, 01 May 2012 22:11:18 -0400, Mehrdad <wfunction@hotmail.com> wrote:

> More problems... similar, but this time related to templates.
>
>   struct Filter(R) { this(R) { } }
>   template filter(R) { alias Filter!(R).__ctor filter; }
>   void main() { filter([1, 2, 3]); }
>
> Error: template linq.filter(R) is not a function template

It's an annoying limitation.  IFTI specifically *only* works with functions.  Not aliases, not types, not anything else.  Functions only.

-Steve