Re: logical const idea - scratchspace

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 21:51, Steven Schveighoffer wrote:

I have an idea on how to create logical const without any language or
compiler changes -- it will exist purely in druntime.

The idea is based on this: Whenever you allocate an object, you use a
memory block. An object, by default, has the following memory layout:

monitor - (void *).sizeof bytes
vtbl - (void *).sizeof bytes
interface_vtbls[] - (void *)sizeof x number of interfaces.

So by default, 8 bytes on 32bit, 16 bytes on 64 bit.

Add any members, and they may increase the size.

This object goes into the GC heap. Yet the GC heap has only 16, 32, 64,
128, etc. sized blocks.

So for instance a class object that requires 24 bytes actually consumes
32. This leaves 8 bytes of scratch space. Using a druntime lookup we
can get access to that entire memory block, including the scratch space.
And since we use druntime to look it up, *not* the object and its
contained members (which remember don't include the scratch space), it
is *not* typed as const or immutable, or whatever the class data is.

In essence, a const(MyObj) is a pointer to a struct that looks like:

struct FicticiousMyObjStruct
{
const(MyObj_data); // not a reference, the actual data
ubyte[8] scratchspace;
}

So we need two pieces for this proposal:

1. An accessor in Object for this scratch space. This should be a)
efficient, and b) opaque.
2. An allocator for a new object that can allocate a minimal scratch
space. So for instance, if your object consumes 32 bytes, but you need
20 bytes of scratch space, you want the runtime to allocate a 64 byte
block. So instead of saying new MyObject, you'd say
newScratchSpace!MyObject(20)

And I think that's it. Since nothing before this proposal ever referred
to or used that scratch space, it's not in danger of breaking existing
code. The only caveat is, it can't properly be typed as shared or not
(it could be accessible from multiple threads, depending on if the
actual type is immutable).

It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.


That renders it useless for caching e.g. a string though...



And of course, we need a better name than newScratchSpace.

-Steve


--
- Alex


Re: logical const idea - scratchspace

2012-05-14 Thread H. S. Teoh
On Mon, May 14, 2012 at 03:51:09PM -0400, Steven Schveighoffer wrote:
 I have an idea on how to create logical const without any language
 or compiler changes -- it will exist purely in druntime.
[...]
 In essence, a const(MyObj) is a pointer to a struct that looks like:
 
 struct FicticiousMyObjStruct
 {
   const(MyObj_data); // not a reference, the actual data
   ubyte[8] scratchspace;
 }
 
 So we need two pieces for this proposal:
 
 1. An accessor in Object for this scratch space.  This should be a)
 efficient, and b) opaque.
 2. An allocator for a new object that can allocate a minimal scratch
 space.  So for instance, if your object consumes 32 bytes, but you
 need 20 bytes of scratch space, you want the runtime to allocate a
 64 byte block.  So instead of saying new MyObject, you'd say
 newScratchSpace!MyObject(20)
 
 And I think that's it.  Since nothing before this proposal ever
 referred to or used that scratch space, it's not in danger of
 breaking existing code.  The only caveat is, it can't properly be
 typed as shared or not (it could be accessible from multiple
 threads, depending on if the actual type is immutable).
[...]

This is all cool and everything, but I'm having a bit of trouble
imagining how this helps us to implement logical const. Is the idea
merely for every object to come with some sort of extra untyped
non-const space that can be used for memoization, storing temporary
state, etc.? How is this different from introducing a 'mutable' keyword
to the language, or some other such change? (A mutable member of a const
object is essentially the same as a member stored in this 'scratch
space' of yours.)

This still doesn't help establish logical constness, since nothing stops
you from creating a class with all data inside the scratch space, in
which case const/immutable no longer has any meaning. Then we will have
devolved back to the land of C++'s const, where calling an immutable
method can freely modify arbitrary data (except that instead of a cast
we use scratchspace).


T

-- 
Just because you survived after you did it, doesn't mean it wasn't stupid!


Re: logical const idea - scratchspace

2012-05-14 Thread Dmitry Olshansky

On 14.05.2012 23:51, Steven Schveighoffer wrote:

I have an idea on how to create logical const without any language or
compiler changes -- it will exist purely in druntime.

The idea is based on this: Whenever you allocate an object, you use a
memory block. An object, by default, has the following memory layout:

monitor - (void *).sizeof bytes
vtbl - (void *).sizeof bytes
interface_vtbls[] - (void *)sizeof x number of interfaces.

So by default, 8 bytes on 32bit, 16 bytes on 64 bit.

Add any members, and they may increase the size.

This object goes into the GC heap. Yet the GC heap has only 16, 32, 64,
128, etc. sized blocks.

So for instance a class object that requires 24 bytes actually consumes
32. This leaves 8 bytes of scratch space. Using a druntime lookup we
can get access to that entire memory block, including the scratch space.
And since we use druntime to look it up, *not* the object and its
contained members (which remember don't include the scratch space), it
is *not* typed as const or immutable, or whatever the class data is.

In essence, a const(MyObj) is a pointer to a struct that looks like:

struct FicticiousMyObjStruct
{
const(MyObj_data); // not a reference, the actual data
ubyte[8] scratchspace;
}

So we need two pieces for this proposal:

1. An accessor in Object for this scratch space. This should be a)
efficient, and b) opaque.
2. An allocator for a new object that can allocate a minimal scratch
space. So for instance, if your object consumes 32 bytes, but you need
20 bytes of scratch space, you want the runtime to allocate a 64 byte
block. So instead of saying new MyObject, you'd say
newScratchSpace!MyObject(20)



Hack of the year?
It looks somewhat backwards but I like it. Especially the no changes in 
the compiler/language.



And I think that's it. Since nothing before this proposal ever referred
to or used that scratch space, it's not in danger of breaking existing
code. The only caveat is, it can't properly be typed as shared or not
(it could be accessible from multiple threads, depending on if the
actual type is immutable).


I take it that you just love reusing slack space found after the sloppy 
D runtime in some beneficial nontrivial way! :)




It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.

And of course, we need a better name than newScratchSpace.

-Steve



--
Dmitry Olshansky


Re: logical const idea - scratchspace

2012-05-14 Thread Steven Schveighoffer

On Mon, 14 May 2012 15:56:37 -0400, Alex Rønne Petersen
xtzgzo...@gmail.com wrote:


On 14-05-2012 21:51, Steven Schveighoffer wrote:



It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.


That renders it useless for caching e.g. a string though...


Yes, it does.  Unless you know the size of the string (so you can allocate
enough scratch space to hold it).

It's not perfect, for sure.  But it might be better than nothing...

-Steve


Re: logical const idea - scratchspace

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 22:13, Steven Schveighoffer wrote:

On Mon, 14 May 2012 15:56:37 -0400, Alex Rønne Petersen
xtzgzo...@gmail.com wrote:


On 14-05-2012 21:51, Steven Schveighoffer wrote:



It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.


That renders it useless for caching e.g. a string though...


Yes, it does. Unless you know the size of the string (so you can allocate
enough scratch space to hold it).

It's not perfect, for sure. But it might be better than nothing...

-Steve


But is there any reason we can't just have the GC check the scratch 
space? If it's all zero, it clearly contains nothing of interest, but if 
it's non-zero, just scan it like regular object memory.


--
- Alex


Re: logical const idea - scratchspace

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 21:51, Steven Schveighoffer wrote:

I have an idea on how to create logical const without any language or
compiler changes -- it will exist purely in druntime.

The idea is based on this: Whenever you allocate an object, you use a
memory block. An object, by default, has the following memory layout:

monitor - (void *).sizeof bytes
vtbl - (void *).sizeof bytes
interface_vtbls[] - (void *)sizeof x number of interfaces.

So by default, 8 bytes on 32bit, 16 bytes on 64 bit.

Add any members, and they may increase the size.

This object goes into the GC heap. Yet the GC heap has only 16, 32, 64,
128, etc. sized blocks.

So for instance a class object that requires 24 bytes actually consumes
32. This leaves 8 bytes of scratch space. Using a druntime lookup we
can get access to that entire memory block, including the scratch space.
And since we use druntime to look it up, *not* the object and its
contained members (which remember don't include the scratch space), it
is *not* typed as const or immutable, or whatever the class data is.

In essence, a const(MyObj) is a pointer to a struct that looks like:

struct FicticiousMyObjStruct
{
const(MyObj_data); // not a reference, the actual data
ubyte[8] scratchspace;
}

So we need two pieces for this proposal:

1. An accessor in Object for this scratch space. This should be a)
efficient, and b) opaque.
2. An allocator for a new object that can allocate a minimal scratch
space. So for instance, if your object consumes 32 bytes, but you need
20 bytes of scratch space, you want the runtime to allocate a 64 byte
block. So instead of saying new MyObject, you'd say
newScratchSpace!MyObject(20)

And I think that's it. Since nothing before this proposal ever referred
to or used that scratch space, it's not in danger of breaking existing
code. The only caveat is, it can't properly be typed as shared or not
(it could be accessible from multiple threads, depending on if the
actual type is immutable).

It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.

And of course, we need a better name than newScratchSpace.

-Steve


Another concern I have is that this couples a feature tightly to the 
implementation of the GC. What if another GC doesn't use the same 
allocation scheme?


--
- Alex


Re: logical const idea - scratchspace

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 23:06, Alex Rønne Petersen wrote:

On 14-05-2012 22:13, Steven Schveighoffer wrote:

On Mon, 14 May 2012 15:56:37 -0400, Alex Rønne Petersen
xtzgzo...@gmail.com wrote:


On 14-05-2012 21:51, Steven Schveighoffer wrote:



It also should be recommended that the scratch space not contain any GC
pointers, since it's *not* participating in the type system properly,
the GC may not treat it as a pointer.


That renders it useless for caching e.g. a string though...


Yes, it does. Unless you know the size of the string (so you can allocate
enough scratch space to hold it).

It's not perfect, for sure. But it might be better than nothing...

-Steve


But is there any reason we can't just have the GC check the scratch
space? If it's all zero, it clearly contains nothing of interest, but if
it's non-zero, just scan it like regular object memory.



Further, we could use a user marking scheme where writing anything 
non-zero to the space flags it dirty or something. There are probably 
lots of ways we could do this.


--
- Alex


Re: logical const idea - scratchspace

2012-05-14 Thread Steven Schveighoffer

On Mon, 14 May 2012 17:11:14 -0400, Alex Rønne Petersen
xtzgzo...@gmail.com wrote:

Another concern I have is that this couples a feature tightly to the  
implementation of the GC. What if another GC doesn't use the same  
allocation scheme?


newScratchSpace uses GC.malloc to ensure the block is big enough.  The GC
must support returning a block of memory large enough to hold the
requested bytes.

It's not tightly coupled, even though it depends on the GC.

-Steve


Re: logical const without casts!

2011-09-30 Thread Christophe
Steven Schveighoffer , dans le message (digitalmars.D:145767), a
 écrit :
 On Thu, 29 Sep 2011 13:26:11 -0400, Steven Schveighoffer  
 schvei...@yahoo.com wrote:
 
 On Thu, 29 Sep 2011 13:06:55 -0400, Simen Kjaeraas  
 simen.kja...@gmail.com wrote:

 On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer  
 schvei...@yahoo.com wrote:

 I just thought of an interesting way to make a logical const object  
 without casts.  It requires a little extra storage, but works without  
 changes to the current compiler (and requires no casts).
 [snip]
 What do people think about this?

 This is what I think about it:


 I agree this breaks immutability, and needs to be addressed.  I think  
 probably implicit casting of delegates (or items that containe  
 delegates) to immutable from strong-pure functions should be disallowed.

 But it's not the pattern I described, and uses a relatively new trick  
 (implicit immutable casting).  I'll file a bug for this case.

 
 http://d.puremagic.com/issues/show_bug.cgi?id=6741


Well, if the langage wants to be consistent, you should prevent implicit 
casting of delegates to const, not just to immutable.

What you revealed is very interesting, but it is clearly a bug. A 
delegate context pointer in a const object should be const, and the 
delegate function should be const with regard to its context pointer if 
you want the function to be called when the delegate is const. 
Constness has to apply to delegates just like it applies to structs. The 
explanation gets messy. An example:


This is what the compiler should say:
 class A {
  int n;
  void delegate( ) dg;
 }

 pure
 A createAnA( int n ) {
  A result = new A;
  result.n = n;
  result.dg = (){ result.n++; };
  return result;
 }

 void main( ) {
  immutable A tmp = createAnA( 3 );
  assert( tmp.n == 3 );
  tmp.dg();
^error: cannot call non const delegate A.dg from an immutable object.
  assert( tmp.n == 3 );
 }

An this is the fix:
 class A {
  int n;
  void delegate( ) const dg; // or const(void delegate()) dg;
 }

 pure
 A createAnA( int n ) {
  A result = new A;
  result.n = n;
  // result.dg = (){ result.n++; }; would result in:
  // error: non const (unamed) delegate cannot be assigned to 
  // const delegate A.dg.
  result.dg = (){ result.n+1; }; // this one is fine.
  return result;
 }

 void main( ) {
  immutable A tmp = createAnA( 3 );
  assert( tmp.n == 3 );
 tmp.dg(); // OK, A.dg is const so it can be called
  assert( tmp.n == 3 );
 }


I guess you could also hide a mutable pointer of an immutable object 
during its construction with the same trick.

-- 
Christophe


Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 09:27:31 -0400, Christophe  
trav...@phare.normalesup.org wrote:



Steven Schveighoffer , dans le message (digitalmars.D:145767), a
 écrit :

On Thu, 29 Sep 2011 13:26:11 -0400, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Thu, 29 Sep 2011 13:06:55 -0400, Simen Kjaeraas
simen.kja...@gmail.com wrote:


On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer
schvei...@yahoo.com wrote:


I just thought of an interesting way to make a logical const object
without casts.  It requires a little extra storage, but works without
changes to the current compiler (and requires no casts).

[snip]

What do people think about this?


This is what I think about it:



I agree this breaks immutability, and needs to be addressed.  I think
probably implicit casting of delegates (or items that containe
delegates) to immutable from strong-pure functions should be  
disallowed.


But it's not the pattern I described, and uses a relatively new trick
(implicit immutable casting).  I'll file a bug for this case.



http://d.puremagic.com/issues/show_bug.cgi?id=6741



Well, if the langage wants to be consistent, you should prevent implicit
casting of delegates to const, not just to immutable.

What you revealed is very interesting, but it is clearly a bug. A
delegate context pointer in a const object should be const, and the
delegate function should be const with regard to its context pointer if
you want the function to be called when the delegate is const.
Constness has to apply to delegates just like it applies to structs. The
explanation gets messy.


If const must be a part of the signature, then you have lost the typeless  
aspect of the context pointer.  If we expose the const (or not const)  
nature of the pointer, then you lose the interoperability that delegates  
provide.  The beauty of delegates is you don't *have to* care what the  
type of the context pointer is.  That's defined by the function the  
delegate represents.


It's why, for example, a function accepting a delegate does not  
distinguish between a delegate literal and a delegate to a member function  
of type Foo or a delegate to a member function of type const(Bar).  You  
must think of the context pointer as a hidden parameter to the delegate,  
as defined when the delegate was created *not* when it is called.  The  
fact that it's actually stored with the delegate pointer is irrelevant.   
Conceptually, it's not stored anywhere, it's just a parameter.


But in the case of the bug I filed, we are talking about a  
compiler-sanctioned implicit transformation to immutable.  We *must*  
guarantee that when we allow an implicit transformation, that there are no  
existing mutable copies of the data.  An explicit transformation should be  
allowed, because then the user has accepted responsibility.


The same is not true for const.  It's *perfectly legal* to have a const  
and mutable reference to an object at the same time.  The only reason  
transitive const exists and is necessary is to support transitive  
immtuable.


Const is never guaranteed to prevent changing data on an object:

class C
{
   int x;
   void multiply(C other) const {other.x *= x;}
}

Is multiply guaranteed not to change the object?  No:

c.multiply(c);

However, if c is immutable, it *is* guaranteed, because there's no way to  
pass c as the parameter to multiply.


That is why I think the solution works -- if you allow the compiler to  
enforce the rules of const and immutable, you can still do unexpected  
things, but you do not break the guarantees of immutability.  It's  
definitely a loophople, but I think it's valid.



I guess you could also hide a mutable pointer of an immutable object
during its construction with the same trick.


But saving a mutable object as immutable requires a cast.  It means  
compiler, I take responsibility for ensuring this no longer has any  
mutable references.  When the cast is *not* required, it's the compiler's  
responsibility.


-Steve


Re: logical const without casts!

2011-09-30 Thread Christophe
Steven Schveighoffer , dans le message (digitalmars.D:145821), a
 écrit :
 If const must be a part of the signature, then you have lost the typeless  
 aspect of the context pointer.  If we expose the const (or not const)  
 nature of the pointer, then you lose the interoperability that delegates  
 provide.  The beauty of delegates is you don't *have to* care what the  
 type of the context pointer is.  That's defined by the function the  
 delegate represents.

No, you don't lose the typeless aspect of the context pointer. You lose 
a litle bit of that aspect, but not that much.

In D, const is transitive. That means: everything you touch via a const 
object remains const. non-const delegate context pointer breaks that 
rule.

You have const member functions, and initially, delegates were member 
functions associated with an object, so why could not the delegate be 
const ? A const delegate would just be a delegate that promise it 
doesn't touch to its context. It's not a type-defined delegate.

If I implement a delegate at the library level, when I call the 
function, I have to make a cast to access the context pointer. With cast 
removing implicitely the constness of the context pointer, I get the 
current D implementation of delegates. This is legal, but this is also 
undefined behavior. I believe that langage delegate work that way, that 
they are thus undefined behavior, and this should be corrected.

I agree the langage could stipulate that delegate context pointer are 
exceptions, to allow the valuable trick like you revealed. But, to me, 
that is not the current policy about constness.


Do you see the problem in the following code:

class Foo
{
  this(ref int _i) { i = () { return _i; } }
  ref int delegate() dg;
  ref int i() const { return dg(); }
}

int globalInt;

immutable Foo globalFoo = Foo(globalInt);

int bar(const Foo foo)
{
return foo.i++;
}

int globalBar()
{
return bar(globalFoo);
}


Answer:

In multithreaded application, globalFoo, which is immutable, is 
automatically shared. However, globalInt is not. globalBar allows you to 
access to (and modify) globalInt. But globalInt is not protected for 
concurrent access. And globalInt from which thread is accessed ?

Possible solutions:

- do nothing, and let the programmer introducing multi-threading in 
the application deal with this, even if the programmers of Foo, bar and 
buggy did not cared to document the impact of their code for 
multithreaded applications.

- forbid to put/call any delegate into an immutable object: that almost 
means forbiding to put/call a delegate into a const object.

- what I propose: implement the separate kind of const delegates, that 
allows to protect their context pointers, and that you can safely call 
from const/immutable data.

 But in the case of the bug I filed, we are talking about a  
 compiler-sanctioned implicit transformation to immutable.  We *must*  
 guarantee that when we allow an implicit transformation, that there are no  
 existing mutable copies of the data.  An explicit transformation should be  
 allowed, because then the user has accepted responsibility.

With my proposal, you can very easily keep an immutable reference in a 
const delegate. The delegate will just not be callable if its 
function pointer is not const with regard to the context pointer.


In any case, if the langage decides that delegate context pointer should 
escape const protection, great care should be taken to make sure they 
don't escape purity. In my example, there should be a compiler error if 
I tried to declare Foo.i and bar pure (maybe there is already an error, 
I didn't test).

-- 
Christophe


Re: logical const without casts!

2011-09-30 Thread Marco Leise
Am 29.09.2011, 16:54 Uhr, schrieb Steven Schveighoffer  
schvei...@yahoo.com:


I just thought of an interesting way to make a logical const object  
without casts.  It requires a little extra storage, but works without  
changes to the current compiler (and requires no casts).


Here's the code, then I'll talk about the implications:

import std.stdio;

class D
{
 D getme() { return this;}
 void foo() {writeln(mutable!);}
}

class C
{
 D delegate() d;
 this()
 {
   auto dinst = new D;
   this.d = dinst.getme;
 }
 void bar() const { d().foo();}
}

void main()
{
 auto c = new C;
 c.bar();
}

[...]

What do people think about this?

-Steve


I think this is a creative use of delegates!



class Vendor {
void sell() {
++_sold;
}

@property
int sold() {
return _sold;
}

private:
int _sold = 1;
}

class Car {
this(Vendor vendor) {
_vendorDlg = () { return vendor; };
}

Vendor getVendor() const {
return _vendorDlg();
}

private:
Vendor delegate() _vendorDlg;
}

void main(string[] args) {
immutable Car car = new immutable(Car)(new Vendor());
Vendor vendor = car.getVendor();
vendor.sell();
}


Re: logical const without casts!

2011-09-30 Thread Michel Fortin
On 2011-09-29 14:54:24 +, Steven Schveighoffer 
schvei...@yahoo.com said:


I just thought of an interesting way to make a logical const object  
without casts.  It requires a little extra storage, but works without  
changes to the current compiler (and requires no casts).


Here's the code, then I'll talk about the implications:

import std.stdio;

class D
{
 D getme() { return this;}
 void foo() {writeln(mutable!);}
}

class C
{
 D delegate() d;
 this()
 {
   auto dinst = new D;
   this.d = dinst.getme;
 }
 void bar() const { d().foo();}
}

void main()
{
 auto c = new C;
 c.bar();
}

outputs:

mutable!

So how does it work?  It works because delegates and especially the  
delegate data is *not* affected by const.  So even when C is 
temporarily  cast to const, the delegate is not affected (i.e. it's 
context pointer is  not temporarily cast to const).


Doesn't this poke holes in const?  Of course it does, but no more holes 
 than are present via another logical const scheme (i.e. using a 
globally  stored AA to retrieve the data).


This is a hole in the transitive const, because the delegate contains a 
pointer to mutable data. It also is a potential source of of low level 
races since returning that type from a pure function could make it 
immutable, which can then make this mutable data accessible to multiple 
threads with no synchronization or atomics to protect the data's 
integrity.



I'm actually thinking that very controlled patterns of logical const 
like  this could be implemented via mixin, and be sanctioned by the 
library.   The way this pattern works, you can dictate as the author of 
a class  whether that class can be a logically const part of another 
object or not,  simply by choosing to implement getme or not.


Whatever the implementation I think this is deeply needed. It is needed 
because people are trying all sorts of things to work around const 
transitivity, many of which are subtly unsafe.



--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 12:16:03 -0400, Christophe  
trav...@phare.normalesup.org wrote:



Steven Schveighoffer , dans le message (digitalmars.D:145821), a
 écrit :
If const must be a part of the signature, then you have lost the  
typeless

aspect of the context pointer.  If we expose the const (or not const)
nature of the pointer, then you lose the interoperability that delegates
provide.  The beauty of delegates is you don't *have to* care what the
type of the context pointer is.  That's defined by the function the
delegate represents.


No, you don't lose the typeless aspect of the context pointer. You lose
a litle bit of that aspect, but not that much.

In D, const is transitive. That means: everything you touch via a const
object remains const. non-const delegate context pointer breaks that
rule.


But transitive const by itself doesn't get you any guarantees.  It does  
not prevent you from modifying that (mutable) value via another pointer.


What it does is allow code that works with both transitive immutable and  
mutable without having to copy code.



You have const member functions, and initially, delegates were member
functions associated with an object, so why could not the delegate be
const ? A const delegate would just be a delegate that promise it
doesn't touch to its context. It's not a type-defined delegate.


A delegate for a const member function today already does not touch its  
context.  There is no need to expose the type.



If I implement a delegate at the library level, when I call the
function, I have to make a cast to access the context pointer. With cast
removing implicitely the constness of the context pointer, I get the
current D implementation of delegates. This is legal, but this is also
undefined behavior. I believe that langage delegate work that way, that
they are thus undefined behavior, and this should be corrected.


As long as you assume the context pointer is part of the state of the  
aggregate, then yes.  But I don't see it that way.  The context pointer is  
part of the state of the delegate, and the delegate reference itself is  
the same as a function pointer -- it's not affected by immutable or const.



Do you see the problem in the following code:

class Foo
{
  this(ref int _i) { i = () { return _i; } }
  ref int delegate() dg;
  ref int i() const { return dg(); }
}

int globalInt;

immutable Foo globalFoo = Foo(globalInt);

int bar(const Foo foo)
{
return foo.i++;
}

int globalBar()
{
return bar(globalFoo);
}


Answer:

In multithreaded application, globalFoo, which is immutable, is
automatically shared. However, globalInt is not. globalBar allows you to
access to (and modify) globalInt. But globalInt is not protected for
concurrent access. And globalInt from which thread is accessed ?


Yes, I see the problem.  It comes from immutable being shared implicitly.   
To reiterate, the fact that the data is not immutable or const is *not* an  
issue, globalInt is not immutable.  The issue is purely the sharing aspect  
of immutable.



Possible solutions:

- do nothing, and let the programmer introducing multi-threading in
the application deal with this, even if the programmers of Foo, bar and
buggy did not cared to document the impact of their code for
multithreaded applications.

- forbid to put/call any delegate into an immutable object: that almost
means forbiding to put/call a delegate into a const object.

- what I propose: implement the separate kind of const delegates, that
allows to protect their context pointers, and that you can safely call
from const/immutable data.


This also doesn't work:

class Foo
{
   this(ref int _i) { i = () { return _i; } }
   ref const(int) delegate() const dg;
   ref const(int) i() const { return dg(); }
}

int globalInt;

immutable Foo globalFoo = Foo(globalInt);

void thread1()
{
   writeln(globalFoo.i);
}

int globalBar()
{
   spawn(thread1);
   globalInt = 5; // does thread1 see this change or not?
}

It looks like any time you call a delegate member of an immutable object,  
it could access a context pointer that is not implicitly shared.


Not to mention, what the hell does a const function mean for a delegate  
literal?  The context pointer is the context of the function, not an  
object.  How does that even work?


What is probably the right solution is to disallow implicit immutable  
objects which have a delegate.  This means:


a) you cannot initialize a shared or immutable such object without a  
cast.  This means the line immutable Foo globalFoo = new Foo(globalInt) is  
an error without a cast.
b) Any time you have a const object that contains a delegate, it can be  
assumed that the object is not shared.


And then we avoid dealing with the const delegate issue altogether.


But in the case of the bug I filed, we are talking about a
compiler-sanctioned implicit transformation to immutable.  We *must*
guarantee that when we allow an implicit transformation, that there are  
no
existing mutable 

Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 13:38:31 -0400, Michel Fortin  
michel.for...@michelf.com wrote:


On 2011-09-29 14:54:24 +, Steven Schveighoffer  
schvei...@yahoo.com said:


I just thought of an interesting way to make a logical const object   
without casts.  It requires a little extra storage, but works without   
changes to the current compiler (and requires no casts).

 Here's the code, then I'll talk about the implications:
 import std.stdio;
 class D
{
 D getme() { return this;}
 void foo() {writeln(mutable!);}
}
 class C
{
 D delegate() d;
 this()
 {
   auto dinst = new D;
   this.d = dinst.getme;
 }
 void bar() const { d().foo();}
}
 void main()
{
 auto c = new C;
 c.bar();
}
 outputs:
 mutable!
 So how does it work?  It works because delegates and especially the   
delegate data is *not* affected by const.  So even when C is  
temporarily  cast to const, the delegate is not affected (i.e. it's  
context pointer is  not temporarily cast to const).
 Doesn't this poke holes in const?  Of course it does, but no more  
holes  than are present via another logical const scheme (i.e. using a  
globally  stored AA to retrieve the data).


This is a hole in the transitive const, because the delegate contains a  
pointer to mutable data. It also is a potential source of of low level  
races since returning that type from a pure function could make it  
immutable, which can then make this mutable data accessible to multiple  
threads with no synchronization or atomics to protect the data's  
integrity.


Simen Kjaeraas and Christophe brought up the same points.  I filed a bug  
on it:


http://d.puremagic.com/issues/show_bug.cgi?id=6741

-Steve


Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 13:46:13 -0400, Steven Schveighoffer  
schvei...@yahoo.com wrote:


Simen Kjaeraas and Christophe brought up the same points.  I filed a bug  
on it:


http://d.puremagic.com/issues/show_bug.cgi?id=6741


Also now:

http://d.puremagic.com/issues/show_bug.cgi?id=6747

-Steve


Re: logical const without casts!

2011-09-30 Thread Michel Fortin
On 2011-09-30 17:46:13 +, Steven Schveighoffer 
schvei...@yahoo.com said:


On Fri, 30 Sep 2011 13:38:31 -0400, Michel Fortin  
michel.for...@michelf.com wrote:


On 2011-09-29 14:54:24 +, Steven Schveighoffer  
schvei...@yahoo.com said:


I just thought of an interesting way to make a logical const object   
without casts.  It requires a little extra storage, but works without   
changes to the current compiler (and requires no casts).

 Here's the code, then I'll talk about the implications:
 import std.stdio;
 class D
{
 D getme() { return this;}
 void foo() {writeln(mutable!);}
}
 class C
{
 D delegate() d;
 this()
 {
   auto dinst = new D;
   this.d = dinst.getme;
 }
 void bar() const { d().foo();}
}
 void main()
{
 auto c = new C;
 c.bar();
}
 outputs:
 mutable!
 So how does it work?  It works because delegates and especially the   
delegate data is *not* affected by const.  So even when C is  
temporarily  cast to const, the delegate is not affected (i.e. it's  
context pointer is  not temporarily cast to const).
 Doesn't this poke holes in const?  Of course it does, but no more  
holes  than are present via another logical const scheme (i.e. using a  
globally  stored AA to retrieve the data).


This is a hole in the transitive const, because the delegate contains a 
 pointer to mutable data. It also is a potential source of of low level 
 races since returning that type from a pure function could make it  
immutable, which can then make this mutable data accessible to multiple 
 threads with no synchronization or atomics to protect the data's  
integrity.


Simen Kjaeraas and Christophe brought up the same points.  I filed a 
bug  on it:


http://d.puremagic.com/issues/show_bug.cgi?id=6741


Interesting proposal. You realize if we had a true 'mutable' storage 
class for class members it could obey the same rule as you propose in 
that bug report: it should be illegal to cast a mutable-containing 
object or struct to immutable implicitly. An explicit cast should be 
required.


Also, if we had shared delegates -- delegates which are guarantied to 
only manipulate shared data -- we could allow the class that contains a 
shared delegate to be implicitly converted to immutable.


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 14:00:36 -0400, Michel Fortin  
michel.for...@michelf.com wrote:


On 2011-09-30 17:46:13 +, Steven Schveighoffer  
schvei...@yahoo.com said:


On Fri, 30 Sep 2011 13:38:31 -0400, Michel Fortin   
michel.for...@michelf.com wrote:


On 2011-09-29 14:54:24 +, Steven Schveighoffer   
schvei...@yahoo.com said:


I just thought of an interesting way to make a logical const object
without casts.  It requires a little extra storage, but works  
without   changes to the current compiler (and requires no casts).

 Here's the code, then I'll talk about the implications:
 import std.stdio;
 class D
{
 D getme() { return this;}
 void foo() {writeln(mutable!);}
}
 class C
{
 D delegate() d;
 this()
 {
   auto dinst = new D;
   this.d = dinst.getme;
 }
 void bar() const { d().foo();}
}
 void main()
{
 auto c = new C;
 c.bar();
}
 outputs:
 mutable!
 So how does it work?  It works because delegates and especially  
the   delegate data is *not* affected by const.  So even when C is   
temporarily  cast to const, the delegate is not affected (i.e. it's   
context pointer is  not temporarily cast to const).
 Doesn't this poke holes in const?  Of course it does, but no more   
holes  than are present via another logical const scheme (i.e. using  
a  globally  stored AA to retrieve the data).
 This is a hole in the transitive const, because the delegate contains  
a  pointer to mutable data. It also is a potential source of of low  
level  races since returning that type from a pure function could make  
it  immutable, which can then make this mutable data accessible to  
multiple  threads with no synchronization or atomics to protect the  
data's  integrity.
 Simen Kjaeraas and Christophe brought up the same points.  I filed a  
bug  on it:

 http://d.puremagic.com/issues/show_bug.cgi?id=6741


Interesting proposal. You realize if we had a true 'mutable' storage  
class for class members it could obey the same rule as you propose in  
that bug report: it should be illegal to cast a mutable-containing  
object or struct to immutable implicitly. An explicit cast should be  
required.


Yes, but the benefit of the proposal is -- it already works in the current  
compiler :)  You have a monstrous hill to climb to convince Walter to  
*add* mutable storage class, but we don't even have to ask for this one,  
it works today.


Also, if we had shared delegates -- delegates which are guarantied to  
only manipulate shared data -- we could allow the class that contains a  
shared delegate to be implicitly converted to immutable.


I'd like to avoid the complication of having any attributes that apply to  
the context pointer.  I like how delegates just fit in anywhere, no matter  
what the context pointer type is (or its constancy).


-Steve


Re: logical const without casts!

2011-09-30 Thread Michel Fortin
On 2011-09-30 18:07:54 +, Steven Schveighoffer 
schvei...@yahoo.com said:


On Fri, 30 Sep 2011 14:00:36 -0400, Michel Fortin  
michel.for...@michelf.com wrote:


Interesting proposal. You realize if we had a true 'mutable' storage  
class for class members it could obey the same rule as you propose in  
that bug report: it should be illegal to cast a mutable-containing  
object or struct to immutable implicitly. An explicit cast should be  
required.


Yes, but the benefit of the proposal is -- it already works in the 
current  compiler :)  You have a monstrous hill to climb to convince 
Walter to  *add* mutable storage class, but we don't even have to ask 
for this one,  it works today.


It works today but is unsafe due to the implicit cast. But yeah, lets 
use delegates for now.


After the implicit cast is fixed it might be the right time to point 
out to Walter that this exception for delegate is no worse than having 
a direct mutable keyword. Especially if everyone is using this delegate 
detour to achieve what would be simpler and more efficient with 
'mutable'.



Also, if we had shared delegates -- delegates which are guarantied to  
only manipulate shared data -- we could allow the class that contains a 
 shared delegate to be implicitly converted to immutable.


I'd like to avoid the complication of having any attributes that apply 
to  the context pointer.  I like how delegates just fit in anywhere, no 
matter  what the context pointer type is (or its constancy).


But that's no longer true. Delegates no longer fit anywhere with the 
fix you just proposed. Only 'shared' delegates would fit anywhere. I 
really means *anywhere* since implicitly casting a shared delegate to a 
non-shared delegates is a non-issue: it does not change the type of the 
variables the context is pointing to.


So basically you'd need to require a shared delegate only where a 
normal delegate cannot do the job (like in a class you want to make 
immutable, or wanting to pass the delegate across threads).


--
Michel Fortin
michel.for...@michelf.com
http://michelf.com/



Re: logical const without casts!

2011-09-30 Thread Christophe
Steven Schveighoffer , dans le message (digitalmars.D:145850), a
 As long as you assume the context pointer is part of the state of the  
 aggregate, then yes.  But I don't see it that way.  The context pointer is  
 part of the state of the delegate, and the delegate reference itself is  
 the same as a function pointer -- it's not affected by immutable or const.

That is actually where our opinions differ.
I wouldn't blame the langage for choosing you proposal rather than mine. 
I just personnaly think my proposal in more in the spirit of the 
langage, and offer more guarantees when dealing with immutables.

 This also doesn't work:
Indeed. The compiler should not allow the implicit conversion of 
globalFoo to immutable.

 class Foo
 {
 this(ref int _i) { dg = () { return _i; } }
 ref const(int) delegate() const dg;
 ref const(int) i() const { return dg(); }
 }
 
 int globalInt;
 
 immutable Foo globalFoo = Foo(globalInt);
^ error: can't cast ref int _i to immutable ref int in immutable 
Foo.this()._delegate_1.

 void thread1()
 {
 writeln(globalFoo.i);
 }
 
 int globalBar()
 {
 spawn(thread1);
 globalInt = 5; // does thread1 see this change or not?
 }

 It looks like any time you call a delegate member of an immutable object,  
 it could access a context pointer that is not implicitly shared.

Not if the compiler cares to check the delegate context is indeed 
immutable when the immutable delegate is created. It is not much more 
complicated than if Foo contained an explicit pointer to i instead of a 
delegate

 Not to mention, what the hell does a const function mean for a delegate  
 literal?  The context pointer is the context of the function, not an  
 object.  How does that even work?

Internally, it is a pointer to a structure containing the delegate 
context that is generated by the compiler. If the delegate is const, the 
pointer should be a const pointer. If the delegate is immutable, it 
should be an immutable pointer, and the compiler as to check there is no 
mutable version of that pointer before making an implicit cast.

 What is probably the right solution is to disallow implicit immutable  
 objects which have a delegate.  This means:
 
 a) you cannot initialize a shared or immutable such object without a  
 cast.  This means the line immutable Foo globalFoo = new Foo(globalInt) is  
 an error without a cast.

Agreed, but with my proposal, you can have an immutable Foo, if the 
delegate context pointer can be cast to immutable.

 b) Any time you have a const object that contains a delegate, it can be  
 assumed that the object is not shared.

If the object is const, you have no guarantee that the objet is not 
shared, making an exception for object containing a delegate is quite 
wierd.

 And then we avoid dealing with the const delegate issue altogether.

Dealing with this issue could be very useful for multithreaded 
applications.

 With my proposal, you can very easily keep an immutable reference in a
 const delegate. The delegate will just not be callable if its
 function pointer is not const with regard to the context pointer.
 
 You can also very easily keep a mutable reference in a const delegate  
 inside an immutable object.  It's not mutable through the delegate, but  
 it's also not immutable.

No, the compiler should check the delegate context is immutable when 
making the cast to immutable.

I admit you have found a very interesting corner-case for my proposal 
that I had not thought of that case. Thank you for that. I think the 
proposal passed the test (until now).




Re: logical const without casts!

2011-09-30 Thread Steven Schveighoffer
On Fri, 30 Sep 2011 14:51:19 -0400, Christophe  
trav...@phare.normalesup.org wrote:



Steven Schveighoffer , dans le message (digitalmars.D:145850), a



This also doesn't work:

Indeed. The compiler should not allow the implicit conversion of
globalFoo to immutable.


class Foo
{
this(ref int _i) { dg = () { return _i; } }
ref const(int) delegate() const dg;
ref const(int) i() const { return dg(); }
}

int globalInt;

immutable Foo globalFoo = Foo(globalInt);

^ error: can't cast ref int _i to immutable ref int in immutable
Foo.this()._delegate_1.


But ref int _i is the parameter to the constructor.  Here is where your  
proposal breaks down.


It's entirely feasible for class Foo to be compiled *separately* from the  
module that contains globalFoo.  And it's also possible that when  
compiling the above line, the language *does not have* access to the  
source code of the constructor.  How does it know that _i gets put into a  
const delegate?


Likewise, when compiling Foo, how does the compiler know that the  
constructor is being used to cast to immutable?  I just don't think there  
is enough information for the compiler to make the correct decision.





void thread1()
{
writeln(globalFoo.i);
}

int globalBar()
{
spawn(thread1);
globalInt = 5; // does thread1 see this change or not?
}


It looks like any time you call a delegate member of an immutable  
object,

it could access a context pointer that is not implicitly shared.


Not if the compiler cares to check the delegate context is indeed
immutable when the immutable delegate is created. It is not much more
complicated than if Foo contained an explicit pointer to i instead of a
delegate


What if it has no access to the delegate creation code?  How does it  
disallow it?



Not to mention, what the hell does a const function mean for a delegate
literal?  The context pointer is the context of the function, not an
object.  How does that even work?


Internally, it is a pointer to a structure containing the delegate
context that is generated by the compiler. If the delegate is const, the
pointer should be a const pointer. If the delegate is immutable, it
should be an immutable pointer, and the compiler as to check there is no
mutable version of that pointer before making an implicit cast.


Hm.. I don't know if there's a way to create such a delegate.  I'd have to  
check.



What is probably the right solution is to disallow implicit immutable
objects which have a delegate.  This means:

a) you cannot initialize a shared or immutable such object without a
cast.  This means the line immutable Foo globalFoo = new Foo(globalInt)  
is

an error without a cast.


Agreed, but with my proposal, you can have an immutable Foo, if the
delegate context pointer can be cast to immutable.


You can also have an immutable Foo if you cast it to immutable.  For  
example, this line should compile:


immutable foo = cast(immutable(Foo))new Foo(globalInt);

But then the onus is on you to ensure you are doing the right thing.  So  
in both solutions, it's possible.  While I don't think your solution is  
feasible or solves the problem you set out to solve, if you did find a way  
to make the compiler make more guarantees, it would be better.  I just  
don't know if it's worth the extra syntax and restrictions.



b) Any time you have a const object that contains a delegate, it can be
assumed that the object is not shared.


If the object is const, you have no guarantee that the objet is not
shared, making an exception for object containing a delegate is quite
wierd.


Well, since you cannot implicitly create such an immutable object, it  
could be an assumption you can make.  However, I can see why it wouldn't  
be a good idea to make that assumption (especially if you created one via  
casting).


We can probably drop this assumption, because to write code like the  
above, even if explicitly casting, you are asking for race conditions.   
Probably safe to assume that it works just like any other const object.





And then we avoid dealing with the const delegate issue altogether.


Dealing with this issue could be very useful for multithreaded
applications.


With my proposal, you can very easily keep an immutable reference in a
const delegate. The delegate will just not be callable if its
function pointer is not const with regard to the context pointer.


You can also very easily keep a mutable reference in a const delegate
inside an immutable object.  It's not mutable through the delegate, but
it's also not immutable.


No, the compiler should check the delegate context is immutable when
making the cast to immutable.


As I said above, it's not always possible to check the context.

-Steve


Re: logical const without casts!

2011-09-30 Thread Christophe
Steven Schveighoffer , dans le message (digitalmars.D:145867), a
 écrit :
 But ref int _i is the parameter to the constructor.  Here is where your  
 proposal breaks down.
 
 It's entirely feasible for class Foo to be compiled *separately* from the  
 module that contains globalFoo.  And it's also possible that when  
 compiling the above line, the language *does not have* access to the  
 source code of the constructor.  How does it know that _i gets put into a  
 const delegate?
 
 Likewise, when compiling Foo, how does the compiler know that the  
 constructor is being used to cast to immutable?  I just don't think there  
 is enough information for the compiler to make the correct decision.

You say it is impossible for the compiler to check the constness of the 
arguments passed to the constructor ?
Well, allow me to doubt your statement, because my compiler can (it is 
gdc 4.4.5 using dmd 2.052).

Easy test, with a pointer instead of a delegate:

struct Foo
{
  int* _i;
  ref int i() { return *_i; }
  this(int* m_i)
  { _i = m_i;  }
  this(const int* m_i) const
  { _i = m_i; }
  this(immutable(int)* m_i) immutable // change immutable attribute of
// the constructor and main won't compile.
  { _i = m_i; }
}

void main()
{
  int ma;
  Foo mfoo = Foo(ma);
  const int ca;
  const Foo cfoo = Foo(ca);
  immutable int ia;
  immutable Foo ifoo = Foo(ia);
}

Now try to remove the const or the immutable attributes in the different 
this(int*) overload. You can play and try many changes, if you find a 
way to put an immutable int inside a mutable Foo, you get a banana for 
having found a great hole in the langage (or at least in the compiler).

Protect can (and has to!) be assured for a simple pointer.
Why would it not be the case with a delegate context pointer ?

Now the full test with delegate:

module a;

struct B
{
  int* _b;

  this(int* m_b) { _b = m_b; }
  this(const(int)* m_b) const { _b = m_b; }
  this(immutable(int)* m_b) immutable { _b = m_b; }

  int* b() { return _b; }
  const(int)* b() const { return _b; }
  immutable(int)* b() immutable { return _b; }
}

struct Foo
{
  int* delegate() _i;
  ref int i() { return *_i(); }
  this(int* m_i)
  {
B b = B(m_i);
_i = b.b;
  }
  this(const int* m_i) const
  {
const B b = B(m_i);
_i = b.b;
  }
  this(immutable int* m_i) immutable
  {
immutable B b = B(m_i);
_i = b.b;
// try to uncomment the following line:
//_i = () { return m_i; };
  }
}


module test;
import a;

void main()
{
  int ma;
  Foo mfoo = Foo(ma);

  const int ca;
  const Foo cfoo = Foo(ca);

  immutable int ia;
  immutable Foo ifoo = Foo(ia);
}


To know why I created the struct B, try to uncomment the 
line I indicated in a.d and compile. What do you discover ?

a.d:34: Error: cannot implicitly convert expression (__dgliteral1) of 
type immutable(int*) delegate() to immutable(int* delegate())

So the type I thought I was proposing, the const or immutable delegate 
that protects its context pointer already exists in my compiler. The 
only problem is that at the moment, you have to use an helper structure 
to create this type of delegate...

 What if it has no access to the delegate creation code?  How does it  
 disallow it?

The compiler cannot cast a newly constructed structure to immutable if 
it cannot access the creation code, unless the creation code is marked 
immutable (and then there is actually no cast to do).


Me thinking I could improve the langage... Well, the conversation was 
interesting and I learned a lot about d today.

-- 
Christophe


Re: logical const without casts!

2011-09-29 Thread Simen Kjaeraas
On Thu, 29 Sep 2011 16:54:24 +0200, Steven Schveighoffer  
schvei...@yahoo.com wrote:


I just thought of an interesting way to make a logical const object  
without casts.  It requires a little extra storage, but works without  
changes to the current compiler (and requires no casts).

[snip]

What do people think about this?


This is what I think about it:

class A {
int n;
void delegate( ) dg;
}

pure
A createAnA( int n ) {
A result = new A;
result.n = n;
result.dg = (){ result.n++; };
return result;
}

void main( ) {
immutable A tmp = createAnA( 3 );
assert( tmp.n == 3 );
tmp.dg();
assert( tmp.n == 3 );
}

--
  Simen


Re: Logical const

2010-12-07 Thread Bruno Medeiros

On 03/12/2010 18:23, Steven Schveighoffer wrote:

On Fri, 03 Dec 2010 11:23:48 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


On 03/12/2010 13:22, Steven Schveighoffer wrote:
 I actually re-read the code and realized that it should work without
any
 changes (sans the issue you bring up below with implicit sharing of
 immutable).


You mean if you wanted to pass a mutable object back and forth?
No it wouldn't, if I understood you correctly. It would merely
compile, but not work (in the general case). So you would create an
object, cast it to shared (which means access would now need to be
synchronized), and pass it to another thread, right? However when you
pass to another thread the TLS part of the object state is lost (aka
the _mutable part). That might be valid for cached data that can be
recalculated (like the determinant), but it would not be valid for
other kinds of mutable data that the object would require and should
not be cleared (like the parent Widget in the other example you gave).


the mutable member is not marked as shared. It cannot be accessed on a
shared instance. I guess it should be explicitly noted that a logical
const notation (such as 'mutable') would not affect shared status, only
const status. shared does not implicitly cast to unshared or unshared
const.

-Steve


Oh, I see what you mean. I thought something like this (or similar) worked:

   shared X foo = new shared(X);
   synchronized(foo) {
 foo.mutable(2);
   }

I was thinking that the synchronized block would remove the shared type 
qualifier from foo inside the block.



--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-07 Thread Bruno Medeiros

On 03/12/2010 23:09, Fawzi Mohamed wrote:


On 3-dic-10, at 17:23, Bruno Medeiros wrote:


On 03/12/2010 13:22, Steven Schveighoffer wrote:

On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


The above are not trivial differences, so I do not agree that it
constitutes full logical const, only a limited form of it. More
concretely, it doesn't constitute logical const in in the sense where
you can use that as argument to say logical const already exists,
it's just clunky to use, so let's add it to the language formally.
Like if mutable members where just syntax sugar, or a betterment of
safety rules.


I disagree, I think it does prove logical const already exists. How do
you define logical const?



I define logical const as the ability to specify that operations on a
given object reference will not modify the logical state of that
object (through that reference), and the ability for the compiler to
verify that statically.


No for me the compiler *cannot* verify logical const. Logical const can
be verified only in some occasions:
for example a place where to store the result of a suspended evaluation
(what functional languages call a thunk).
A dataflow variable for example in general cannot be verified by the
compiler, but should also be logically const.


If you have a different notion of what logical state is, then yeah, 
could be that the compiler cannot verify it. But considering what I 
meant with logical state, the compiler can verify it. Let me go back and 
restate what I meant by logical const:


For any type, the programmer can define a subset of that type's data 
that composes the logical state. He will do that using annotations on 
the type's members for example.  Then logical const is being able to 
annotate function parameters (or any variable) to indicate that the 
function will not change the logical state (aka, the previously 
defined data subset) of that argument, and have the compiler verify this.


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Steven Schveighoffer
On Thu, 02 Dec 2010 20:38:01 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:



On 29/11/2010 14:56, Steven Schveighoffer wrote:
This has been discussed at length on this newsgroup, and I argued for  
it

for a long time.  You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ?  
That doesn't sound right to me.


 Here is where I show how logical const already exists, it's just  
clunky to use.
 BTW, this was before TLS, so the example would have to be updated a  
bit.
  
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927


What you're doing is keeping an alternate, mutable reference to each  
object. This does not mean that logical const == const.


No I'm not.  I'm keeping a portion of the object in a global AA.  I'm not  
storing a mutable reference to the object itself.


When you call a const function, *no data* that is defined within the data  
of the object is modified.  It is true logical const, not a hack (in  
contrast, the example I gave in this thread is a hack).


-Steve


Re: Logical const

2010-12-03 Thread Bruno Medeiros

On 02/12/2010 21:04, Steven Schveighoffer wrote:

On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


On 29/11/2010 14:56, Steven Schveighoffer wrote:

This has been discussed at length on this newsgroup, and I argued for it
for a long time. You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ?
That doesn't sound right to me.



Here is where I show how logical const already exists, it's just clunky
to use.

BTW, this was before TLS, so the example would have to be updated a bit.

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927


-Steve


Ok. Well, for starters the const functions that mutate the object 
state cannot be pure. (if you manage to mutate it without casts in a 
pure function, it's because of a compiler bug)
Second, there's the TLS thing. I don't think you can just update it a 
bit, there would be significant changes, maybe not in code size, but in 
runtime effect: You would need to make it global shared, and thus have 
to synchronize the access to the _mutable global. This is quite significant.


The above are not trivial differences, so I do not agree that it 
constitutes full logical const, only a limited form of it. More 
concretely, it doesn't constitute logical const in in the sense where 
you can use that as argument to say logical const already exists, it's 
just clunky to use, so let's add it to the language formally. Like if 
mutable members where just syntax sugar, or a betterment of safety rules.



There is one thing however that doesn't feel right in all of this, with 
regards to passing immutable objects to other threads. But I think I 
need to read TDPL concurrency chapter to clarify some things first of all.


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Bruno Medeiros

On 03/12/2010 01:38, Walter Bright wrote:

Steven Schveighoffer wrote:

On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


On 29/11/2010 14:56, Steven Schveighoffer wrote:

This has been discussed at length on this newsgroup, and I argued
for it
for a long time. You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ?
That doesn't sound right to me.



Here is where I show how logical const already exists, it's just
clunky to use.

BTW, this was before TLS, so the example would have to be updated a bit.

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927



What you're doing is keeping an alternate, mutable reference to each
object. This does not mean that logical const == const.


The statement logical const == const is meaningless really. Please use 
better terms people. ~_~'


What Steven was trying to say, I think, is that you can always emulate 
the behavior of logical const in D in a valid (safe) way, and that 
therefore the current D const system doesn't actually offer more 
guarantees than having logical const (by this I mean having mutable 
members).


Whether this is true or not, that's the question. I don't think it is 
true, I've argued that in a reply to the previous post. Note that the 
other extreme, which you mentioned: Having mutable members destroys any 
guarantees that const provides. , is also not true. (again, argued in 
another post).


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Steven Schveighoffer
On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:



On 02/12/2010 21:04, Steven Schveighoffer wrote:

On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


On 29/11/2010 14:56, Steven Schveighoffer wrote:
This has been discussed at length on this newsgroup, and I argued for  
it

for a long time. You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ?
That doesn't sound right to me.



Here is where I show how logical const already exists, it's just clunky
to use.

BTW, this was before TLS, so the example would have to be updated a bit.

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927


-Steve


Ok. Well, for starters the const functions that mutate the object  
state cannot be pure. (if you manage to mutate it without casts in a  
pure function, it's because of a compiler bug)


Yes, pure requires special handling.  I believe that mutable-marked  
members of an object should not be considered part of the object, just  
related to the object.  So it would make sense to disallow access to those  
members in pure functions when the objects are const or immutable.  It  
would at least make the semantics the same as logical const is defined  
here.  When the object itself is not const or immutable, I think possibly  
access to the mutable members should be allowed, but I haven't worked  
through the details in my head yet.


Second, there's the TLS thing. I don't think you can just update it a  
bit, there would be significant changes, maybe not in code size, but in  
runtime effect: You would need to make it global shared, and thus have  
to synchronize the access to the _mutable global. This is quite  
significant.


I actually re-read the code and realized that it should work without any  
changes (sans the issue you bring up below with implicit sharing of  
immutable).


The above are not trivial differences, so I do not agree that it  
constitutes full logical const, only a limited form of it. More  
concretely, it doesn't constitute logical const in in the sense where  
you can use that as argument to say logical const already exists, it's  
just clunky to use, so let's add it to the language formally. Like if  
mutable members where just syntax sugar, or a betterment of safety rules.


I disagree, I think it does prove logical const already exists.  How do  
you define logical const?


There is one thing however that doesn't feel right in all of this, with  
regards to passing immutable objects to other threads. But I think I  
need to read TDPL concurrency chapter to clarify some things first of  
all.


Implicit sharing of immutable is definitely an issue to consider.  If an  
object is immutable, then the mutable data is also implicitly sharable  
since the mutable data rides around with the object.  I don't have a good  
answer for this yet, but I don't think it's a deal-killer.  We may need to  
adjust the rules regarding sharing immutable data (i.e. you can't  
implicitly share immutable data, but you can cast it to shared to share  
it).


I thought implicit unshared felt completely wrong when it was first  
introduced, now I think it's completely correct.


-Steve


Re: Logical const

2010-12-03 Thread Steven Schveighoffer
On Fri, 03 Dec 2010 08:22:01 -0500, Steven Schveighoffer  
schvei...@yahoo.com wrote:


On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:


The above are not trivial differences, so I do not agree that it  
constitutes full logical const, only a limited form of it. More  
concretely, it doesn't constitute logical const in in the sense where  
you can use that as argument to say logical const already exists, it's  
just clunky to use, so let's add it to the language formally. Like if  
mutable members where just syntax sugar, or a betterment of safety  
rules.


I disagree, I think it does prove logical const already exists.  How do  
you define logical const?


I'll add to this that synchronization issues can be handled.  They should  
not play a role in 'does logical const exist', they should only play a  
role in 'does efficient logical const exist'.


-Steve


Re: Logical const

2010-12-03 Thread Bruno Medeiros

On 03/12/2010 13:00, Bruno Medeiros wrote:

(if you manage to mutate it without casts in a pure function, it's
because of a compiler bug)


Fresh from this discussion:
http://d.puremagic.com/issues/show_bug.cgi?id=5311

--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Bruno Medeiros

On 03/12/2010 13:22, Steven Schveighoffer wrote:

On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


The above are not trivial differences, so I do not agree that it
constitutes full logical const, only a limited form of it. More
concretely, it doesn't constitute logical const in in the sense where
you can use that as argument to say logical const already exists,
it's just clunky to use, so let's add it to the language formally.
Like if mutable members where just syntax sugar, or a betterment of
safety rules.


I disagree, I think it does prove logical const already exists. How do
you define logical const?



I define logical const as the ability to specify that operations on a 
given object reference will not modify the logical state of that object 
(through that reference), and the ability for the compiler to verify 
that statically. Logical state is defined (_not very precisely though_) 
as the data subset of an object which is relevant for opEquals 
calculations. So in that Matrix example the elements of the Matrix 
arrays are part of the logical state, the cached determinant is not.


Mutable members is one way to implement support for logical const in a 
language. (There could be other ways.)



 Second, there's the TLS thing. I don't think you can just update it a
 bit, there would be significant changes, maybe not in code size, but
 in runtime effect: You would need to make it global shared, and thus
 have to synchronize the access to the _mutable global. This is quite
 significant.

 I actually re-read the code and realized that it should work without any
 changes (sans the issue you bring up below with implicit sharing of
 immutable).


You mean if you wanted to pass a mutable object back and forth?
No it wouldn't, if I understood you correctly. It would merely compile, 
but not work (in the general case). So you would create an object, cast 
it to shared (which means access would now need to be synchronized), and 
pass it to another thread, right? However when you pass to another 
thread the TLS part of the object state is lost (aka the _mutable part). 
That might be valid for cached data that can be recalculated (like the 
determinant), but it would not be valid for other kinds of mutable data 
that the object would require and should not be cleared (like the parent 
Widget in the other example you gave).



--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Bruno Medeiros

On 03/12/2010 14:03, Steven Schveighoffer wrote:

On Fri, 03 Dec 2010 08:22:01 -0500, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:



The above are not trivial differences, so I do not agree that it
constitutes full logical const, only a limited form of it. More
concretely, it doesn't constitute logical const in in the sense where
you can use that as argument to say logical const already exists,
it's just clunky to use, so let's add it to the language formally.
Like if mutable members where just syntax sugar, or a betterment of
safety rules.


I disagree, I think it does prove logical const already exists. How do
you define logical const?


I'll add to this that synchronization issues can be handled. They should
not play a role in 'does logical const exist', they should only play a
role in 'does efficient logical const exist'.

-Steve


If by does efficient logical const exist you mean that we can devise 
some language rules/changes to make logical const work in D in a safe 
way, without losing the safety (and performance) guarantees that we have 
with current D with regards to immutability and concurrency, then yes, I 
think we can devise such a system. I definitely don't agree with Walter 
that Having mutable members destroys any guarantees that const 
provides.  (unless we were to do it exactly like C++, which obviously 
we wouldn't)



Whether it is desirable or not to implement such rules in D anytime soon 
(if at all), well... that's another question altogether...


--
Bruno Medeiros - Software Engineer


On const and inout (was Re: Logical const)

2010-12-03 Thread Bruno Medeiros

On 02/12/2010 09:18, Don wrote:
 Walter Bright wrote:
 spir wrote:
 What would be the consequences if D had no const, only immutable
 (that, IIUC, removes the latter non-guarantee)?

 You'd have to write most every function twice, once to take immutable
 args and again for mutable ones.

 Doesn't 'inout' do almost the same thing?
 The only difference I can see between const and inout, is that inout
 tells which parameters could be aliased with the return value.

Yes, that is more or less it, although it may not the best way to think 
about it.


I've come to realize an interesting parallel between inout and Java 
wildcards. In fact, that might give inspiration for another way to 
explain inout and const. Let's go back a little, to think about const 
itself.


Ask yourselves this, does it makes sense to *instantiate* data typed as 
const, such as this:

auto blah = new const(int[4]);
?





The answer is no (even though the compiler allows it). The resulting 
data would effectively be the same as immutable, so you might as well 
instantiate it as immutable. The key thing here is that const(int), and 
any other const(T), is actually kinda like an abstract type. It tells 
you some of the things you can do with the data, but the data itself 
must be of a concrete type, which is mutable or immutable, but not 
const. It is entirely accurate to think of const(T) as some T that can 
be mutable or immutable, but one don't know which, so one can only work 
on the lowest common assumptions. So const is like a wildcard.


Why is this interesting? Because it helps us understand what inout does. 
(if some people have trouble understanding and/or explaining const, then 
inout will be much worse). inout is based on const, it also says I 
don't know if this data is mutable or not. However, it adds the 
guarantee/restriction that all data typed as inout in the function 
parameters and return type actually has the same *concrete* type, with 
regards to immutability. So if you have this function:


inout(int)[] func(inout(int)[] arr, int x, int y) {
return arr[x .. y];
}

You are specifying: I don't know what arr's actual/real/runtime 
immutability is (so I'll treat is an unknown), but I guarantee that the 
actual/real/runtime immutability of the return type will be that same as 
that of arr's.


This pretty much the same conceptually as creating a binding of a 
wildcard type in Java (I'm not sure these are the correct terms). Java's 
wildcard support is much more... err.. of a generic solution (I mean 
generic as in http://www.answers.com/generic, not as in related to 
generics). For example, in Java you can specify a function signature 
such as this:


public static T T get(T obj) { //...

Which guarantees that the static return type is the same as the static 
type of the argument. So for example this will compile:

  String foo = get(new String());
  Number foo = get(new Integer());
but this will not:
  Number foo = get(new String());

Conceptually this is pretty much the same as with inout in D, where 
inout is only able to bind to one type modifier, and it does so 
anonymously.
Also D's support for this is only with regards to immutability, 
otherwise D is not able to define _one_ function with a 
signature/contract like the above.



--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-03 Thread Steven Schveighoffer
On Fri, 03 Dec 2010 11:23:48 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:



On 03/12/2010 13:22, Steven Schveighoffer wrote:
  I actually re-read the code and realized that it should work without  
any

  changes (sans the issue you bring up below with implicit sharing of
  immutable).
 

You mean if you wanted to pass a mutable object back and forth?
No it wouldn't, if I understood you correctly. It would merely compile,  
but not work (in the general case). So you would create an object, cast  
it to shared (which means access would now need to be synchronized), and  
pass it to another thread, right? However when you pass to another  
thread the TLS part of the object state is lost (aka the _mutable part).  
That might be valid for cached data that can be recalculated (like the  
determinant), but it would not be valid for other kinds of mutable data  
that the object would require and should not be cleared (like the parent  
Widget in the other example you gave).


the mutable member is not marked as shared.  It cannot be accessed on a  
shared instance.  I guess it should be explicitly noted that a logical  
const notation (such as 'mutable') would not affect shared status, only  
const status.  shared does not implicitly cast to unshared or unshared  
const.


-Steve


Re: Logical const

2010-12-03 Thread Fawzi Mohamed


On 3-dic-10, at 17:23, Bruno Medeiros wrote:


On 03/12/2010 13:22, Steven Schveighoffer wrote:

On Fri, 03 Dec 2010 08:00:43 -0500, Bruno Medeiros
brunodomedeiros+s...@com.gmail wrote:


The above are not trivial differences, so I do not agree that it
constitutes full logical const, only a limited form of it. More
concretely, it doesn't constitute logical const in in the sense  
where

you can use that as argument to say logical const already exists,
it's just clunky to use, so let's add it to the language formally.
Like if mutable members where just syntax sugar, or a betterment of
safety rules.


I disagree, I think it does prove logical const already exists. How  
do

you define logical const?



I define logical const as the ability to specify that operations on  
a given object reference will not modify the logical state of that  
object (through that reference), and the ability for the compiler to  
verify that statically.


No for me the compiler *cannot* verify logical const. Logical const  
can be verified only in some occasions:
for example a place where to store the result of a suspended  
evaluation (what functional languages call a thunk).
A dataflow variable for example in general cannot be verified by the  
compiler, but should also be logically const.
Normally the user should use only suspended pure operations or  
dataflow variables, so it is safe.
Still in D I want to be able to *implement* them, and that  cannot be  
verified by the compiler.
As for why I want to be able to implement it in D, the reasons (beyond  
the fact that it is a system language) is that in some occasions one  
can implement it much more efficiently that any generic implementation  
(for example if one knows that the value cannot be x, x can be  
directly used to mark the need to update it, and if one knows the  
functions and arguments to call an explicit thunk (i.e. closure with  
heap allocation) can also be spared.


So for me it is ok that the hole to implement them is ugly (haskell  
for example has unsafePerformIO), but I want an officially sanctioned  
hole.
I am actually against using mutable, it that solution should be  
accepeted then the name should look much worse, like unsafeValue or  
something like that.

Casual use should be discouraged.

Logical state is defined (_not very precisely though_) as the data  
subset of an object which is relevant for opEquals calculations. So  
in that Matrix example the elements of the Matrix arrays are part of  
the logical state, the cached determinant is not.


No for me logical const means that all methods applied to the object  
will always return the same value, it is not connected with the data  
stored, that is exactly the reason one wants mutable values.


Mutable members is one way to implement support for logical const in  
a language. (There could be other ways.)


yes

In any case I find tail const (or weak const, as I prefer to call it,  
which is simply const on referred data, but not on the one that is  
always locally stored on the stack) more important, and *that* can be  
enforced by the compiler.

I think that in should mean the weak const, not const.

Fawzi


Re: Logical const

2010-12-02 Thread Don

Walter Bright wrote:

spir wrote:
What would be the consequences if D had no const, only immutable 
(that, IIUC, removes the latter non-guarantee)?


You'd have to write most every function twice, once to take immutable 
args and again for mutable ones.


Doesn't 'inout' do almost the same thing?
The only difference I can see between const and inout, is that inout 
tells which parameters could be aliased with the return value.


Re: Logical const

2010-12-02 Thread Fawzi Mohamed


On 1-dic-10, at 04:52, Jesse Phillips wrote:


Fawzi Mohamed Wrote:


The thing is that a lazy structure is very useful in functional
programming.
A lazy evaluation is something that should be possible using pure and
immutable.
I find it jarring that to do that one has to avoid D pure and  
immutable.


Don't know what you mean by this.


a lazy list (for example one that list all natural numbers) cannot be  
immutable, without the possibility of a backdoor because all the  
next elements will have to be set at creation time.
(Lazy structures can be seen as memoizing a value produced by a pure  
function forever (i.e. never forgetting it).



To be able to safely use pure and immutable as I said one would need
some idioms that are guaranteed to be non optimized by the compiler.
for example casting a heap allocated type should be guaranteed to
remain modifiable behind the back:
auto t=new T;
auto t2=cast(immutable(typeof(t)))t;

auto tModif=cast(typeof(t))t2; // the compiler has not moved or
flagged the memory of t, so one can modify tModif.


This code is valid, the requirements placed on cast will not allow  
it to move the data. Even types declared to be immutable my be  
modifiable when cast to Unqual!(T), but the compiler can not  
guarantee these.


If I am wrong, please let me know why.


The code works now, I would like some assurance that  
cast(immutable(T)) doesn't do fancy stuff (or some equivalent way to  
ensure that *some* idiom will remain allowed.
If you think about it already now with opCast you cannot really know  
what opCast does, so a compiler would be allowed to return an  
immutable copy, or (if it uses whole pages) make the whole memory as  
read only.



clearly this is unsafe and it is up to the implementer to make sure
that the object is really logically const
and no function will see the internal changes.


Yes, and I don't think compiler support adds any more guarantee than  
casting those you want to modify in a const function. This Mutable  
struct is supposed to help verify only modifiable data is cast:


https://gist.github.com/721066


you example has an error in parallel, this is a good example of why  
the casting away should not be made convenient, and a user should just  
use well tested library code (for example dong thunk evaluation), that  
might be in a special type or mixed in.


You cannot have separate flag, and value without any synchronization,  
as other threads could see their value in a different order, so they  
could see dirty=false, but still no determinant.


This is somehow related to dataflow variables that can be set several  
times, but only to the same value (and indeed with a lazy list one can  
allow two threads to calculate the next element, but then only one  
should set it (using atomic ops to avoid collistions).
I have implemented DataFlow variables (but using the blip  
paralelization, that delays a task that waits, and resumes it when the  
value is ready, not with a thread lock) in


https://github.com/fawzi/blip/blob/master/blip/parallel/smp/DataFlowVar.d
using D1


I've taken many example use-cases for logical const and added them  
as unittests. I think it is fairly reasonable if I could just get an  
answer to my question about concurrency and declaring immutable types.



This is something that should be done sparingly, probably just in
library code implementing lazy evaluation or memoization (but code
that might be mixed in).


Could you give an example of how lazy evaluation is achieved by  
modifying state?


lazy structures need to modify the state (as I showed with the linked  
list example), lazy evaluation alone does not need to modify the state  
(and is indeed possible in D), but the storing/memoizing of the result  
needs it. To say the truth in D memoizing can be done with a static  
variable, but think about making the singly linked list like that, and  
you should immediately see that if gets *very* inefficient.


Re: Logical const

2010-12-02 Thread Fawzi Mohamed


On 1-dic-10, at 22:18, Steven Schveighoffer wrote:


On Wed, 01 Dec 2010 11:49:36 -0500, so s...@so.do wrote:


On Wed, 01 Dec 2010 18:38:23 +0200, so s...@so.do wrote:

Since i called it a bad design, i am entitled to introduce a  
better design.


interface renderer {
void draw(rect rects, size_t n);
}

class widget {
void draw(renderer r) { ... }
}


Pfft sorry for that abomination!

interface renderer {
void draw(rect[] rects);
}

class widget {
rect r;
window owner;
void draw(renderer) const { ... }
}


This requires you to store the widget-renderer relationship outside  
the widget.  Since each widget has exactly one location where it  
lives, this is awkward.  Much better to just store the relationship  
on each widget.


indeed that is one of the main things that I want from logical const:  
being able to store/memoize values in a structure, not outside it.
It is ok to have to jump some hoops to get it, but it should be  
possible (casting that is guaranteed to work in some clearly defined  
circumstances would do it for example).


Fawzi



Re: Logical const

2010-12-02 Thread Bruno Medeiros

On 01/12/2010 21:09, Steven Schveighoffer wrote:

On Tue, 30 Nov 2010 16:53:14 -0500, Walter Bright
newshou...@digitalmars.com wrote:


Steven Schveighoffer wrote:

If you find the above unsurprising, you are in the minority. I find
it surprising, and invalid that anyone would write code this way.
People simply just don't do that normally. It's just written to
demonstrate a point that the compiler does not guarantee anything via
const, it's guaranteed by convention. The compiler simply helps you
follow the convention.


Ok, I see what you mean now. Your code is relying on there being a
mutable alias of the same object.

This is not surprising behavior. It is explicit in how const is
defined. It makes sense that const does not have immutable behavior,
because otherwise there wouldn't be both const and immutable type
constructors.

You're wrong in saying the compiler doesn't guarantee anything with
const. I listed the things it does guarantee.


The literal guarantee is that things aren't modified through that
reference.


So now you do agree that (D's) const does provide guarantees, right?


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-02 Thread Jonathan M Davis
On Thursday, December 02, 2010 01:18:31 Don wrote:
 Walter Bright wrote:
  spir wrote:
  What would be the consequences if D had no const, only immutable
  (that, IIUC, removes the latter non-guarantee)?
  
  You'd have to write most every function twice, once to take immutable
  args and again for mutable ones.
 
 Doesn't 'inout' do almost the same thing?
 The only difference I can see between const and inout, is that inout
 tells which parameters could be aliased with the return value.

Except that doesn't inout actually produce multiple versions of the function, 
whereas with const, you only get the one?

- Jonathan M Davis


Re: Logical const

2010-12-02 Thread Bruno Medeiros

On 29/11/2010 23:04, Walter Bright wrote:

Steven Schveighoffer wrote:

On Mon, 29 Nov 2010 15:58:10 -0500, Walter Bright
newshou...@digitalmars.com wrote:


Steven Schveighoffer wrote:

Having a logical const feature in D would not be a convention, it
would be enforced, as much as const is enforced. I don't understand
why issues with C++ const or C++'s mutable feature makes any
correlations on how a D logical const system would fare. C++ const
is not D const, not even close.



Because people coming from C++ ask why not do it like C++'s?


I don't get it. A way to make a field mutable in a transitively-const
system is syntactically similar to C++, but it's not the same. Having
a logical-const feature in D does not devolve D's const into C++'s
const. If anything it's just a political problem.


Having mutable members destroys any guarantees that const provides.
That's not political.



That is not true, trivially. (assuming we are talking about D)

Having mutable members only (slightly) modifies the guarantees of const. 
For example:


class Foo {
  int x, y, z;
  mutable int blah;
}

void someFunc(const Foo foo) { ... }


here someFunc is still guaranteed to not modify any data transitively 
reachable through the foo reference, _with the exception of mutable 
members_. Is this still a useful guarantee? Well yes, for example in 
Peter's Matrix example, I could pass a const(Matrix) as an argument and 
still be confident that at most, only the mutable members would be 
change (the cached determinant), but not the logical state. The compiler 
would be able to check this, just as much as with D's current const.


Also, it would still guarantee that immutable data passed to that 
function is not transitively modified (with the exception of mutable 
members). And that is the main point of const.



A more interesting question is whether mutable members would 
significantly alter the guarantees of *immutable*. They would not 
changue the guarantee of immutable with regards to single-threaded 
optimization. So if a function has an immutable object, it can still 
assume the non-mutable members don't change, and optimize accordingly.


However, a big thing that could no longer be guaranteed, is that you 
would be able to safely pass an immutable object to a function running 
in another thread, without synchronization. This is because said 
function would be allowed to mutate the mutable members, but these could 
be being accessed concurrently, so...



--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-02 Thread Bruno Medeiros

On 29/11/2010 14:56, Steven Schveighoffer wrote:

This has been discussed at length on this newsgroup, and I argued for it
for a long time.  You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ? That 
doesn't sound right to me.


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-02 Thread Bruno Medeiros

On 28/11/2010 14:50, Peter Alexander wrote:

1. I have to change getWorldTransform to be a non-const function that
returns a non-const Matrix.

2. renderGameObject needs to be changed to receive a non-const GameObject.

3. I have lost any guarantee that rendering my GameObjects won't destroy
them...

Surely I'm not the only person that finds something *incredibly* wrong
with this!?


Indeed, you should not try to use D's const for the use-case of logical 
const. Rather, logical const is simply not supported, on a static type 
level.
But why is that such a big problem actually? A lot of other languages 
(Java, C#, etc.) have no support at all for something like logical 
const, and yet are consider generally much safer than C++. Why didn't 
they add logical const? Similarly for D, D has a lot of features that 
generally make it safer to program in C++ (with the same level of 
performance), such that in total they more than compensate for the lack 
of logical const. That is no good reason to not use D.


--
Bruno Medeiros - Software Engineer


Re: Logical const

2010-12-02 Thread Walter Bright

Don wrote:

Walter Bright wrote:

spir wrote:
What would be the consequences if D had no const, only immutable 
(that, IIUC, removes the latter non-guarantee)?


You'd have to write most every function twice, once to take immutable 
args and again for mutable ones.


Doesn't 'inout' do almost the same thing?
The only difference I can see between const and inout, is that inout 
tells which parameters could be aliased with the return value.


inout applies at the top level, but you cannot define a struct that has inout 
fields.


Re: Logical const

2010-12-02 Thread Don

Jonathan M Davis wrote:

On Thursday, December 02, 2010 01:18:31 Don wrote:

Walter Bright wrote:

spir wrote:

What would be the consequences if D had no const, only immutable
(that, IIUC, removes the latter non-guarantee)?

You'd have to write most every function twice, once to take immutable
args and again for mutable ones.

Doesn't 'inout' do almost the same thing?
The only difference I can see between const and inout, is that inout
tells which parameters could be aliased with the return value.


Except that doesn't inout actually produce multiple versions of the function, 


No. My understanding is that the constness of the return value is 
determined at the call site, but otherwise, it's as if all 'inout' 
parameters were const.



whereas with const, you only get the one?

- Jonathan M Davis


Re: Logical const

2010-12-02 Thread Steven Schveighoffer

On Thu, 02 Dec 2010 15:25:49 -0500, Don nos...@nospam.com wrote:


Jonathan M Davis wrote:

On Thursday, December 02, 2010 01:18:31 Don wrote:

Walter Bright wrote:

spir wrote:

What would be the consequences if D had no const, only immutable
(that, IIUC, removes the latter non-guarantee)?

You'd have to write most every function twice, once to take immutable
args and again for mutable ones.

Doesn't 'inout' do almost the same thing?
The only difference I can see between const and inout, is that inout
tells which parameters could be aliased with the return value.


inout is different in that parameters cannot implicitly cast to inout.   
It's actually on the same level as immutable and mutable.


 Except that doesn't inout actually produce multiple versions of the  
function,


No. My understanding is that the constness of the return value is  
determined at the call site, but otherwise, it's as if all 'inout'  
parameters were const.


This is correct, except for the implicit casting thing mentioned above.

-Steve


Re: Logical const

2010-12-02 Thread Steven Schveighoffer
On Wed, 01 Dec 2010 18:34:34 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:

If I see a function like:
 void foo(const(C) c);
 it doesn't mean that foo cannot modify the object referred to by c, it  
just means that foo won't modify data referenced through c.  But a C  
could store some data in a global variable, possibly even uniquely  
associated with each instance (I have shown this in a very old post  
proving logical const == const).  Then logically, the author of C could  
consider that data a part of C.  I have no way to stop him from editing  
that logical part of C, I'm relying on the author of C not to count  
mutable state as part of the state of C.
 Adding logical const just provides a location in the object itself for  
this data that is not supposed to be part of C's state.  It's not  
changing the guarantees that const already provides (which is very  
little, but helps you follow the correct conventions).


foo() could only modify c if it has, via some other means, acquired a  
mutable reference to c. If the user does not provide this other mutable  
reference, then foo() cannot modify c. foo() cannot go out and grab or  
create such a reference.


You're assuming the domain of c's state stops at its scope.  In fact,  
except for in pure functions, c's 'state' includes all global variables as  
well.  There is nothing stopping a programmer from using something outside  
the object/struct itself to store some state of c.  Those external data  
members would essentially be mutable (as I have shown in the past).  This  
takes most of the teeth out of const's guarantees.


This is quite the opposite from what you're proposing. Currently, the  
user has to make it possible for foo() to modify c, whereas your  
proposal is that foo() can modify c despite all attempts by the user to  
stop it.


Not exactly.  My proposal recognizes that we have to trust the programmer  
to only use mutable state (or external state in the current const  
implementation) to represent 'non-state' variables, that is, variables  
that are not considered part of the object itself.  This includes  
references to unowned data (like an output stream) or cached computations  
(which are not part of the state, they are an optimization).  My proposal  
allows the programmer to store that state inside the object.  All const  
guarantees on the actual state variables is fully enforced.


The question is, is the user of c concerned about const-ifying data  
members that are non-state members?  Usually no.  They only care about  
variables that are part of the state of the object.


Furthermore, if you mark foo() as pure, the compiler can guarantee there  
is no such hidden mutable reference.


Pure is an area where logical const would have to follow special rules.   
My inclination is that pure functions would have no access to members  
marked as 'no-state'.  That would at least be consistent with the current  
implementation of logical const.


This can have implications as far as copying value-types (what do you do  
with the mutable members?), but I think we could work out those rules.


-Steve


Re: Logical const

2010-12-02 Thread Steven Schveighoffer
On Thu, 02 Dec 2010 12:59:22 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:



On 01/12/2010 21:09, Steven Schveighoffer wrote:

On Tue, 30 Nov 2010 16:53:14 -0500, Walter Bright
newshou...@digitalmars.com wrote:


Steven Schveighoffer wrote:

If you find the above unsurprising, you are in the minority. I find
it surprising, and invalid that anyone would write code this way.
People simply just don't do that normally. It's just written to
demonstrate a point that the compiler does not guarantee anything via
const, it's guaranteed by convention. The compiler simply helps you
follow the convention.


Ok, I see what you mean now. Your code is relying on there being a
mutable alias of the same object.

This is not surprising behavior. It is explicit in how const is
defined. It makes sense that const does not have immutable behavior,
because otherwise there wouldn't be both const and immutable type
constructors.

You're wrong in saying the compiler doesn't guarantee anything with
const. I listed the things it does guarantee.


The literal guarantee is that things aren't modified through that
reference.


So now you do agree that (D's) const does provide guarantees, right?


It guarantees something very focused, and possible to work around without  
resorting to unsafe code.  That's my point.  The guarantee is well-defined  
and useful because it helps write correct code, but I don't see how a  
logical const guarantee is mythical whereas D's current const guarantee is  
impermeable.


I have shown examples of how const does not guarantee an object's state  
doesn't change.  It's some work to make it happen (which is good), but  
'guarantee' is too strong a word in my opinion.  I'd say it's a tool that  
helps you follow a good convention, just like logical const would.


-Steve


Re: Logical const

2010-12-02 Thread Steven Schveighoffer
On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros  
brunodomedeiros+s...@com.gmail wrote:



On 29/11/2010 14:56, Steven Schveighoffer wrote:

This has been discussed at length on this newsgroup, and I argued for it
for a long time.  You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ? That  
doesn't sound right to me.




Here is where I show how logical const already exists, it's just clunky to  
use.


BTW, this was before TLS, so the example would have to be updated a bit.

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927

-Steve


Re: Logical const

2010-12-02 Thread Jason House
Don Wrote:

 Jonathan M Davis wrote:
  On Thursday, December 02, 2010 01:18:31 Don wrote:
  Walter Bright wrote:
  spir wrote:
  What would be the consequences if D had no const, only immutable
  (that, IIUC, removes the latter non-guarantee)?
  You'd have to write most every function twice, once to take immutable
  args and again for mutable ones.
  Doesn't 'inout' do almost the same thing?
  The only difference I can see between const and inout, is that inout
  tells which parameters could be aliased with the return value.
  
  Except that doesn't inout actually produce multiple versions of the 
  function, 
 
 No. My understanding is that the constness of the return value is 
 determined at the call site, but otherwise, it's as if all 'inout' 
 parameters were const.

Inside a function, inout(T) should be considered a subtype of const(T). Nothing 
should be convertible to inout.



Re: Logical const

2010-12-02 Thread Walter Bright

Steven Schveighoffer wrote:
On Thu, 02 Dec 2010 13:57:04 -0500, Bruno Medeiros 
brunodomedeiros+s...@com.gmail wrote:



On 29/11/2010 14:56, Steven Schveighoffer wrote:

This has been discussed at length on this newsgroup, and I argued for it
for a long time.  You will not get any traction with Walter, because
I've already proven that logical const == const, and it still doesn't
change his mind.


Could you detail a bit what do you mean by logical const == const ? 
That doesn't sound right to me.




Here is where I show how logical const already exists, it's just clunky 
to use.


BTW, this was before TLS, so the example would have to be updated a bit.

http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.Darticle_id=58927 


What you're doing is keeping an alternate, mutable reference to each object. 
This does not mean that logical const == const.


Re: Logical const

2010-12-02 Thread Walter Bright

Steven Schveighoffer wrote:
I have shown examples of how const does not guarantee an object's state 
doesn't change.


Yes, as is well documented, const is a read only view. It is not immutable. That 
is why immutable is a separate attribute.


Re: Logical const

2010-12-01 Thread spir
On Tue, 30 Nov 2010 15:03:43 -0800
Walter Bright newshou...@digitalmars.com wrote:

 Andrew Wiley wrote:
  I've been following this thread on and off, but is there a definition 
  somewhere of exactly what const means in D2 and exactly what that 
  guaranties or doesn't guaranty? 
 
 Const provides a read-only view of a reference and anything reachable 
 through 
 that reference. It guarantees that no other thread can read or write that 
 reference or anything reachable through it.
 
 It does not guarantee that there isn't a mutable alias to that reference 
 elsewhere in the same thread that may modify it.

What would be the consequences if D had no const, only immutable (that, IIUC, 
removes the latter non-guarantee)?


Denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Logical const

2010-12-01 Thread Jonathan M Davis
On Wednesday 01 December 2010 03:13:08 spir wrote:
 On Tue, 30 Nov 2010 15:03:43 -0800
 
 Walter Bright newshou...@digitalmars.com wrote:
  Andrew Wiley wrote:
   I've been following this thread on and off, but is there a definition
   somewhere of exactly what const means in D2 and exactly what that
   guaranties or doesn't guaranty?
  
  Const provides a read-only view of a reference and anything reachable
  through that reference. It guarantees that no other thread can read or
  write that reference or anything reachable through it.
  
  It does not guarantee that there isn't a mutable alias to that reference
  elsewhere in the same thread that may modify it.
 
 What would be the consequences if D had no const, only immutable (that,
 IIUC, removes the latter non-guarantee)?

The biggest problem would be that no function could then work on both a mutable 
and an immutable value (unless it could be copied by value). With const, you 
can 
pass both mutable and immutable stuff to it. Without const, any and all 
functions 
which would want to deal with both would have to be duplicated. That includes 
stuff like member functions.

And of course, as C++ shows, there are plenty of cases where having const but 
no 
immutable can be quite valuable. Just the fact that you can pass an object to a 
function and know with reasonable certainty (and more certainty in D than C++) 
than that object won't be altered can be extremely valuable. Sure, many 
languages get by without const, but I think that they're definitely worse off 
for 
it. And with immutable added to the mix, I think that const is that much more 
important.

- Jonathan M Davis


Re: Logical const

2010-12-01 Thread spir
On Wed, 1 Dec 2010 03:22:39 -0800
Jonathan M Davis jmdavisp...@gmx.com wrote:

  What would be the consequences if D had no const, only immutable (that,
  IIUC, removes the latter non-guarantee)?  
 
 The biggest problem would be that no function could then work on both a 
 mutable 
 and an immutable value (unless it could be copied by value). With const, you 
 can 
 pass both mutable and immutable stuff to it. Without const, any and all 
 functions 
 which would want to deal with both would have to be duplicated. That includes 
 stuff like member functions.

Right, but isn't this the main point of Unqual!? (Would unqualify immutable as 
well, no?).

 And of course, as C++ shows, there are plenty of cases where having const but 
 no 
 immutable can be quite valuable. Just the fact that you can pass an object to 
 a 
 function and know with reasonable certainty (and more certainty in D than 
 C++) 
 than that object won't be altered can be extremely valuable. Sure, many 
 languages get by without const, but I think that they're definitely worse off 
 for 
 it. And with immutable added to the mix, I think that const is that much more 
 important.

For this case, I prefere the in qualifier. (And imo value parameters should 
be in by default).
Unless I miss important use cases, seems I would be happy with immutable and 
in.


denis
-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



Re: Logical const

2010-12-01 Thread Jordi

On 11/29/2010 11:56 PM, Steven Schveighoffer wrote:

On Sat, 20 Nov 2010 09:21:04 -0500, Peter Alexander
peter.alexander...@gmail.com wrote:


D does not support logical const due to the weak guarantees that it
provides.

So, without logical const, how are D users supposed to provide lazy
evaluation and memoization in their interfaces, given that the
interface should *seem* const, e.g.

class Matrix
{
double getDeterminant() const { /* expensive calculation */ }
}

If it turns out that getDeterminant is called often with the raw
matrix data remaining unchanged, how can we add caching to this class
without rewriting the const-ness of all code that touches it?


This has been discussed at length on this newsgroup, and I argued for it
for a long time. You will not get any traction with Walter, because I've
already proven that logical const == const, and it still doesn't change
his mind.

The thing is we *already* have a hidden field that is logically const --
an object's monitor. Regardless of an object's constancy, you can always
mutate the monitor. The compiler does it by logically inserting a cast
away from const, so that's what I'd suggest.

In reality, once you get into the realm of logical const, the compiler
no longer helps you. Any guarantees are now provided by you, not the
compiler.

I've proposed a very complex system to allow you to indicate only
certain fields as const which also worked with the existing const
system. While I think Walter and Andrei agreed it was possible, I agreed
with them that it was too complex to ask users to deal with.

What I'd like to see is a solution like this:

struct S
{
@mutable int i;
int x;
}

S s;

lconst(S) *ls = s; // new keyword!

ls.x = 5; // error
ls.i = 5; // OK

const(S) *cs = s;
ls = cs; // error, less restrictive const version

I think a library solution would be more crude than what the compiler
could do in this regard. But another keyword, and complicating the const
system further is probably too much tax for this feature.

In the end, I'd say just cast away const for the fields you want to
change, and at least you can get your work done.


And how do we write generic code when it's practically impossible to
determine const-ness from a glance? e.g. getDeterminant looks like it
should be const, but wouldn't be if it had caching, so writing generic
code that uses getDeterminant would be very difficult.


This is the point of Walter's objection. Logical const is not const, so
you would not be able to guarantee anything when you see const.

This kind of feature should only be used where the 'mutable' member is
not considered part of the state of the object. I.e. a cached
calculation is a good example of this. This rule is not enforceable, so
it is up to you to determine applicability.

-Steve


This entire thread has been very instructive. Thanks everybody.

This post is the one that, in my humble opinion of C++ game developer 
(like the original poster), moves more towards a solution.


I have one question though, that comes from the C++ const being totally 
ignored by dmc's optimizer, combined with the emphasys on D's const 
being verifiable by dmd:


Is the const keyword (both in C++, and in D) for the developer writing 
code, or for the compiler compiling it?


From what Walter said (and it makes a lot of sense to me), in the case 
of C++ it is only a hint to the programmer that the compiler will 
enforce you, but internally the compiler cannot really use it for anything.


On the other side, in D, it is a hint to the compiler but it will only 
use it if is verifiable. If that is the case, why do we need the const 
keyword in the first place? The compiler can already verify that the 
code is const and do the proper opimizations.


Steve's solution would be on the side of useful for the programmer, 
enforced by the compiler, uselss for the compiler.


Please let me know if i wrote something that doesn't make sense.

Jordi


Re: Logical const

2010-12-01 Thread so
Most likely not.  How do you say that the 'draw' function switches the  
widget to a different parameterized type?  With const, you can just slap  
a const on the end of the function.


Here is some example of what I mean:

class Widget
{
mutable Window location;
int x, y, width, height;
void draw() const { location.drawRectangle(x, y, width, height); }
}

where Window.drawRectangle is not a const function.

-Steve


Looking at the replies seems i am the only one that didn't quite get the  
example.


1. Why do you call it location. Is it something like a context/handle?
2. Why does Widget have a draw function?
3. If the Window doesn't provide a const draw function, why do you insist  
on having one? Because Widget actually doesn't change?
Well in this example it does and if the details of drawRectangle is not  
available to you, that change might be a serious one.


IMO for this particular example mutable just encourages a bad design.
On the other hand, mutable mutex is clear and reasonable since threads are  
different from usual program flow. Which as Sean said we have a solution.
I don't think we can have mutable within current const design. With the  
vast differences between D and C++ const system we shouldn't have it  
anyhow.


One another thing i don't quite get is why cast from immutable/const is  
allowed.
Is it because of the cases that we know the function doesn't change  
anything but still lacks the immutable/const signature?


Thank you!

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Logical const

2010-12-01 Thread so

Since i called it a bad design, i am entitled to introduce a better design.

interface renderer {
void draw(rect rects, size_t n);
}

class widget {
void draw(renderer r) { ... }
}


--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Logical const

2010-12-01 Thread so

On Wed, 01 Dec 2010 18:38:23 +0200, so s...@so.do wrote:

Since i called it a bad design, i am entitled to introduce a better  
design.


interface renderer {
void draw(rect rects, size_t n);
}

class widget {
void draw(renderer r) { ... }
}


Pfft sorry for that abomination!

interface renderer {
void draw(rect[] rects);
}

class widget {
rect r;
window owner;
void draw(renderer) const { ... }
}

--
Using Opera's revolutionary email client: http://www.opera.com/mail/


Re: Logical const

2010-12-01 Thread Jesse Phillips
Jordi Wrote:

 Is the const keyword (both in C++, and in D) for the developer writing 
 code, or for the compiler compiling it?

It is for the compiler to enforce what the developer is saying it is.

  From what Walter said (and it makes a lot of sense to me), in the case 
 of C++ it is only a hint to the programmer that the compiler will 
 enforce you, but internally the compiler cannot really use it for anything.

But the compiler is does not enforce it. At least people assume it is 
transitive when it is not.
 
 On the other side, in D, it is a hint to the compiler but it will only 
 use it if is verifiable. If that is the case, why do we need the const 
 keyword in the first place? The compiler can already verify that the 
 code is const and do the proper opimizations.

It can, unless the source is not available. Though it could probably output the 
information when creating .di files.
 
 Steve's solution would be on the side of useful for the programmer, 
 enforced by the compiler, uselss for the compiler.

But it isn't enforced by the compiler. It can enforce that you only modify 
things you claimed could be modified. But it can't enforce that it is logically 
const. It could prevent the modifiable parts from influencing the result, but 
then the requested caching isn't possible.

 Please let me know if i wrote something that doesn't make sense.
 
 Jordi

I do not believe you will run into issues with casting in a const function if 
logical const is truely happening.

The areas that could be an issue is when what you are casting is immutable and 
placed in read-only memory. In which case you should avoid creating immutable 
objects that use logical const.

Personally I would like to know how pointers/references in an immutable are 
dealt with when creating or passing them to a thread/process.

struct Foo {
int* foo;
}

immutable Foo f;

I believe that if this data is copied, such as in concurrent program, the data 
referenced by foo may also be placed in read only memory as immutable is 
defined to never change (though it can due to casting). Is this correct?


Re: Logical const

2010-12-01 Thread Jonathan M Davis
On Wednesday, December 01, 2010 06:17:56 spir wrote:
 On Wed, 1 Dec 2010 03:22:39 -0800
 
 Jonathan M Davis jmdavisp...@gmx.com wrote:
   What would be the consequences if D had no const, only immutable (that,
   IIUC, removes the latter non-guarantee)?
  
  The biggest problem would be that no function could then work on both a
  mutable and an immutable value (unless it could be copied by value).
  With const, you can pass both mutable and immutable stuff to it. Without
  const, any and all functions which would want to deal with both would
  have to be duplicated. That includes stuff like member functions.
 
 Right, but isn't this the main point of Unqual!? (Would unqualify immutable
 as well, no?).

No. All Unqual does is help you with template constraints and static ifs. It 
doesn't actually change the type. In the function itself, you're still going to 
end up with a mutable, const, or immutable type to deal with. Unqual!T just 
makes it so that you don't have to check for every combination of const, 
immutable, shared, etc.

  And of course, as C++ shows, there are plenty of cases where having const
  but no immutable can be quite valuable. Just the fact that you can pass
  an object to a function and know with reasonable certainty (and more
  certainty in D than C++) than that object won't be altered can be
  extremely valuable. Sure, many languages get by without const, but I
  think that they're definitely worse off for it. And with immutable added
  to the mix, I think that const is that much more important.
 
 For this case, I prefere the in qualifier. (And imo value parameters
 should be in by default). Unless I miss important use cases, seems I
 would be happy with immutable and in.

in _is_ const. It's essentially an alias for const scope.

Also, a classic example for the use of const which immutable doesn't help you 
with it all is returning member variables by reference or which are reference 
types when you don't want the caller to be able to modify them. Without const, 
you couldn't do that. const is huge. I'd _hate_ to see const go. The fact that 
D 
has const is one of the best things that it has going for it IMHO. I _hate_ the 
fact that languages like Java don't. It drives me nuts. Sure, you _can_ write 
programs without const - people do it all the time - but you have far fewer 
guarantees about your code, and it's much harder to determine which a function 
may or may not alter the value of a variable when you call it.

- Jonathan M Davis


Re: Logical const

2010-12-01 Thread Walter Bright

spir wrote:

What would be the consequences if D had no const, only immutable (that, IIUC, 
removes the latter non-guarantee)?


You'd have to write most every function twice, once to take immutable args and 
again for mutable ones.


Re: Logical const

2010-12-01 Thread Walter Bright

Jonathan M Davis wrote:
Also, a classic example for the use of const which immutable doesn't help you 
with it all is returning member variables by reference or which are reference 
types when you don't want the caller to be able to modify them. Without const, 
you couldn't do that. const is huge. I'd _hate_ to see const go. The fact that D 
has const is one of the best things that it has going for it IMHO. I _hate_ the 
fact that languages like Java don't. It drives me nuts. Sure, you _can_ write 
programs without const - people do it all the time - but you have far fewer 
guarantees about your code, and it's much harder to determine which a function 
may or may not alter the value of a variable when you call it.


As far as I know, D is the only language with a workable, enforcable, const 
system. That means we're the pioneers in getting this done right. It's up to us 
to show that it is an advantage.


Re: Logical const

2010-12-01 Thread Jesse Phillips
so Wrote:

 One another thing i don't quite get is why cast from immutable/const is  
 allowed.
 Is it because of the cases that we know the function doesn't change  
 anything but still lacks the immutable/const signature?
 
 Thank you!

I believe it is because cast doesn't do any checking. Cast is meant for 
breaking out of the type system and that is a big selling point for D. This is 
why casting should be avoided and to! should be used instead.

Thinking about it I think I'll post another thread about opCast.


Re: Logical const

2010-12-01 Thread Steven Schveighoffer
On Tue, 30 Nov 2010 16:53:14 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
If you find the above unsurprising, you are in the minority.  I find it  
surprising, and invalid that anyone would write code this way.  People  
simply just don't do that normally.  It's just written to demonstrate a  
point that the compiler does not guarantee anything via const, it's  
guaranteed by convention.  The compiler simply helps you follow the  
convention.


Ok, I see what you mean now. Your code is relying on there being a  
mutable alias of the same object.


This is not surprising behavior. It is explicit in how const is defined.  
It makes sense that const does not have immutable behavior, because  
otherwise there wouldn't be both const and immutable type constructors.


You're wrong in saying the compiler doesn't guarantee anything with  
const. I listed the things it does guarantee.


The literal guarantee is that things aren't modified through that  
reference.  The semantic result to someone calling the function is that it  
doesn't guarantee that the data referred to won't change, or that part of  
C's state cannot change through that reference.


If I see a function like:

void foo(const(C) c);

it doesn't mean that foo cannot modify the object referred to by c, it  
just means that foo won't modify data referenced through c.  But a C could  
store some data in a global variable, possibly even uniquely associated  
with each instance (I have shown this in a very old post proving logical  
const == const).  Then logically, the author of C could consider that data  
a part of C.  I have no way to stop him from editing that logical part of  
C, I'm relying on the author of C not to count mutable state as part of  
the state of C.


Adding logical const just provides a location in the object itself for  
this data that is not supposed to be part of C's state.  It's not changing  
the guarantees that const already provides (which is very little, but  
helps you follow the correct conventions).


When it comes to immutable and pure, we would need new rules.

-Steve


Re: Logical const

2010-12-01 Thread Steven Schveighoffer

On Wed, 01 Dec 2010 11:09:08 -0500, so s...@so.do wrote:

Most likely not.  How do you say that the 'draw' function switches the  
widget to a different parameterized type?  With const, you can just  
slap a const on the end of the function.


Here is some example of what I mean:

class Widget
{
mutable Window location;
int x, y, width, height;
void draw() const { location.drawRectangle(x, y, width, height); }
}

where Window.drawRectangle is not a const function.

-Steve


Looking at the replies seems i am the only one that didn't quite get the  
example.


1. Why do you call it location. Is it something like a context/handle?


It is where the widget is located, i.e. a physical window where the widget  
is drawn.



2. Why does Widget have a draw function?


Really?

3. If the Window doesn't provide a const draw function, why do you  
insist on having one? Because Widget actually doesn't change?
Well in this example it does and if the details of drawRectangle is not  
available to you, that change might be a serious one.


What changes?  x, y, width and height do not change.  That is considered  
part of the widget's state.  The location is part of the state but not  
completely, only the association with the location is considered part of  
the state.  This is not exactly conveyed in the type, if we have tail-  
references then we could use tail-mutable references as well, which means  
we get head-const :)


Note that drawRectangle does not take any references, so there is no  
possibility that any of widget's state changes.  That is provable.


-Steve


Re: Logical const

2010-12-01 Thread Steven Schveighoffer

On Wed, 01 Dec 2010 11:49:36 -0500, so s...@so.do wrote:


On Wed, 01 Dec 2010 18:38:23 +0200, so s...@so.do wrote:

Since i called it a bad design, i am entitled to introduce a better  
design.


interface renderer {
void draw(rect rects, size_t n);
}

class widget {
void draw(renderer r) { ... }
}


Pfft sorry for that abomination!

interface renderer {
void draw(rect[] rects);
}

class widget {
rect r;
window owner;
void draw(renderer) const { ... }
}


This requires you to store the widget-renderer relationship outside the  
widget.  Since each widget has exactly one location where it lives, this  
is awkward.  Much better to just store the relationship on each widget.


-Steve


Re: Logical const

2010-12-01 Thread Steven Schveighoffer
On Wed, 01 Dec 2010 12:43:38 -0500, Jesse Phillips  
jessekphillip...@gmail.com wrote:



Jordi Wrote:


On the other side, in D, it is a hint to the compiler but it will only
use it if is verifiable. If that is the case, why do we need the const
keyword in the first place? The compiler can already verify that the
code is const and do the proper opimizations.


It can, unless the source is not available. Though it could probably  
output the information when creating .di files.


This isn't practical.  .di files are not object files, so they can be  
altered too easily.


Note also that the compiler cannot do any optimizations for *const*  
functions, it can only restrict access (useful for programmer).



Steve's solution would be on the side of useful for the programmer,
enforced by the compiler, uselss for the compiler.


But it isn't enforced by the compiler. It can enforce that you only  
modify things you claimed could be modified. But it can't enforce that  
it is logically const. It could prevent the modifiable parts from  
influencing the result, but then the requested caching isn't possible.


The current const regime cannot enforce that you cannot modify the logical  
state of the object, because it does not know if any global variables are  
part of the state.


Logically const cannot be enforced, and it cannot be prevented by const  
alone.


A pure function enforces that you are not modifying external state.  If  
storage for non-state data is allowed inside the object (thereby enabling  
logically const data), then special rules would need to be followed for  
pure and immutable functions.


-Steve


Re: Logical const

2010-12-01 Thread so

2. Why does Widget have a draw function?


Really?


Sorry about that one, it was a misunderstanding on my part, was thinking i  
removed that.

And thanks for the reply!


Re: Logical const

2010-12-01 Thread Walter Bright

Steven Schveighoffer wrote:

If I see a function like:

void foo(const(C) c);

it doesn't mean that foo cannot modify the object referred to by c, it 
just means that foo won't modify data referenced through c.  But a C 
could store some data in a global variable, possibly even uniquely 
associated with each instance (I have shown this in a very old post 
proving logical const == const).  Then logically, the author of C could 
consider that data a part of C.  I have no way to stop him from editing 
that logical part of C, I'm relying on the author of C not to count 
mutable state as part of the state of C.


Adding logical const just provides a location in the object itself for 
this data that is not supposed to be part of C's state.  It's not 
changing the guarantees that const already provides (which is very 
little, but helps you follow the correct conventions).


foo() could only modify c if it has, via some other means, acquired a mutable 
reference to c. If the user does not provide this other mutable reference, then 
foo() cannot modify c. foo() cannot go out and grab or create such a reference.


This is quite the opposite from what you're proposing. Currently, the user has 
to make it possible for foo() to modify c, whereas your proposal is that foo() 
can modify c despite all attempts by the user to stop it.


Furthermore, if you mark foo() as pure, the compiler can guarantee there is no 
such hidden mutable reference.


Re: Logical const

2010-11-30 Thread Peter Alexander

On 29/11/10 11:13 PM, Walter Bright wrote:

Peter Alexander wrote:

On 29/11/10 8:58 PM, Walter Bright wrote:

Because people coming from C++ ask why not do it like C++'s?


No one is asking this.


I interpreted this from Jonathon as asking for it:

Without immutable, you could presumably get something similar
to what C++ has (if you could ever get Walter to go for it),

You also wrote:

I can be sure that my object will come back unmodified. That it is the
primary purpose of const. Like you said, it allows you to reason about
your programs.
Yes, GameObject could be unreasonably mutilated by careless use of
mutable, but in practice that simply doesn't happen.

Which implies that you regard C++'s system as sufficient. I pointed out
5 reasons why the be sure is incorrect. I believe it is necessary to
discuss and understand these points in order to justify why D does not
adopt C++'s system.


That was in response to your claim that C++'s const provides no 
guarantees. I was just giving a counterexample.


Just to clarify my position

- I *do not* want to copy C++'s const system.
- D style immutability is useful.
- C++ style logical const is also useful.
- I think they can both work side by side by introducing a new level of 
const and the mutable keyword.


Re: Logical const

2010-11-30 Thread Peter Alexander

On 30/11/10 2:00 AM, Walter Bright wrote:

Logical const means the same value is returned every time, not a
different one.


No, it doesn't. Not even D's const can guarantee that:

struct Foo
{
  int foo() const { return random(); }
}


pure is related to const, but they are not the same thing.


Re: Logical const

2010-11-30 Thread Simen kjaeraas

Walter Bright newshou...@digitalmars.com wrote:

It still is not verifiable. That's why logical constness is not a  
language issue, it is a convention.


And finally I understood what you meant. Sorry about this taking a while.

--
Simen


Re: Logical const

2010-11-30 Thread Max Samukha

On 11/30/2010 02:35 AM, Walter Bright wrote:

Fawzi Mohamed wrote:

logical const is useful for lazy functions and memoization, and if
implemented correctly it is perfectly safe.
As I said in an older discussions, to have it with the current system
all that is needed is some guarantees that the compiler will not
disallow unsafe changes (by moving to read only memory for
example)in some cases.
For example casted mutable types, so that casting to mutable works.


D allows escape from the type system, but the programmer who does that
loses the guarantees, and it's up to him to ensure that the result works.

String literals, for example, are going to often wind up in read only
memory.


The problem is that logical const has many perfectly valid use cases. 
You cannot simply tell people: Don't use it. It is a fraud. They will 
still be using casts or not using D. As casting away const is undefined 
behavior in D, the outcome will be every second non-trivial D program 
relying on undefined behavior.




Re: Logical const

2010-11-30 Thread Steven Schveighoffer
On Mon, 29 Nov 2010 17:21:27 -0500, Simen kjaeraas  
simen.kja...@gmail.com wrote:



Steven Schveighoffer schvei...@yahoo.com wrote:

Except the language says what results from your code (casting away  
const and then mutating) is undefined behavior.  This means all bets  
are off, it can crash your program.  I'm unsure how the compiler could  
take that route, but that's what's in the spec.


Maybe because of the way const works, it never really is undefined, but  
there will always be that loophole.


The thing is, immutable is implicitly castable to const, and immutable
data could be stored in write-protected memory. Apart from that, I  
believe

it is safe to cast away const.


One would have to ensure that data with mutable members never makes it  
into ROM.  This should be easy for the compiler since the full type is  
always known at construction time.


There are other issues with logical const that would need to be addressed,  
specifically pure functions and implicit sharing.


-Steve


Re: Logical const

2010-11-30 Thread Steven Schveighoffer
On Mon, 29 Nov 2010 18:04:31 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
On Mon, 29 Nov 2010 15:58:10 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
Having a logical const feature in D would not be a convention, it  
would be enforced, as much as const is enforced.  I don't understand  
why issues with C++ const or C++'s mutable feature makes any  
correlations on how a D logical const system would fare.  C++ const  
is not D const, not even close.



Because people coming from C++ ask why not do it like C++'s?
 I don't get it.  A way to make a field mutable in a transitively-const  
system is syntactically similar to C++, but it's not the same.  Having  
a logical-const feature in D does not devolve D's const into C++'s  
const.  If anything it's just a political problem.


Having mutable members destroys any guarantees that const provides.  
That's not political.


What guarantees?  Const provides no guarantees.

class C
{
   static C theCommonOne;
   int x;
   void foo() const {
  theCommonOne.x = 5; // theCommonOne could be this
   }
}

Only immutable provides guarantees.  As I said earlier, the place where  
logical const would cause issues is immutable.  Immutable data is  
currently implicitly shareable, and the compiler may take steps to  
optimize strongly-pure function calls, which require immutable  
parameters.  Any logical const system would have to either work with those  
requirements or force them to be reevaluated.


And, I repeat, having a mutable type qualifier DOES NOT make logical  
const a language feature. This is why discussion and understanding of  
C++'s const system is so important - people impute characteristics into  
it that it simply does not have.


I've proven in the past that logical const is equivalent to full const.   
That is, I can emulate logical const without any casts in the current  
const regime.  Making it more efficient and simpler is all we would be  
doing.


-Steve


Re: Logical const

2010-11-30 Thread Steven Schveighoffer
On Tue, 30 Nov 2010 08:04:57 -0500, Steven Schveighoffer  
schvei...@yahoo.com wrote:


On Mon, 29 Nov 2010 17:21:27 -0500, Simen kjaeraas  
simen.kja...@gmail.com wrote:



Steven Schveighoffer schvei...@yahoo.com wrote:

Except the language says what results from your code (casting away  
const and then mutating) is undefined behavior.  This means all bets  
are off, it can crash your program.  I'm unsure how the compiler could  
take that route, but that's what's in the spec.


Maybe because of the way const works, it never really is undefined,  
but there will always be that loophole.


The thing is, immutable is implicitly castable to const, and immutable
data could be stored in write-protected memory. Apart from that, I  
believe

it is safe to cast away const.


One would have to ensure that data with mutable members never makes it  
into ROM.  This should be easy for the compiler since the full type is  
always known at construction time.


I should clarify that this only applies if logical const is a language  
feature, not a library feature.  So in the context of this sub-thread, you  
are right, one needs to be careful.


-Steve


Re: Logical Const using a Mutable template

2010-11-30 Thread Max Samukha

On 11/30/2010 09:30 AM, Jesse Phillips wrote:

This came up in discussion and I think the behavior is safe and usable when 
wanting to change class data in a const function.

http://article.gmane.org/gmane.comp.lang.d.general/43476

One limitation is that value types declared as Mutable (Mutable!(int)) can not 
be changed if the encapsulating class is declared immutable. For this reason a 
Mutable value can not be changed inside a const function. I think this is 
acceptable.

Another limitation appears to be an issue with alias this. It is commented as a 
//FIXME in the code.

https://gist.github.com/721066

An example usage looks like:

 class Inner { int n; }

 class A {
 Mutable!(int*) n;
 Mutable!(int)  n2;
 Mutable!(Inner) innerClass;
 this() { n = new int; innerClass = new Inner; }

 void foo( int num ) {
 n2 = num;
 }

 void bar( int num ) const {
 innerClass.n = num;
 }
 }

 auto aImmu = new immutable(A);
 auto aMu   = new A;
 int i = 8;

 *aImmu.n = i,
 aImmu.bar(6),
 *aMu.n = i,
 aMu.n =i,
 aMu.n2 = i,
 aMu.bar(6),
 aMu.foo(6),



It is not enough to forward opAssign and opEquals. Any operator should 
be handled. For example, change the implementation of A.foo to


n2 += 1;

and you will get a segfault during compilation.

You could add more kludge with operator overloads but I don't think it 
is worth the effort. It makes more sense to wait until alias this bugs 
are fixed. On the other hand, we may wait forever since the way alias 
this should handle operators has never been specified.




Re: Logical const

2010-11-30 Thread Don

Peter Alexander wrote:
D does not support logical const due to the weak guarantees that it 
provides.


So, without logical const, how are D users supposed to provide lazy 
evaluation and memoization in their interfaces, given that the interface 
should *seem* const, e.g.


class Matrix
{
  double getDeterminant() const { /* expensive calculation */ }
}

If it turns out that getDeterminant is called often with the raw matrix 
data remaining unchanged, how can we add caching to this class without 
rewriting the const-ness of all code that touches it?


And how do we write generic code when it's practically impossible to 
determine const-ness from a glance? e.g. getDeterminant looks like it 
should be const, but wouldn't be if it had caching, so writing generic 
code that uses getDeterminant would be very difficult.


What are the use cases for logical const? Are there any other important 
ones, apart from caching?




Re: Logical const

2010-11-30 Thread Steven Schveighoffer

On Tue, 30 Nov 2010 10:06:40 -0500, Don nos...@nospam.com wrote:


Peter Alexander wrote:
D does not support logical const due to the weak guarantees that it  
provides.
 So, without logical const, how are D users supposed to provide lazy  
evaluation and memoization in their interfaces, given that the  
interface should *seem* const, e.g.

 class Matrix
{
  double getDeterminant() const { /* expensive calculation */ }
}
 If it turns out that getDeterminant is called often with the raw  
matrix data remaining unchanged, how can we add caching to this class  
without rewriting the const-ness of all code that touches it?
 And how do we write generic code when it's practically impossible to  
determine const-ness from a glance? e.g. getDeterminant looks like it  
should be const, but wouldn't be if it had caching, so writing generic  
code that uses getDeterminant would be very difficult.


What are the use cases for logical const? Are there any other important  
ones, apart from caching?


Being able to associate data with an object/struct without owning the  
data.  Deep in this thread I give an example of having a widget who knows  
its location and knows how to draw itself in that location.  Such a  
function cannot be marked const even though no widget data is changed,  
since the draw function is not const.  But the Widget doesn't own the  
location, it's just referencing it.


At that point, const moves from compiler-verified to documentation only.

-Steve


Re: Logical const

2010-11-30 Thread Sean Kelly
Don Wrote:
 
 What are the use cases for logical const? Are there any other important 
 ones, apart from caching?

In C++ I'd say the need to lock a mutex to safely read the const data, but 
synchronized largely obviates that need in D.


Re: Logical const

2010-11-30 Thread Andrei Alexandrescu

On 11/30/10 5:25 AM, Max Samukha wrote:

On 11/30/2010 02:35 AM, Walter Bright wrote:

Fawzi Mohamed wrote:

logical const is useful for lazy functions and memoization, and if
implemented correctly it is perfectly safe.
As I said in an older discussions, to have it with the current system
all that is needed is some guarantees that the compiler will not
disallow unsafe changes (by moving to read only memory for
example)in some cases.
For example casted mutable types, so that casting to mutable works.


D allows escape from the type system, but the programmer who does that
loses the guarantees, and it's up to him to ensure that the result works.

String literals, for example, are going to often wind up in read only
memory.


The problem is that logical const has many perfectly valid use cases.
You cannot simply tell people: Don't use it. It is a fraud. They will
still be using casts or not using D. As casting away const is undefined
behavior in D, the outcome will be every second non-trivial D program
relying on undefined behavior.


I'm not seeing half of non-trivial C++ programs using mutable.

Andrei


Re: Logical Const using a Mutable template

2010-11-30 Thread Jesse Phillips
Max Samukha Wrote:

 On 11/30/2010 09:30 AM, Jesse Phillips wrote:
  This came up in discussion and I think the behavior is safe and usable when 
  wanting to change class data in a const function.
 
  http://article.gmane.org/gmane.comp.lang.d.general/43476
 
  One limitation is that value types declared as Mutable (Mutable!(int)) can 
  not be changed if the encapsulating class is declared immutable. For this 
  reason a Mutable value can not be changed inside a const function. I think 
  this is acceptable.
 
  Another limitation appears to be an issue with alias this. It is commented 
  as a //FIXME in the code.
 
  https://gist.github.com/721066
 
 It is not enough to forward opAssign and opEquals. Any operator should 
 be handled. For example, change the implementation of A.foo to
 
 n2 += 1;
 
 and you will get a segfault during compilation.
 
 You could add more kludge with operator overloads but I don't think it 
 is worth the effort. It makes more sense to wait until alias this bugs 
 are fixed. On the other hand, we may wait forever since the way alias 
 this should handle operators has never been specified.
 

The intent isn't to work around alias this bugs. It is to provide mutation in a 
const function, but with the guarantee that it will not result in undefined 
behavior. (I should have stated that first, but it was getting late). 
Concurrency shouldn't be any different from having a reference in a class you 
are sharing. Sharing immutable objects should be fine as they still can not 
have their state changed.

The rules for this are:

* Only mutable data can be assigned to a Mutable
* Modification of referenced fields can be modified (inner class fields, 
pointer targets)

I'll be back to finish this in a bit.


Re: Logical const

2010-11-30 Thread Max Samukha

On 11/30/2010 05:39 PM, Andrei Alexandrescu wrote:

On 11/30/10 5:25 AM, Max Samukha wrote:

On 11/30/2010 02:35 AM, Walter Bright wrote:

Fawzi Mohamed wrote:

logical const is useful for lazy functions and memoization, and if
implemented correctly it is perfectly safe.
As I said in an older discussions, to have it with the current system
all that is needed is some guarantees that the compiler will not
disallow unsafe changes (by moving to read only memory for
example)in some cases.
For example casted mutable types, so that casting to mutable works.


D allows escape from the type system, but the programmer who does that
loses the guarantees, and it's up to him to ensure that the result
works.

String literals, for example, are going to often wind up in read only
memory.


The problem is that logical const has many perfectly valid use cases.
You cannot simply tell people: Don't use it. It is a fraud. They will
still be using casts or not using D. As casting away const is undefined
behavior in D, the outcome will be every second non-trivial D program
relying on undefined behavior.


I'm not seeing half of non-trivial C++ programs using mutable.

Andrei


That was a hyperbole. But logical const is obviously not some obscure 
corner case. Objects with reference counters, proxy objects, objects 
keeping access statistics (e.g. for debugging purposes), objects using 
logger objects, etc - all require logical const. To corroborate, Qt 
sources have over 600 'mutable' field declarations.


Re: Logical const

2010-11-30 Thread Sean Kelly
Steven Schveighoffer Wrote:

 On Tue, 30 Nov 2010 10:06:40 -0500, Don nos...@nospam.com wrote:
 
 
  What are the use cases for logical const? Are there any other important  
  ones, apart from caching?
 
 Being able to associate data with an object/struct without owning the  
 data.  Deep in this thread I give an example of having a widget who knows  
 its location and knows how to draw itself in that location.  Such a  
 function cannot be marked const even though no widget data is changed,  
 since the draw function is not const.  But the Widget doesn't own the  
 location, it's just referencing it.

Hm... can a const object mutate globals?


Re: Logical const

2010-11-30 Thread Jonathan M Davis
On Tuesday, November 30, 2010 09:10:03 Sean Kelly wrote:
 Steven Schveighoffer Wrote:
  On Tue, 30 Nov 2010 10:06:40 -0500, Don nos...@nospam.com wrote:
   What are the use cases for logical const? Are there any other important
   ones, apart from caching?
  
  Being able to associate data with an object/struct without owning the
  data.  Deep in this thread I give an example of having a widget who knows
  its location and knows how to draw itself in that location.  Such a
  function cannot be marked const even though no widget data is changed,
  since the draw function is not const.  But the Widget doesn't own the
  location, it's just referencing it.
 
 Hm... can a const object mutate globals?

Yes. All it means for a const function to be const is that its this 
pointer/reference is const. If you don't want it to mess with globals, you need 
to make it pure.

- Jonathan M Davis


Re: Logical Const using a Mutable template

2010-11-30 Thread Jesse Phillips
Jesse Phillips Wrote:

 The rules for this are:
 
 * Only mutable data can be assigned to a Mutable
 * Modification of referenced fields can be modified (inner class fields, 
 pointer targets)
 
 I'll be back to finish this in a bit.

* Value types can be modified if the encapsulating class
  is not declared const/immutable

And I have come up with 2 major concerns related to immutable classes. Would an 
inner class be placed in read-only memory  in instantiating an immutable class:

class A {
Mutable!(Inner) innerclass;
this() { innerclass = new Inner; } // Is innerclass placed in read-only 
memory
}

new immutable(A);

And what about when an immutable instance is passed to a thread or network. 
Would the innerclass be placed in read-only memory for the new thread/machine.

And looking over it again, I found that I wasn't using the opAssign const 
functions, they didn't meet the requirements. So I think a working alias this 
would allow the template to be: (The important check being that Mutables are 
unqualified)

struct Mutable( T ) if ( is( T : Unqual!T ) ) {
 private T _payload;

 this( T t ) {
 _payload = t;
 }

 @trusted @property ref T get( )( ) const {
 T* p = cast( T* )_payload;
 return *p;
 }
 alias get this;
}

https://gist.github.com/721066


Re: Logical const

2010-11-30 Thread Walter Bright

Peter Alexander wrote:

Just to clarify my position

- I *do not* want to copy C++'s const system.
- D style immutability is useful.
- C++ style logical const is also useful.
- I think they can both work side by side by introducing a new level of 
const and the mutable keyword.



Once mutable is introduced, the const attribute becomes no longer useful. You 
(and the compiler) can no longer look at a function signature and reason about 
the const-ness behavior. If a new attribute is introduced, newlevel, you 
really cannot reason about the behavior at all.


I wish to emphasize that the compiler cannot provide any verification that 
logical constness is indeed happening. (It cannot do it in C++, either, the 
notion that the C++ type system supports logical constness is incorrect.)


Re: Logical const

2010-11-30 Thread Walter Bright

Steven Schveighoffer wrote:

What guarantees?  Const provides no guarantees.


1. That nobody will modify any of the data structure accessed through the const 
reference provided. Thus, if your data is immutable, unique, or the function is 
pure, you are guaranteed it will not be modified.


2. That another thread will not be modifying any of that data structure.


Re: Logical const

2010-11-30 Thread Walter Bright

Simen kjaeraas wrote:

Walter Bright newshou...@digitalmars.com wrote:

It still is not verifiable. That's why logical constness is not a 
language issue, it is a convention.


And finally I understood what you meant. Sorry about this taking a while.



I know it's a tough issue, and worth the effort at trying to explain it.


Re: Logical const

2010-11-30 Thread Walter Bright

Max Samukha wrote:
The problem is that logical const has many perfectly valid use cases. 
You cannot simply tell people: Don't use it. It is a fraud.


I am not and never have said don't use it. It's a fraud. I said that the C++ 
language has no notion of logical constness, and that it is nothing more than 
a popular convention. The notion that C++ supports logical constness is the fraud.



They will 
still be using casts or not using D. As casting away const is undefined 
behavior in D, the outcome will be every second non-trivial D program 
relying on undefined behavior.


Yes, it's undefined behavior. That doesn't mean you cannot use it, just that you 
are responsible for getting it right rather than the compiler.


Re: Logical const

2010-11-30 Thread Walter Bright

Sean Kelly wrote:

Hm... can a const object mutate globals?


Yes.


Re: Logical const

2010-11-30 Thread Steven Schveighoffer
On Tue, 30 Nov 2010 14:07:51 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:

What guarantees?  Const provides no guarantees.


1. That nobody will modify any of the data structure accessed through  
the const reference provided. Thus, if your data is immutable, unique,  
or the function is pure, you are guaranteed it will not be modified.


If your data is immutable it will not be modified.  That is a contract of  
immutable, not a const function.
If the function is pure, and *all* arguments to the function are const,  
the data wil not be modified, that is verifiable.
If the data is unique is not enforced by the compiler, so it's up to you  
to ensure this.  Sounds like a convention to me.


The example that I gave does not seem to you like it would surprise  
someone?  I passed in a const object and it got modified, even though no  
casts were used.


If you add a portion to the object that is mutable or head-const, this  
alters the rule, but does not detract from the guarantees.  The rule then  
just contains an exception for the mutable portion.



2. That another thread will not be modifying any of that data structure.


That rule is not affected by logical const.

-Steve


Re: Logical const

2010-11-30 Thread Sean Kelly
Walter Bright Wrote:

 Sean Kelly wrote:
  Hm... can a const object mutate globals?
 
 Yes.

That's what I thought.  Having a transitively const object modify a global 
doesn't seem much different to me than having it modify a non-owned aliased 
object.  Excepting of course that the compiler can't know which references 
denote ownership and non-ownership, so the current behavior seems correct from 
a pragmatic perspective if nothing else.


Re: Logical const

2010-11-30 Thread Walter Bright

Steven Schveighoffer wrote:
The example that I gave does not seem to you like it would surprise 
someone?  I passed in a const object and it got modified, even though no 
casts were used.


No, it doesn't surprise me. Const on one object does not apply to another 
object.


Re: Logical const

2010-11-30 Thread Steven Schveighoffer
On Tue, 30 Nov 2010 15:16:04 -0500, Walter Bright  
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
The example that I gave does not seem to you like it would surprise  
someone?  I passed in a const object and it got modified, even though  
no casts were used.


No, it doesn't surprise me. Const on one object does not apply to  
another object.


So this:

void myfn(const(C) n)
{
   assert(n.x == 1);
   n.foo();
   assert(n.x == 5);
}

Wouldn't be surprising to you?

-Steve


Re: Logical const

2010-11-30 Thread Jérôme M. Berger
Walter Bright wrote:
 Steven Schveighoffer wrote:
 The example that I gave does not seem to you like it would surprise
 someone?  I passed in a const object and it got modified, even though
 no casts were used.
 
 No, it doesn't surprise me. Const on one object does not apply to
 another object.

const C c = C.theCommonOne;
auto old = c.x;
c.foo();
assert (old == c.x); // Fails and this does not surprise you?

Jerome
-- 
mailto:jeber...@free.fr
http://jeberger.free.fr
Jabber: jeber...@jabber.fr



signature.asc
Description: OpenPGP digital signature


Re: Logical const

2010-11-30 Thread Peter Alexander

On 30/11/10 6:21 AM, so wrote:

I can understand Peter using Matrix just for example but still i have to
say.
A small vector/matrix with an extra field to memoize things perform
likely worse than their usual implementations.
That small extra field might just destroy any alignments, optimizations,
which are scarier than the computation itself.


This may be true for this particular case, but has no bearing on the 
discussion in general.




Re: Logical const

2010-11-30 Thread Walter Bright

Steven Schveighoffer wrote:
On Tue, 30 Nov 2010 15:16:04 -0500, Walter Bright 
newshou...@digitalmars.com wrote:



Steven Schveighoffer wrote:
The example that I gave does not seem to you like it would surprise 
someone?  I passed in a const object and it got modified, even though 
no casts were used.


No, it doesn't surprise me. Const on one object does not apply to 
another object.


So this:

void myfn(const(C) n)
{
   assert(n.x == 1);
   n.foo();
   assert(n.x == 5);
}

Wouldn't be surprising to you?


No. There are a lot of things a member x could be. The const only applies to 
the instance data.


  1   2   >