Re: Getting the const-correctness of Object sorted once and for all

2012-05-23 Thread era scarecrow

On Tuesday, 15 May 2012 at 21:18:11 UTC, Chad J wrote:

On 05/15/2012 03:32 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:
An idea I thought of is to introduce a method local 
declaration that

allows a method to access instance-specific-state that isn't
accessible to the rest of the class:


This is an interesting idea (as it seems to really try to keep
the state changes internal to the function, which can be seen  
as
how D handles purity)... however, it breaks the point of 
purity due to

this:

pure nothrow hash_t toHash() const {
@instance hash_t bad = 0;
++bad;
return hashfn(field) + bad;
}

Now it violates everyone's definition of purity and we can no
longer make our sweet optimizations and reasoning about the  
code.

Sure, we could trust the programmers to not do this ... but
that's another debate entirely.



Yes.  I intend to trust the programmers to not do this.

Otherwise we need to find some way to ensure that a function 
that alters external state will always return the same value as 
long as the rest of the program doesn't change the state it 
looks at.




... and setting toStringCacheValid to true in toString violates
const, so this is absolutely not allowed. Sorry.



Yep, ya got me.



Maybe there's a solution, but I doubt the solution is something
the programmer can/should do completely transparently.


 I think I have an answer. It came to me last night when I was 
going to sleep. Anyways, it may be a little verbose but I Hope I 
got it all right. Oddly enough it stays within D's own rules :)


--

import std.stdio;
import std.conv;

struct CachedHash(T) {
  T element;
  uint hash;
  alias element this;

  this(T inVal) {
element = inVal;
  }

  @property uint toHash(){
if(!hash) {
  writeln(Hashing!);
  hash = element.toHash();
} else
  writeln(From Cache!);
return hash;
  }
}

uint toHash(string x){
  return 123456; //for example
}

class AString {
  string str;

  this(string s) {
str = s;
  }

  const uint toHash(){
return 123456;
  }
}

uint calledHash(T)(T as){
  return as.toHash();
}

int main() {
  immutable char[] s = this is a test!;

  auto ch_s = CachedHash!string(s);  //since s is inherently 
immutable..


  writeln(s);
  writeln(s.toHash, \n);

  writeln(ch_s);
  writeln(ch_s.toHash);
  writeln(ch_s.toHash, \n);

  AString as = new AString(s);
  immutable AString ias = cast(immutable AString) new AString(s);

  auto as_s = CachedHash!AString(as);
  auto ias_s = CachedHash!(immutable AString)(ias);

  writeln(as.toHash, \n);
  writeln(as_s.toHash);
  writeln(as_s.toHash);
  writeln(ias_s.toHash);
  writeln(ias_s.toHash, \n);

  writeln(calledHash!AString(as_s)); //non-CachedHash aware 
simulate

  writeln(calledHash!(typeof(as_s))(as_s));

  return 0;
}




Re: Getting the const-correctness of Object sorted once and for all

2012-05-17 Thread Marco Leise
Am Mon, 14 May 2012 16:54:34 -0700
schrieb Walter Bright newshou...@digitalmars.com:

 On 5/14/2012 10:08 AM, Tove wrote:
  but c++ has the 'mutable' keyword as an easy escape route...
 
 The existence of that capability means that 'const' in C++ cannot be 
 meaningfully reasoned about.

class Foo
{
  uint a, b;
  // can only call const pure nothrow members here:
  lazy uint somethingLazyInitialized = { return 2 * bar(); }

  uint bar() const pure nothrow @safe
  {
// complex calculation
return a + b;
  }

  override uint toHash() const pure nothrow @safe
  {
// sets the field on the first use
return somethingLazyInitialized;
  }
}

Internally the lazy uint consists of a function pointer and the uint. A lazy 
field acts like a read-only property. Whenever the field is read, code is 
generated that first checks, if the function pointer is not null. It then 
updates the uint with the return value of the function call and sets the 
function pointer to null, to indicate that the value is now initialized.

An instance of Foo cannot be immutable (it makes no sense to ask for that with 
lazy initialization), but it can be const. This is a form of logical const that 
still allows reasoning about the code + compiler enforcement in contrast to the 
more flexible (in a positive and negative sense) C++ mutable.

-- 
Marco


Re: Getting the const-correctness of Object sorted once and for all

2012-05-16 Thread Christophe
Chad J , dans le message (digitalmars.D:167472), a écrit :
 On 05/15/2012 02:49 PM, Christophe wrote:
 Chad J , dans le message (digitalmars.D:167461), a écrit :
 An idea I thought of is to introduce a method local declaration that
 allows a method to access instance-specific-state that isn't accessible
 to the rest of the class:

 It is not good for caching results, since the cache often has to be
 erased when the object is modified.
 
 I did outline a way to invalidate caches with this.

I was a bit fast on my post, but it cannot work: Something has to be 
modified both normally by a non-const function to invalidate the cache, 
and exceptionally by the const function to set the cache.


Two remarks about people trying to solve the const problem:

 - People trying to modify a const value by using holes like delegates 
and inner classes might as well cast away const.

 - People trying to associate mutable members to a const object might as 
well ask for a @mutable keyword to have members that stays mutable even 
if the object is const (and remove power to class with @mutable members, 
such as the possibility to be immutable, have strongly pure methods, 
etc.). It will have the same effect as other proposed work arround, but 
at least it is easy to understand how it works and won't cripple the 
langage with too many awkward rules.

After all, D is a mutli-paradigm programming langage. It should not 
force programmers to use hard constness. Better use as clean as possible 
@mutable data than complicated workarrounds.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-16 Thread Timon Gehr

On 05/16/2012 10:17 AM, Christophe wrote:

[...] @mutable keyword to have members that stays mutable even
if the object is const (and remove power to class with @mutable members,
such as the possibility to be immutable, have strongly pure methods,
etc.). It will have the same effect as other proposed work arround, but
at least it is easy to understand how it works and won't cripple the
langage with too many awkward rules.



I think this is a very sane proposal, (the important points are stated 
in parens) because 'const' alone does not give any useful guarantees. 
The essential guarantee is that an immutable object attached to a const 
reference cannot be mutated through it.


An issue is that fully 'const pure' methods would lose their guarantees 
when passed a mutable object, unless const member functions that mutate 
@mutable members of the receiver object cannot be pure. This rule would 
disqualify the solution for what is discussed in the OP.


Leaving the rule out would imply that the currently valid code 
transformation:


int foo(const pure A){ }

A a = ...;

int x=foo(a), y=foo(a)
=
int x=foo(a), y=x;

would become incorrect in the general case. The proposal trades off 
'const' guarantees against mutable/immutable interoperability. I would 
be willing to take that.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-16 Thread Christophe
Timon Gehr , dans le message (digitalmars.D:167544), a écrit :
 Leaving the rule out would imply that the currently valid code 
 transformation:
 
 int foo(const pure A){ }
 
 A a = ...;
 
 int x=foo(a), y=foo(a)
 =
 int x=foo(a), y=x;
 
 would become incorrect in the general case. The proposal trades off 
 'const' guarantees against mutable/immutable interoperability. I would 
 be willing to take that.

The language could declare that the transformation is legal, and that 
the programmer using 'mutable' members is responsible for keeping the 
function logically const.

-- 
Christophe


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Walter Bright

On 5/14/2012 1:03 AM, Mehrdad wrote:

On Monday, 14 May 2012 at 06:27:15 UTC, Walter Bright wrote:

#3 Improves self-documentation of code - it's more understandable and less
susceptible to breakage during maintenance.


#3 is also valid for C++, so I wasn't exactly considering that.


No, it isn't.

1. C++ legally allows changing const values.

2. const only applies to the top level (i.e. head const). So if you have 
anything more than a simple value type, it literally means *nothing*.





#4 Improves encapsulation


o.O How does it improve encapsulation?


By guaranteeing that the method call is a reader, not a mutator.


D is a very deliberate step away from correctness by convention and towards 
correctness with mechanical verification.




Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread bearophile

Walter Bright:

Being able to reason about code is *intensely* important. All 
those decorations - const, pure, nothrow, out - serve to help 
that out.


Bertrand Meyer (Eiffel author) is now working a lot on this, as
you see from many of the last posts on his blog
(http://bertrandmeyer.com/ ). The Modern Eiffel language is
being designed right to allow more reasoning about code. This
seems a small trend in new languages, but I can't predict how
much important it will become in ten years.

Bye,
bearophile


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Stewart Gordon

On 14/05/2012 18:08, Tove wrote:
snip

class Outer
{
  int i = 6; // mutable

  class Inner {
int y=0;

int foo() const
{
  // ++y; // fail
  return ++i; // look ma, mutable const
}
  }
  Inner inner;
  this()
  {
inner = new Inner;
  }
  alias inner this;
}


Indeed, you've found a hole in the const system nobody seems to have noticed 
before!

Inner.foo is const, so from foo's point of view, Inner.outer needs to be.

To expand your example a bit:
--
import std.stdio;

class Outer {
int i = 6;

class Inner {
int y=0;

int foo() const {
pragma(msg, this.outer:  ~ typeof(this.outer).stringof);
pragma(msg, i:   ~ typeof(i).stringof);
return ++i;
}
}
Inner inner;
this() {
inner = new Inner;
}
}

void main() {
const(Outer) x = new Outer;
pragma(msg, x:   ~ typeof(x).stringof);
pragma(msg, x.inner: ~ typeof(x.inner).stringof);
x.inner.foo();
writeln(x.i);
}
--
C:\Users\Stewart\Documents\Programming\D\Testsdmd inner_const.d
this.outer: const(Outer)
i:  const(int)
x:  const(Outer)
x.inner:const(Inner)
--

but nonetheless, it allows i to be modified!

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

Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chad J

On 05/13/2012 12:39 PM, Stewart Gordon wrote:

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

This has gone on for too long.

Object.toString, .toHash, .opCmp and .opEquals should all be const.
(It's also been stated somewhere that they should be pure and nothrow,
or something like that, but I forget where.)

This makes it a nightmare to use const objects in data structures, among
other things, at best forcing the use of ugly workarounds. There are
probably other, more serious effects of this that can't easily be worked
around.

It seems that the main obstacle is rewriting the relevant methods in
std.stream. The current implementation doesn't make sense anyway -
reading the entire contents of a file is certainly not the way to
generate a hash or string representation of the stream. I'm thinking the
hash should probably be the stream handle, and the string representation
could perhaps be the full pathname of the file. Of course, what it
should be for non-file streams is another matter. (This would be a
change at the API level, but when the API's as fundamentally flawed as
this)

Are there any other bits of druntime/Phobos that need to be sorted out
before these methods can be declared const/pure/nothrow once and for all?

Stewart.


I haven't read all of the replies here, but the gist I'm getting is that 
we have two contradictory interests:
(1.)  .toString(), .toHash(), .opCmp(), .opEquals(), should be 
const/pure/nothrow because their operations are inherently 
const/pure/nothrow and it would be both unintuitive and less reusable if 
they weren't.
(2.)  Marking these as const/pure/nothrow prevents caching and 
optimizations that are important for real-world code.


When I see a dichotomy like this forming, I have to think that we're 
missing something.  There is definitely a better way!  I, for one, 
wouldn't give up until it's found.




So I'll toss out an idea:

I think the const we want is a kind of interface const rather than an 
implementation const.  Interface const means that calling the method 
will not cause any /observable/ state-changes in the referred object. 
Implementation const is stricter: it means that calling the method will 
not cause ANY state-changes in the referred object at all.


I am going to be fairly strict about my notion of observable.  Changes 
to private members are observable:


class Foo
{
private string strCache = null;

// The isCaching method makes strCache observable.
public bool isCaching()
{
if ( strCache is null )
return false;
else
return true;
}

public string toString()
{
if ( strCache is null )
{
// Observable change in strCache!
// ... because isCaching reveals it
//   to everyone.
strCache = someComplicatedCalculation();
return strCache;
}
else
return strCache;
}
}


An idea I thought of is to introduce a method local declaration that 
allows a method to access instance-specific-state that isn't accessible 
to the rest of the class:


class Foo
{
// The isCaching method is no longer possible.

public pure nothrow string toString() const
{
// strCache gets stored in an instance of Foo
// strCache is only accessable in this method body.
@instance string strCache = null;

if ( strCache is null )
{
// Observable change in strCache!
// ... because isCaching reveals it
//   to everyone.
strCache = someComplicatedCalculation();
return strCache;
}
else
return strCache;
}
}

Now it is not possible (or at least, not very easy at all) for the 
statefulness of strCache to leak into the rest of the class (or 
program).  It is not observable.  If the implementor does their 
caching wrong, then the statefulness might be observable from the 
toString method, but nowhere else (except for methods that call 
toString).  It's not a perfect situation, but it's /a lot/ better.  We 
may be required to trust the implementor a little bit and assume that 
they know how to make sure strCache's statefulness isn't observable (ex: 
two calls to toString() should return the same results).


To communicate intents to invalidate the cache:

class Foo
{
private toStringCacheValid = false;

public void methodThatInvalidatesCache()
{
...
toStringCacheValid = false;
}

public pure nothrow string toString() const
{
// strCache gets stored in an instance of Foo

Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Christophe
Chad J , dans le message (digitalmars.D:167461), a écrit :
 An idea I thought of is to introduce a method local declaration that 
 allows a method to access instance-specific-state that isn't accessible 
 to the rest of the class:

It is not good for caching results, since the cache often has to be 
erased when the object is modified.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chad J

On 05/15/2012 02:49 PM, Christophe wrote:

Chad J , dans le message (digitalmars.D:167461), a écrit :

An idea I thought of is to introduce a method local declaration that
allows a method to access instance-specific-state that isn't accessible
to the rest of the class:


It is not good for caching results, since the cache often has to be
erased when the object is modified.


I did outline a way to invalidate caches with this.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chris Cain

On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:
An idea I thought of is to introduce a method local declaration 
that allows a method to access instance-specific-state that 
isn't accessible to the rest of the class:


This is an interesting idea (as it seems to really try to keep
the state changes internal to the function, which can be seen as
how D handles purity)... however, it breaks the point of purity 
due to this:


pure nothrow hash_t toHash() const {
@instance hash_t bad = 0;
++bad;
return hashfn(field) + bad;
}

Now it violates everyone's definition of purity and we can no
longer make our sweet optimizations and reasoning about the code.
Sure, we could trust the programmers to not do this ... but
that's another debate entirely.


To communicate intents to invalidate the cache:

class Foo
{
private toStringCacheValid = false;

public void methodThatInvalidatesCache()
{
...
toStringCacheValid = false;
}

public pure nothrow string toString() const
{
// strCache gets stored in an instance of Foo
// strCache is only accessable in this method body.
@instance string strCache = null;

if ( !toStringCacheValid )
{
// Observable change in strCache!
// ... because isCaching reveals it
//   to everyone.
strCache = someComplicatedCalculation();
toStringCacheValid = true;
return strCache;
}
else
return strCache;
}
}


... and setting toStringCacheValid to true in toString violates
const, so this is absolutely not allowed. Sorry.


Maybe there's a solution, but I doubt the solution is something
the programmer can/should do completely transparently.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chad J

On 05/15/2012 03:32 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:

An idea I thought of is to introduce a method local declaration that
allows a method to access instance-specific-state that isn't
accessible to the rest of the class:


This is an interesting idea (as it seems to really try to keep
the state changes internal to the function, which can be seen as
how D handles purity)... however, it breaks the point of purity due to
this:

pure nothrow hash_t toHash() const {
@instance hash_t bad = 0;
++bad;
return hashfn(field) + bad;
}

Now it violates everyone's definition of purity and we can no
longer make our sweet optimizations and reasoning about the code.
Sure, we could trust the programmers to not do this ... but
that's another debate entirely.



Yes.  I intend to trust the programmers to not do this.

Otherwise we need to find some way to ensure that a function that alters 
external state will always return the same value as long as the rest of 
the program doesn't change the state it looks at.





To communicate intents to invalidate the cache:

class Foo
{
private toStringCacheValid = false;

public void methodThatInvalidatesCache()
{
...
toStringCacheValid = false;
}

public pure nothrow string toString() const
{
// strCache gets stored in an instance of Foo
// strCache is only accessable in this method body.
@instance string strCache = null;

if ( !toStringCacheValid )
{
// Observable change in strCache!
// ... because isCaching reveals it
// to everyone.
strCache = someComplicatedCalculation();
toStringCacheValid = true;
return strCache;
}
else
return strCache;
}
}


... and setting toStringCacheValid to true in toString violates
const, so this is absolutely not allowed. Sorry.



Yep, ya got me.



Maybe there's a solution, but I doubt the solution is something
the programmer can/should do completely transparently.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Era Scarecrow

On Tuesday, 15 May 2012 at 21:18:11 UTC, Chad J wrote:

On 05/15/2012 03:32 PM, Chris Cain wrote:
Yep, ya got me.



Maybe there's a solution, but I doubt the solution is something
the programmer can/should do completely transparently.


 Perhaps an alternate workaround... a thought coming to mind, is 
that the constructor for a const object lets you set it once in 
the constructor (but not touch it again after) So Maybe...?


class X {
  uint cached_hash;

  this() {

cached_hash = X.toHash(this); //last line, only if we can 
send 'this'

  }

  static uint toHash(const X x) {
return hashedResult;
  }

  uint toHash(){
return X.toHash(this);
  }

  uint toHash() const {
return hash;
  }
}

 Although there's a lot there, with a const and non-const version 
the const version is optimized and always returned the proper 
precalculated hash, and if it isn't it creates it on the fly as 
appropriate.


 You'd almost say an interface you can attach would be the way to 
get the same basic functionality with minimal effort. So maybe... 
Perhaps a template is better used. Mmm


interface CachedHash(T) {
  private uint cachedHash;

  //your hashing function
  static uint toHash(T);

  //call from constructor
  private void setHash() {
cachedHash = T.toHash(this);
  }
  uint toHash() const {
return cachedHash;
  }

  override uint toHash() {
return T.toHash(this);
  }
}

 You should get the basic idea. Same thing would be done for 
toString with a cached result.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chris Cain

On Tuesday, 15 May 2012 at 21:18:11 UTC, Chad J wrote:

On 05/15/2012 03:32 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:
An idea I thought of is to introduce a method local 
declaration that

allows a method to access instance-specific-state that isn't
accessible to the rest of the class:


This is an interesting idea (as it seems to really try to keep
the state changes internal to the function, which can be seen 
as
how D handles purity)... however, it breaks the point of 
purity due to

this:

pure nothrow hash_t toHash() const {
@instance hash_t bad = 0;
++bad;
return hashfn(field) + bad;
}

Now it violates everyone's definition of purity and we can no
longer make our sweet optimizations and reasoning about the 
code.

Sure, we could trust the programmers to not do this ... but
that's another debate entirely.



Yes.  I intend to trust the programmers to not do this.

Otherwise we need to find some way to ensure that a function 
that alters external state will always return the same value as 
long as the rest of the program doesn't change the state it 
looks at.


It still wouldn't work though. Your @instance variables couldn't
be stored with the object (and, thus, would have to be a pointer
to mutable memory). So it'd require some backend work to make
sure that's even feasible (it is, you could have an immutable
pointer to a mutable pointer to the int, but let's face it:
that's further spitting in the face of the way D's type system
has been designed).

So, you'd have invisible indirections, additional complexity, and
you'd have to trust the programmers to be responsible. In other
words, C++ + the slowness of indirections.

On Tuesday, 15 May 2012 at 22:33:56 UTC, Era Scarecrow wrote:
 Perhaps an alternate workaround... a thought coming to mind, 
is that the constructor for a const object lets you set it once 
in the constructor (but not touch it again after) So 
Maybe...?


This is a good approach, but I think a lot of people want 
something that's lazy ... if they don't need a hash, they don't 
want it to be calculated for them.



FWIW, my idea: the thing we really need to do is to RD some
design patterns  idioms for D. There's solutions for using const
and caching and such, but formalizing them and working out the
kinks to make sure it's optimal for everyone's circumstances
would be helpful. And people will have to accept that C++'s
particular idioms can't be used with D (and vice versa ... some
things you can do easily in D is infeasible or incorrect/invalid
with C++).

Book idea? :) I'd do it, but I'm just a student, so I haven't
seen even a decent subset of all possible software engineering
problems.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chad J

On 05/15/2012 06:41 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 21:18:11 UTC, Chad J wrote:

On 05/15/2012 03:32 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 18:07:12 UTC, Chad J wrote:

An idea I thought of is to introduce a method local declaration that
allows a method to access instance-specific-state that isn't
accessible to the rest of the class:


This is an interesting idea (as it seems to really try to keep
the state changes internal to the function, which can be seen as
how D handles purity)... however, it breaks the point of purity due to
this:

pure nothrow hash_t toHash() const {
@instance hash_t bad = 0;
++bad;
return hashfn(field) + bad;
}

Now it violates everyone's definition of purity and we can no
longer make our sweet optimizations and reasoning about the code.
Sure, we could trust the programmers to not do this ... but
that's another debate entirely.



Yes. I intend to trust the programmers to not do this.

Otherwise we need to find some way to ensure that a function that
alters external state will always return the same value as long as the
rest of the program doesn't change the state it looks at.


It still wouldn't work though. Your @instance variables couldn't
be stored with the object (and, thus, would have to be a pointer
to mutable memory). So it'd require some backend work to make
sure that's even feasible (it is, you could have an immutable
pointer to a mutable pointer to the int, but let's face it:
that's further spitting in the face of the way D's type system
has been designed).

So, you'd have invisible indirections, additional complexity, and
you'd have to trust the programmers to be responsible. In other
words, C++ + the slowness of indirections.



The idea /was/ to store the @instance variable with the object 
specifically to avoid complex indirections.  Still have to trust 
programmers though :/


Otherwise it's better to just memoize things using external lookups and 
whatnot.  That solution does have complex indirections, but it doesn't 
require trusting the programmer and it exists already.



On Tuesday, 15 May 2012 at 22:33:56 UTC, Era Scarecrow wrote:

Perhaps an alternate workaround... a thought coming to mind, is that
the constructor for a const object lets you set it once in the
constructor (but not touch it again after) So Maybe...?


This is a good approach, but I think a lot of people want something
that's lazy ... if they don't need a hash, they don't want it to be
calculated for them.


FWIW, my idea: the thing we really need to do is to RD some
design patterns  idioms for D. There's solutions for using const
and caching and such, but formalizing them and working out the
kinks to make sure it's optimal for everyone's circumstances
would be helpful. And people will have to accept that C++'s
particular idioms can't be used with D (and vice versa ... some
things you can do easily in D is infeasible or incorrect/invalid
with C++).

Book idea? :) I'd do it, but I'm just a student, so I haven't
seen even a decent subset of all possible software engineering
problems.





Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chris Cain

On Tuesday, 15 May 2012 at 23:36:38 UTC, Chad J wrote:
The idea /was/ to store the @instance variable with the object 
specifically to avoid complex indirections.  Still have to 
trust programmers though :/


But you /can't/ store the @instance variable with the object. As 
per the language reference, immutables may be stored in ROM. If 
the object is immutable (and const objects might be immutable), 
then the @instance variable would be stored in ROM, in which case 
it physically couldn't change no matter how hard you tried (or it 
would vomit run-time errors or some undefined behavior). The only 
workable solution would be an immutable pointer to the mutable 
variable.


Link: http://dlang.org/const3.html



Re: Getting the const-correctness of Object sorted once and for all

2012-05-15 Thread Chad J

On 05/15/2012 08:18 PM, Chris Cain wrote:

On Tuesday, 15 May 2012 at 23:36:38 UTC, Chad J wrote:

The idea /was/ to store the @instance variable with the object
specifically to avoid complex indirections. Still have to trust
programmers though :/


But you /can't/ store the @instance variable with the object. As per the
language reference, immutables may be stored in ROM. If the object is
immutable (and const objects might be immutable), then the @instance
variable would be stored in ROM, in which case it physically couldn't
change no matter how hard you tried (or it would vomit run-time errors
or some undefined behavior). The only workable solution would be an
immutable pointer to the mutable variable.

Link: http://dlang.org/const3.html



I guess this matters for toHash... so now we not only want 
pure/const/nothrow methods, but there is this implication that we are 
desiring to mutate immutable things.  If we really wanted to overcome 
this, we could have things that behave as if immutable, but carry 
mutable state internally.  These would not be eligible for placement in 
ROM.


The other thing that I'm wondering about is if there are use-cases 
/besides/ hashing.  Are there any?  If not, it might be worth 
special-casing somehow.  It's a difficult route to travel though, 
because it's very difficult to know that there won't be any roadblocks 
besides caching in the future.  Still, I suspect this is not well 
researched.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Mehrdad

On Monday, 14 May 2012 at 03:19:57 UTC, Jonathan M Davis wrote:
I suspect that the folks who are looking for absolutely every 
CPU cycle and want caching and lazy-loading in their types


It's not a CPU cycle issue.

Caching/lazy-loading can make the difference between a window 
that freezes, and a window that doesn't.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Mehrdad

On Monday, 14 May 2012 at 04:10:30 UTC, Chris Cain wrote:
Let me ask you something: Could you try to name 2 or 3 good 
things about const/immutable without looking it up? If not, 
you've really not given enough effort to learning about the 
benefits of it.



(1) Compiler helps you write correct multithreaded code
(2) You help compiler perform optimizations based on contracts
(3) I don't think there exists a #3 that's very different from #1 
and #2





I'm not saying const is bad (at least, that's not what I'm saying 
*here*).


What I'm saying is that it's being forced upon the user, and it's 
becoming unavoidable. Which means people who don't like it won't 
use D.


Kind of like the GC.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Walter Bright

On 5/13/2012 11:09 PM, Mehrdad wrote:

On Monday, 14 May 2012 at 04:10:30 UTC, Chris Cain wrote:

Let me ask you something: Could you try to name 2 or 3 good things about
const/immutable without looking it up? If not, you've really not given enough
effort to learning about the benefits of it.



(1) Compiler helps you write correct multithreaded code
(2) You help compiler perform optimizations based on contracts
(3) I don't think there exists a #3 that's very different from #1 and #2



#3 Improves self-documentation of code - it's more understandable and less 
susceptible to breakage during maintenance.


#4 Improves encapsulation

#5 Makes function purity possible


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 08:26, Walter Bright wrote:

On 5/13/2012 11:09 PM, Mehrdad wrote:

On Monday, 14 May 2012 at 04:10:30 UTC, Chris Cain wrote:

Let me ask you something: Could you try to name 2 or 3 good things about
const/immutable without looking it up? If not, you've really not
given enough
effort to learning about the benefits of it.



(1) Compiler helps you write correct multithreaded code
(2) You help compiler perform optimizations based on contracts
(3) I don't think there exists a #3 that's very different from #1 and #2



#3 Improves self-documentation of code - it's more understandable and
less susceptible to breakage during maintenance.

#4 Improves encapsulation


I'm not sure I would agree on this one. const/immutable arguably leak 
implementation details more than they encapsulate them.




#5 Makes function purity possible


--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Walter Bright

On 5/13/2012 10:34 PM, Alex Rønne Petersen wrote:

I have yet to see any compiler make sensible use of the information provided by
both C++'s const and D's const.


D's const is part of purity, which is optimizable.


const in particular is completely useless to an optimizer because it does not
give it any information that it can use for anything. The kind of information
that an optimization pass, in general, wants to see is whether something is
guaranteed to *never* change. const does not provide this information. const
simply guarantees that the code working on the const data cannot alter it (but
at the same time allows *other* code to alter it), which, as said, is useless to
the optimizer.

immutable is a different story. immutable actually opens the door to many
optimization opportunities exactly because the optimizer knows that the data
will not be altered, ever. This allows it to (almost) arbitrarily reorder code,
fold many computations at compile time, do conditional constant propagation,
dead code elimination, ...


You cannot have immutable without also having const. Or, at least, it would be 
impractical.



This seems reasonable. But now consider that the majority of functions *are
written for const, not immutable*. Thereby, you're throwing away the immutable
guarantee, which is what the *compiler* (not the *programmer*) cares about.
immutable is an excellent idea in theory, but in practice, it doesn't help the
compiler because you'd have to either

a) templatize all functions operating on const/immutable data so the compiler
can retain the immutable guarantee when the input is such, or
b) explicitly duplicate code for the const and the immutable case.


strings are immutable, not just const. It's been very successful.



Both approaches clearly suck. Templates don't play nice with polymorphism, and
code duplication is...well...duplication. So, most of druntime and phobos is
written for const because const is the bridge between the mutable and immutable
world, and writing code against that rather than explicitly against
mutable/immutable data is just simpler. But this completely ruins any
opportunity the compiler has to optimize!


That isn't true when it comes to purity.



(An interesting fact is that even the compiler engineers working on compilers
for strictly pure functional languages have yet to take full advantage of the
potential that a pure, immutable world offers. If *they* haven't done it yet, I
don't think we're going to do it for a long time to come.)


It isn't just what the compiler can do, purity and immutability offer a means to 
prove things about code.



Now, you might argue that the compiler could simply say okay, this data is
const, which means it cannot be changed in this particular piece of code and
thus nowhere else, since it is not explicitly shared, and therefore not touched
by any other threads. This would be great if shared wasn't a complete design
fallacy. Unfortunately, in most real world code, shared just doesn't cut it, and
data is often shared between threads without using the shared qualifier
(__gshared is one example).


Yes, if you're thinking like a C programmer!



shared is another can of worms entirely. I can list a few initial reasons why
it's unrealistic and impractical:

1) It is extremely x86-biased; implementing it on other architectures is going
to be...interesting (read: on many architectures, impossible at ISA level).


I don't see why.


2) There is no bridge between shared and unshared like there is for mutable and
immutable. This means that all code operating on shared data has to be
templatized (no, casts will not suffice; the compiler can't insert memory
barriers then) or code has to be explicitly duplicated for the shared and
unshared case. Funnily, the exact same issue mentioned above for const and
immutable!


Frankly, you're doing it wrong if you're doing more than trivial things with 
shared types. Running an algorithm on a shared type is just a bad idea.



3) It only provides documentation value. The low-level atomicity that it is
supposed to provide (but doesn't yet...) is of extremely questionable value. In
my experience, I never actually access shared data from multiple threads
simultaneously, but rather, transfer the data from one thread to another and use
it exclusively in the other thread (i.e. handing over the ownership). In such
scenarios, shared just adds overhead (memory barriers are Bad (TM) for
performance).


Transferring data between threads should be done either using value types, which 
are copied, or references which are typed as shared only transitorially.




Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Jakob Ovrum

On Sunday, 13 May 2012 at 17:02:46 UTC, Stewart Gordon wrote:

On 13/05/2012 17:41, Alex Rønne Petersen wrote:
snip
I agree with everything but toString(). I'm afraid that 
forcing toString() to be const
will have harm flexibility severely. Can't we do better, 
somehow?


How exactly?

If you're talking about memoization, it ought to be possible to 
make use of std.functional.memoize to implement it.


Otherwise, using toString to change the state of an object is 
bending semantics.  If you want a method to generate a string 
representation of an object in a way that might do this, then 
create your own method to do it.


Stewart.


How about logically constant opEquals, toString etc? Currently, 
this is perfectly possible by just *not using const*. Logical 
constancy goes beyond memoization.


For example, take this function:

http://jakobovrum.github.com/LuaD/luad.base.html#LuaObject.opEquals

It cannot be const because Lua instances are state machines. The 
function pushes the this-object to the Lua stack, then it pushes 
the object to compare to, then it uses a Lua API comparison 
function which pops the two objects and pushes the comparison 
result. The result is then popped off the stack and returned as a 
boolean.


The function is logically constant, hence it does not use D's 
const, which should be a completely valid choice to make.




Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Era Scarecrow
On Monday, 14 May 2012 at 05:31:01 UTC, H. S. Teoh wrote: That's 
part of the reason I didn't really get comfortable with D 
programming until I bought TDPL -- I needed to learn D from 
scratch, as it were, to think about it from a fresh perspective 
instead of bringing along my years of C/C++ baggage. For that, 
looking at a bunch of online reference docs didn't help: you're 
just learning the vocabulary, as it were, and not really 
thinking in the language. As any foreign language learner 
knows, you will never speak the language well if you just keep 
translating from your native language; you have to learn to 
think in that language. I needed to read through TDPL like a 
newbie in order to learn to write D the way it's supposed to be 
written.


Once I started doing that, many things began to make a lot more 
sense.


 Exactly! If you don't have a good concise enough overview or 
explanation of the language and features you end up with 
guesswork from all over the place. Thankfully my only OO 
experience beyond D2 has been Java, and that only helped me 
understand polymorphism and interfaces, giving me what I needed 
without cluttering me with the necessaries of the language. I am 
perhaps as 'unlearned' as I can be, aside from doing some ASM/C 
work (and going away from pointer and - manipulation is nice :) )


 I'm still trying to find a good OO book that will teach me how 
to think and build with OOP properly. I'm probably only half as 
effective as I could be. If you have any good book 
recommendations I'll try and get ahold of them.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Walter Bright

On 5/13/2012 11:39 PM, Era Scarecrow wrote:

I'm still trying to find a good OO book that will teach me how to think and
build with OOP properly. I'm probably only half as effective as I could be. If
you have any good book recommendations I'll try and get ahold of them.


The classic is Object-Oriented Programming by Bertrand Meyer.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Mehrdad

On Monday, 14 May 2012 at 06:27:15 UTC, Walter Bright wrote:
#3 Improves self-documentation of code - it's more 
understandable and less susceptible to breakage during 
maintenance.


#3 is also valid for C++, so I wasn't exactly considering that.


#4 Improves encapsulation


o.O How does it improve encapsulation?


#5 Makes function purity possible


Ah yea, this one I forgot.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 08:37, Walter Bright wrote:

On 5/13/2012 10:34 PM, Alex Rønne Petersen wrote:

I have yet to see any compiler make sensible use of the information
provided by
both C++'s const and D's const.


D's const is part of purity, which is optimizable.


A function can still be weakly pure and operating on const arguments, 
meaning you *still* have no opportunity for optimization. Strongly pure 
functions are easily optimizable - because they operate on immutable 
data (or, at least, data with shared indirection).





const in particular is completely useless to an optimizer because it
does not
give it any information that it can use for anything. The kind of
information
that an optimization pass, in general, wants to see is whether
something is
guaranteed to *never* change. const does not provide this information.
const
simply guarantees that the code working on the const data cannot alter
it (but
at the same time allows *other* code to alter it), which, as said, is
useless to
the optimizer.

immutable is a different story. immutable actually opens the door to many
optimization opportunities exactly because the optimizer knows that
the data
will not be altered, ever. This allows it to (almost) arbitrarily
reorder code,
fold many computations at compile time, do conditional constant
propagation,
dead code elimination, ...


You cannot have immutable without also having const. Or, at least, it
would be impractical.


I agree entirely. I'm just saying that the way it is in the language 
right now doesn't make it easy for the compiler to optimize for 
immutable at all, due to how programmers tend to program against it.





This seems reasonable. But now consider that the majority of functions
*are
written for const, not immutable*. Thereby, you're throwing away the
immutable
guarantee, which is what the *compiler* (not the *programmer*) cares
about.
immutable is an excellent idea in theory, but in practice, it doesn't
help the
compiler because you'd have to either

a) templatize all functions operating on const/immutable data so the
compiler
can retain the immutable guarantee when the input is such, or
b) explicitly duplicate code for the const and the immutable case.


strings are immutable, not just const. It's been very successful.


And yet, the majority of functions operate on const(char)[], not 
immutable(char)[], thereby removing the guarantee that string was 
supposed to give about immutability.






Both approaches clearly suck. Templates don't play nice with
polymorphism, and
code duplication is...well...duplication. So, most of druntime and
phobos is
written for const because const is the bridge between the mutable and
immutable
world, and writing code against that rather than explicitly against
mutable/immutable data is just simpler. But this completely ruins any
opportunity the compiler has to optimize!


That isn't true when it comes to purity.


I don't follow. Can you elaborate?





(An interesting fact is that even the compiler engineers working on
compilers
for strictly pure functional languages have yet to take full advantage
of the
potential that a pure, immutable world offers. If *they* haven't done
it yet, I
don't think we're going to do it for a long time to come.)


It isn't just what the compiler can do, purity and immutability offer a
means to prove things about code.


Absolutely, and I think that has significant value. Keep in mind that I 
am only contesting the usefulness of const in terms of optimizations in 
a normal compiler.





Now, you might argue that the compiler could simply say okay, this
data is
const, which means it cannot be changed in this particular piece of
code and
thus nowhere else, since it is not explicitly shared, and therefore
not touched
by any other threads. This would be great if shared wasn't a complete
design
fallacy. Unfortunately, in most real world code, shared just doesn't
cut it, and
data is often shared between threads without using the shared qualifier
(__gshared is one example).


Yes, if you're thinking like a C programmer!


Or if you're doing low-level thread programming (in my case, for a 
virtual machine).






shared is another can of worms entirely. I can list a few initial
reasons why
it's unrealistic and impractical:

1) It is extremely x86-biased; implementing it on other architectures
is going
to be...interesting (read: on many architectures, impossible at ISA
level).


I don't see why.


Some architectures with weak memory models just plain don't have fence 
instructions.





2) There is no bridge between shared and unshared like there is for
mutable and
immutable. This means that all code operating on shared data has to be
templatized (no, casts will not suffice; the compiler can't insert memory
barriers then) or code has to be explicitly duplicated for the shared and
unshared case. Funnily, the exact same issue mentioned above for const
and
immutable!


Frankly, you're doing it wrong if you're doing more than trivial 

Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Stewart Gordon

On 13/05/2012 23:50, Jonathan M Davis wrote:
snip

Caching and lazy
evaluation _will_ be impossible in those functions without breaking the type
system.


Unless stuff is added to the type system to accommodate it.

For example, a type modifier that adds a set flag.  When unset, a const method cannot 
read it (to do so would throw an AssertError or similar), but can assign to it, at which 
point it becomes set.  When set, it acts just like any member (non-const methods have 
read-write access, const methods have read-only access, etc.).  Non-const methods can also 
unset it, which they would do if they change the state of the object in a way that 
invalidates the cached value.


Alternatively, you could argue that it's the compiler's job to implement caching as an 
optimisation for pure methods.  But it would have to implement the logic to decache it 
when relevant state of the object changes, which could get complicated if you want to do 
it efficiently.



Anything that absolutely requires them will probably have to either
have to break the type system or use _other_ functions with the same
functionality but without those attributes.

snip

I think that's half the point of std.functional.memoize - to be such a function for you to 
use when you want it.


Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer
On Mon, 14 May 2012 02:35:16 -0400, Jakob Ovrum jakobov...@gmail.com  
wrote:



On Sunday, 13 May 2012 at 17:02:46 UTC, Stewart Gordon wrote:

On 13/05/2012 17:41, Alex Rønne Petersen wrote:
snip
I agree with everything but toString(). I'm afraid that forcing  
toString() to be const

will have harm flexibility severely. Can't we do better, somehow?


How exactly?

If you're talking about memoization, it ought to be possible to make  
use of std.functional.memoize to implement it.


Otherwise, using toString to change the state of an object is bending  
semantics.  If you want a method to generate a string representation of  
an object in a way that might do this, then create your own method to  
do it.


Stewart.


How about logically constant opEquals, toString etc? Currently, this is  
perfectly possible by just *not using const*. Logical constancy goes  
beyond memoization.


This means you cannot compare two const objects.

The issue is, non-const opEquals makes sense on some objects, and const  
opEquals makes sense on others.  However, you must make them all come  
together in Object.opEquals.


I think we already have the hooks to properly compare objects without  
requiring Object.opEquals.


Right now, when two objects are compared, the compiler calls  
object.opEquals (that's little o for object, meaning the module function  
*not* the class method).


So why can't object.opEquals be a template that decides whether to use  
Object.opEquals (which IMO *must be* const) or a derived version?  I don't  
think it's that much of a stretch (not sure if we need a compiler fix for  
this).


-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread bearophile

Alex Rønne Petersen:

But how would you memoize the value in the instance of the 
object if it's const?


I opened a thread on such matters:
http://forum.dlang.org/thread/gtpdmrfektaygfmec...@forum.dlang.org

---

Jonathan M Davis:

Caching and lazy evaluation _will_ be impossible in those 
functions without breaking the type system.


Take a look at the articles I've linked.

Bye,
bearophile


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer
On Sun, 13 May 2012 16:52:15 -0400, Dmitry Olshansky  
dmitry.o...@gmail.com wrote:



On 14.05.2012 0:48, Stewart Gordon wrote:

On 13/05/2012 20:42, Walter Bright wrote:
snip

I'd like to see std.stream dumped. I don't see any reason for it to
exist that std.stdio
doesn't do (or should do).


So std.stdio.File is the replacement for the std.stream stuff?

How does/will it provide all the different kinds of stream that
std.stream provides, as well as the other kinds of stream that
applications will need?



I think I've seen proper replacement (or rather a draft of it). If only  
Steven can be bothered to finish it :)


Yes, I know.

I hate starting things and then not finishing them.  Especially when I've  
got so much of it completed...


I'm going to make some time to finish this.  I need probably a good few  
days (of solid time).  Which means, based on my normal schedule, 2-3  
weeks.  Most of the difficult parts are complete, I have a working buffer  
implementation, fast unicode translation (meaning UTF-8, UTF-16, UTF-16LE,  
UTF-32, and UTF-32LE), and a path to use RAII for seamless integration  
with std.stdio.File.  I even have preliminary agreement from Andrei and  
Walter on a design (no guarantees they accept the final product, but I  
think I can massage it into acceptance).


The one last puzzle to solve is sharing.  File is this half-breed of  
sharing, because it contains a FILE *, which is a shared type, but File is  
not.  Then it does some casting to get around the problems.  We need a  
better solution than this, but shared is so difficult to use, I think I'm  
going to have to implement something similar.  It has been stipulated by  
Walter and Andrei that fixing this shared situation is a requirement for  
any new replacement.  I have some ideas, but I have to play around to see  
if they actually work and make sense.


-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Dmitry Olshansky

On 14.05.2012 16:37, Steven Schveighoffer wrote:

On Sun, 13 May 2012 16:52:15 -0400, Dmitry Olshansky
dmitry.o...@gmail.com wrote:


On 14.05.2012 0:48, Stewart Gordon wrote:

On 13/05/2012 20:42, Walter Bright wrote:
snip

I'd like to see std.stream dumped. I don't see any reason for it to
exist that std.stdio
doesn't do (or should do).


So std.stdio.File is the replacement for the std.stream stuff?

How does/will it provide all the different kinds of stream that
std.stream provides, as well as the other kinds of stream that
applications will need?



I think I've seen proper replacement (or rather a draft of it). If
only Steven can be bothered to finish it :)


Yes, I know.

I hate starting things and then not finishing them. Especially when I've
got so much of it completed...

I'm going to make some time to finish this. I need probably a good few
days (of solid time). Which means, based on my normal schedule, 2-3
weeks. Most of the difficult parts are complete, I have a working buffer
implementation, fast unicode translation (meaning UTF-8, UTF-16,
UTF-16LE, UTF-32, and UTF-32LE), and a path to use RAII for seamless
integration with std.stdio.File. I even have preliminary agreement from
Andrei and Walter on a design (no guarantees they accept the final
product, but I think I can massage it into acceptance).



Great!


The one last puzzle to solve is sharing. File is this half-breed of
sharing, because it contains a FILE *, which is a shared type, but File
is not. Then it does some casting to get around the problems.


And  it all went nice and well till I spotted FILE*. Boom.
I mean I half assumed we are going to drop this crutch. Yep, too harsh. 
So probably leave it as *one* of file backends that features 
compatibility with C. Because IMHO using C-runtime this way is not 
buying us anything else other then C compatibility and welcome to the 
world of unportable hacks where performance matters.


We need a

better solution than this, but shared is so difficult to use, I think
I'm going to have to implement something similar. It has been stipulated
by Walter and Andrei that fixing this shared situation is a requirement
for any new replacement. I have some ideas, but I have to play around to
see if they actually work and make sense.


Probably it worths checking how std.stdio does it.

--
Dmitry Olshansky


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread John

On Monday, 14 May 2012 at 06:04:33 UTC, Mehrdad wrote:

On Monday, 14 May 2012 at 03:19:57 UTC, Jonathan M Davis wrote:
I suspect that the folks who are looking for absolutely every 
CPU cycle and want caching and lazy-loading in their types


It's not a CPU cycle issue.

Caching/lazy-loading can make the difference between a window 
that freezes, and a window that doesn't.


You can still do this pretty easily.  You just have to cast away 
const.


You can even make it reusable.

mixin template TrustedToString() {
   const @safe pure nothrow string toString() {
  return unsafeToString();
   }
   final const @trusted pure nothrow string unsafeToString() {
  auto nonConstThis = cast(Unqual!(typeof(this)))this;
  return nonConstThis.trustedToString();
   }
}

class Foo {
 public:
   const @safe pure nothrow string toString() {
  return Foo;
   }
}

class Bar : Foo {
 public:
   mixin TrustedToString;
   final pure @trusted nothrow string trustedToString() {
  if (cachedString.length == 0) {
 cachedString = Bar;
  }
  return cachedString;
   }
   string cachedString;
}

unittest {
   auto f = new Foo();
   assert(f.toString() == Foo);

   auto b = new Bar();
   assert(b.toString() == Bar);
}


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer
On Mon, 14 May 2012 09:20:57 -0400, Dmitry Olshansky  
dmitry.o...@gmail.com wrote:



On 14.05.2012 16:37, Steven Schveighoffer wrote:



The one last puzzle to solve is sharing. File is this half-breed of
sharing, because it contains a FILE *, which is a shared type, but File
is not. Then it does some casting to get around the problems.


And  it all went nice and well till I spotted FILE*. Boom.
I mean I half assumed we are going to drop this crutch. Yep, too harsh.  
So probably leave it as *one* of file backends that features  
compatibility with C. Because IMHO using C-runtime this way is not  
buying us anything else other then C compatibility and welcome to the  
world of unportable hacks where performance matters.


I think the compromise agreed upon will be reasonable.  A constructed File  
will start out as a FILE * entity until you want to do anything more  
complex than writeln or readf.  At that point, it switches over to D-based  
backend automatically.  I don't want to be trying to implement advanced  
buffer-based techniques using FILE *, and I also don't want to restrict  
phobos' types to only doing things that FILE * can do well.  At the same  
time, we have this legacy with std.stdio.File that we have to maintain  
(the comments on my preliminary library were near-unanimous -- it cannot  
break existing code).


Walter has a very very hard requirement that D's equivalent stdout stdin,  
and stderr all interoperate with the C calls that use the equivalent C  
structures.  In other words, it is a hard requirement that writeln and  
printf interoperate.


Really, printf is the *only* reason to have this backwards compatibility  
feature, and I strongly wish we could get rid of it.


That being said, if you *want* to avoid FILE *, it will definitely be  
possible.



We need a

better solution than this, but shared is so difficult to use, I think
I'm going to have to implement something similar. It has been stipulated
by Walter and Andrei that fixing this shared situation is a requirement
for any new replacement. I have some ideas, but I have to play around to
see if they actually work and make sense.


Probably it worths checking how std.stdio does it.


I have.  I don't know how well it applies to my library.  One thing about  
using FILE * is that you have full knowledge of the known universe of FILE  
*.  This means you can control internally the interactions between  
threads, making sure that you don't have an unshared pointer unless the  
thing is locked (and this is what std.stdio does).


The problem is when you create an *extendable* system like the one I'm  
doing.  At that point, you can lock when you cast away shared, but you  
have no idea whether a method call is going to squirrel away an unshared  
reference to itself somewhere, so that when you go back to shared (and  
unlock), there's a leaked thread-local reference somewhere.


It may have to require documentation-based restrictions (i.e.  
not-compiler-enforced).  I haven't put enough thought into the possible  
means to do this.  Ideas are welcome!


-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Jonathan M Davis
On Monday, May 14, 2012 15:27:41 John wrote:
 On Monday, 14 May 2012 at 06:04:33 UTC, Mehrdad wrote:
  On Monday, 14 May 2012 at 03:19:57 UTC, Jonathan M Davis wrote:
  I suspect that the folks who are looking for absolutely every
  CPU cycle and want caching and lazy-loading in their types
  
  It's not a CPU cycle issue.
  
  Caching/lazy-loading can make the difference between a window
  that freezes, and a window that doesn't.
 
 You can still do this pretty easily.  You just have to cast away
 const.

But that breaks the type system.

http://stackoverflow.com/questions/4219600/logical-const-in-d

Yes, you _can_ do it, but it's not actually guaranteed to work (casting away 
const and mutating a variable is _undefined_), and if you do it with an object 
which is actually immutable, then things could go _very_ wrong (e.g. 
segfault).

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Timon Gehr

On 05/14/2012 06:10 AM, Chris Cain wrote:

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const,
because, practically speaking, it's being forced onto the programmers
by the language.


You're really against const in this language, huh?



I guess this is not the most important point.
He has been trying to use const like in OO-ish C++.
This just does not work, because D const is detrimental to OO
principles when used that way.
The proposal is about _enforcing_ C++-like usage of const.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Tove

On Monday, 14 May 2012 at 16:53:24 UTC, Timon Gehr wrote:

On 05/14/2012 06:10 AM, Chris Cain wrote:

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const,
because, practically speaking, it's being forced onto the 
programmers

by the language.


You're really against const in this language, huh?



I guess this is not the most important point.
He has been trying to use const like in OO-ish C++.
This just does not work, because D const is detrimental to OO
principles when used that way.
The proposal is about _enforcing_ C++-like usage of const.


but c++ has the 'mutable' keyword as an easy escape route... 
which saved me a bunch of times... guess one can emulate it with 
a library-solution using nested classes? But... what about 
structs?


class Outer
{
  int i = 6; // mutable

  class Inner {
int y=0;

int foo() const
{
  // ++y; // fail
  return ++i; // look ma, mutable const
}
  }
  Inner inner;
  this()
  {
inner = new Inner;
  }
  alias inner this;
}


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread deadalnix

Le 14/05/2012 00:56, Mehrdad a écrit :

On Sunday, 13 May 2012 at 22:51:05 UTC, Jonathan M Davis wrote:

Anything that absolutely requires them will probably have to either
have to break the type system or use _other_ functions with the same
functionality but without those attributes. In some cases though,
providing overloads which aren't const, pure, etc. should work though.
If you want it to be otherwise, you're going to have to convince
Walter, and I think that it's pretty clear that this is the way that
it's going to have to be thanks to how const et al. work.


This is *exactly* the sort of problem I was referring to in my
const(rant) thread, so to speak.

These const-related issues make D, simply speaking, *HARD TO USE*.
(Yes, that means even harder then C++ in some cases.)

When people say it's painful to find workarounds to problems in D, I
hope -- at the very least -- no one will be surprised as to why.


The only reason I'd see a toSting function as non pure or non const is 
memoize. It can be tackled with lib support in phobos.


What are other uses cases ?


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Andrej Mitrovic
On 5/14/12, Steven Schveighoffer schvei...@yahoo.com wrote:
 Really, printf is the *only* reason to have this backwards compatibility
 feature, and I strongly wish we could get rid of it.

printf is also unique in that it works when called in class
destructors, which is sometimes needed for debugging (unlike writef
which wants to allocate memory and then throws).


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Mehrdad

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:
The only reason I'd see a toSting function as non pure or non 
const is memoize. It can be tackled with lib support in phobos.

What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Alex Rønne Petersen

On 14-05-2012 21:04, Mehrdad wrote:

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:

The only reason I'd see a toSting function as non pure or non const is
memoize. It can be tackled with lib support in phobos.
What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


That's actually a very, very good point.

--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread deadalnix

Le 14/05/2012 04:47, Jonathan M Davis a écrit :

They can be in almost all cases. The problem is the folks who want to have
caching and/or lazy initiailization in their classes/structs. You can't cache
the result of any of those functions (toHash being the main target for it) if
they're const and pure except in overloads which _aren't_ const, making those
functions more expensive in comparison to what they could be if they weren't
required to be const. And lazy initialization becomes almost impossible,
because if the member variables needed in those functions haven't been
initialized yet when they're called, then you _can't_ initialize them. If
getting the value that it _would_ be without actually initializing the member
variable works, then you can do that if it hasn't been initialized, but if you
can't do that (e.g. the variable _must_ be set only once), then you're
screwed. And regardless of whether you can make it work with const, it _will_
be less efficient.



Lazy initialization is more a problem than a solution when it comes to 
multithreading. And I'm afraid it is the future.


BTW, nothing prevent to define toString an non const and another as 
const. The const one cannot cache the result, but certainly can read it. 
And every method that is aware of the non const version will use it.


This whole thing is overinflated. This isn't a problem. I'm pretty sure 
that most code that will broke was actually already a bad idea in the 
first place.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread deadalnix

Le 14/05/2012 21:04, Mehrdad a écrit :

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:

The only reason I'd see a toSting function as non pure or non const is
memoize. It can be tackled with lib support in phobos.
What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


I'd argue that hiding such an operation in toString or similar stuff is 
what you want to avoid.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread H. S. Teoh
On Mon, May 14, 2012 at 09:18:42PM +0200, deadalnix wrote:
[...]
 Lazy initialization is more a problem than a solution when it comes
 to multithreading. And I'm afraid it is the future.
 
 BTW, nothing prevent to define toString an non const and another as
 const. The const one cannot cache the result, but certainly can read
 it. And every method that is aware of the non const version will use
 it.
 
 This whole thing is overinflated. This isn't a problem. I'm pretty
 sure that most code that will broke was actually already a bad idea
 in the first place.

I agree. Existing stuff that use toString makes certain assumptions
about it, const being one of them. Things like toHash() falls back to
hashing the value of toString() if no other suitable default is found.
It would be a very very bad idea if accessing AA's start causing network
access, for example.

If you really, really, REALLY absolutely have to have non-const
toString, then just define one. Then code that is aware of the non-const
version will use it, and that code will actually be aware of the
consequences. It's a bad idea to do things like network access in the
const version of toString() when existing code that uses it (built-in
stuff like druntime) assumes this doesn't happen.


T

-- 
A linguistics professor was lecturing to his class one day. In
English, he said, A double negative forms a positive. In some
languages, though, such as Russian, a double negative is still a
negative. However, there is no language wherein a double positive can
form a negative. A voice from the back of the room piped up, Yeah,
yeah.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer
On Mon, 14 May 2012 15:07:30 -0400, Andrej Mitrovic  
andrej.mitrov...@gmail.com wrote:



On 5/14/12, Steven Schveighoffer schvei...@yahoo.com wrote:

Really, printf is the *only* reason to have this backwards compatibility
feature, and I strongly wish we could get rid of it.


printf is also unique in that it works when called in class
destructors, which is sometimes needed for debugging (unlike writef
which wants to allocate memory and then throws).


That's an excellent point.  But what a really mean is, I wish we could get  
rid of the requirement for interoperability between printf and writef.


Of course, we couldn't get rid of printf, it's part of the C runtime!

-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer

On Mon, 14 May 2012 13:08:06 -0400, Tove t...@fransson.se wrote:


On Monday, 14 May 2012 at 16:53:24 UTC, Timon Gehr wrote:

On 05/14/2012 06:10 AM, Chris Cain wrote:

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const,
because, practically speaking, it's being forced onto the programmers
by the language.


You're really against const in this language, huh?



I guess this is not the most important point.
He has been trying to use const like in OO-ish C++.
This just does not work, because D const is detrimental to OO
principles when used that way.
The proposal is about _enforcing_ C++-like usage of const.


but c++ has the 'mutable' keyword as an easy escape route... which saved  
me a bunch of times... guess one can emulate it with a library-solution  
using nested classes? But... what about structs?


class Outer
{
   int i = 6; // mutable

   class Inner {
 int y=0;

 int foo() const
 {
   // ++y; // fail
   return ++i; // look ma, mutable const
 }
   }
   Inner inner;
   this()
   {
 inner = new Inner;
   }
   alias inner this;
}


I have never seen this suggested before.  I would guess that it might be
considered a bug, since you are accessing the outer instance via a pointer
contained in the inner instance.

But I like the approach (even if it is a bug)!

-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Mehrdad

On Monday, 14 May 2012 at 19:13:30 UTC, deadalnix wrote:

Le 14/05/2012 21:04, Mehrdad a écrit :

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:
The only reason I'd see a toSting function as non pure or non 
const is

memoize. It can be tackled with lib support in phobos.
What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


I'd argue that hiding such an operation in toString or similar 
stuff is what you want to avoid.


Uhm, I beg to differ...

What, exactly, is wrong with asking a non-const object for your 
toString() information?


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer

On Mon, 14 May 2012 15:23:59 -0400, Steven Schveighoffer
schvei...@yahoo.com wrote:

On Mon, 14 May 2012 15:07:30 -0400, Andrej Mitrovic  
andrej.mitrov...@gmail.com wrote:



On 5/14/12, Steven Schveighoffer schvei...@yahoo.com wrote:
Really, printf is the *only* reason to have this backwards  
compatibility

feature, and I strongly wish we could get rid of it.


printf is also unique in that it works when called in class
destructors, which is sometimes needed for debugging (unlike writef
which wants to allocate memory and then throws).


That's an excellent point.  But what a really mean is, I wish we could  
get rid of the requirement for interoperability between printf and  
writef.


Of course, we couldn't get rid of printf, it's part of the C runtime!


Oh, and also, we should fix that problem (that writef allocates).
However, I think we need DIP9 in order to do that.
http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP9

-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Steven Schveighoffer

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


On 14-05-2012 21:04, Mehrdad wrote:

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:

The only reason I'd see a toSting function as non pure or non const is
memoize. It can be tackled with lib support in phobos.
What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


That's actually a very, very good point.



Yes, the use case for logical const is when an object *doesn't own* a
reference.  I proposed a logical const solution a long time ago that
called it nostate instad of mutable.

But there is a really big problem with logical const that makes it not
seem worth the trouble  of having it -- the syntax.  You would need a lot
of keywords and/or features to correctly describe relationships between
objects.  I just don't think people would tolerate it.

Hm... I just had a really good idea (I will divulge it in another post) on
how to implement logical const without altering the language and/or
compiler.

-Steve


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread deadalnix

Le 14/05/2012 21:24, Steven Schveighoffer a écrit :

On Mon, 14 May 2012 13:08:06 -0400, Tove t...@fransson.se wrote:


On Monday, 14 May 2012 at 16:53:24 UTC, Timon Gehr wrote:

On 05/14/2012 06:10 AM, Chris Cain wrote:

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const,
because, practically speaking, it's being forced onto the programmers
by the language.


You're really against const in this language, huh?



I guess this is not the most important point.
He has been trying to use const like in OO-ish C++.
This just does not work, because D const is detrimental to OO
principles when used that way.
The proposal is about _enforcing_ C++-like usage of const.


but c++ has the 'mutable' keyword as an easy escape route... which
saved me a bunch of times... guess one can emulate it with a
library-solution using nested classes? But... what about structs?

class Outer
{
int i = 6; // mutable

class Inner {
int y=0;

int foo() const
{
// ++y; // fail
return ++i; // look ma, mutable const
}
}
Inner inner;
this()
{
inner = new Inner;
}
alias inner this;
}


I have never seen this suggested before. I would guess that it might be
considered a bug, since you are accessing the outer instance via a pointer
contained in the inner instance.

But I like the approach (even if it is a bug)!

-Steve


Yes, you have similar bugs with delegates. This is a problem.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread deadalnix

Le 14/05/2012 21:25, Mehrdad a écrit :

On Monday, 14 May 2012 at 19:13:30 UTC, deadalnix wrote:

Le 14/05/2012 21:04, Mehrdad a écrit :

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:

The only reason I'd see a toSting function as non pure or non const is
memoize. It can be tackled with lib support in phobos.
What are other uses cases ?


Not necessarily 'memoization' -- you could be instead querying
another object for this information, which may not be const.
(Say, a network connection.)


I'd argue that hiding such an operation in toString or similar stuff
is what you want to avoid.


Uhm, I beg to differ...

What, exactly, is wrong with asking a non-const object for your
toString() information?


Nothing, expect it make it impossible to get that information on 
const/immutables objects.


Additionnaly, this operation isn't supposed to modify the object in any 
meaningfull way, or perform complex operation like connecting a remote 
database.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Dmitry Olshansky

On 14.05.2012 23:26, Steven Schveighoffer wrote:

On Mon, 14 May 2012 15:23:59 -0400, Steven Schveighoffer
schvei...@yahoo.com wrote:


On Mon, 14 May 2012 15:07:30 -0400, Andrej Mitrovic
andrej.mitrov...@gmail.com wrote:


On 5/14/12, Steven Schveighoffer schvei...@yahoo.com wrote:

Really, printf is the *only* reason to have this backwards
compatibility
feature, and I strongly wish we could get rid of it.


printf is also unique in that it works when called in class
destructors, which is sometimes needed for debugging (unlike writef
which wants to allocate memory and then throws).


That's an excellent point. But what a really mean is, I wish we could
get rid of the requirement for interoperability between printf and
writef.

Of course, we couldn't get rid of printf, it's part of the C runtime!


Oh, and also, we should fix that problem (that writef allocates).
However, I think we need DIP9 in order to do that.
http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP9



DIP9 for the win!
I tried to revive at least *some* interest in it for a long time.


--
Dmitry Olshansky


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Jonathan M Davis
On Monday, May 14, 2012 21:18:42 deadalnix wrote:
 This whole thing is overinflated. This isn't a problem. I'm pretty sure
 that most code that will broke was actually already a bad idea in the
 first place.

I'm fine with it, but there are people who complain quite bitterly about it 
(generally game programmers, I think - people with pretty insane restrictions
on their programs due to very strict performance requirements).

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread H. S. Teoh
On Mon, May 14, 2012 at 11:55:15PM +0400, Dmitry Olshansky wrote:
 On 14.05.2012 23:26, Steven Schveighoffer wrote:
[...]
 Oh, and also, we should fix that problem (that writef allocates).
 However, I think we need DIP9 in order to do that.
 http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP9
 
 
 DIP9 for the win!
 I tried to revive at least *some* interest in it for a long time.
[...]

+1.

I like this proposal. I have never liked the idea of toString() ever
since Java introduced it.  Besides memory usage issues, toString() also
binds you to the string type for no good reason: what if you wanted to
store the result into a database file? Why waste resources on an
intermediate representation if it can be written straight to its final
destination?

I'd even argue that DIP9 is an excellent example of:

http://en.wikipedia.org/wiki/Dependency_inversion_principle

We don't know what representation the caller ultimately wants (string in
memory, file on disk, network socket, etc.), so why impose an
essentially arbitrary concrete representation type? Let the delegate
decide what to do with the data.


T

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


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Era Scarecrow
On Monday, 14 May 2012 at 19:24:15 UTC, Steven Schveighoffer 
wrote:
I have never seen this suggested before.  I would guess that it 
might be considered a bug, since you are accessing the outer 
instance via a pointer contained in the inner instance.


But I like the approach (even if it is a bug)!


 I was thinking something along these lines last night, although 
I couldn't put it quite together, it did end up using alias this.



 I forget but someone commented on possibly using a flag that 
would let you write to a variable for the first time, and break 
if you tried to read from it. A thought that went through my head 
would indeed need a little thought, but I couldn't see an easy 
way to do it outside of a new keyword. What if .init (or null) 
was your flag effectively? Let's assume allowinit (or something 
better) to be the keyword. Then...


struct S {
  allowinit uint hash;

  uint toHash() const {
if (hash == uint.init) { //read always allowed safely within 
struct

hash = 1;
}

return hash;
  }

  void throwtest() {
hash = 2; //might not throw
  }
}

func(){
 const S s, z;

// writeln(s.hash);   //throw!

 //throwtest();   //would not throw here
 writeln(z.toHash()); //fine
 writeln(z.toHash()); //yep still fine
 writeln(z.hash);   //allowed? technically yes...

 throwtest();   //throw!
}

 Course these types may cause more problems if outside code had 
to do extra checks, so making allowinit forced to be private 
would remove that issue leaving it only to within the struct, 
making it good for caching results as you could only set it once.


 But that only works with const data, immutable being locked in 
memory it wouldn't do the job. So the only way to get around that 
is to allow a constructor that sets it during construction.


//as above
struct S {
  this(uint h) {
hash = h;
  }
}

func(){
  immutable S i; //compile error! Immutable cannot have allowinit 
with default constructor!

  immutable S i2 = S(10); //fine
  immutable S i2 = S(uint.init); //error! allowinit cannot be 
assigned to .init value for immutable!

}




Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Timon Gehr

On 05/14/2012 09:18 PM, deadalnix wrote:

Le 14/05/2012 04:47, Jonathan M Davis a écrit :

They can be in almost all cases. The problem is the folks who want to
have
caching and/or lazy initiailization in their classes/structs. You
can't cache
the result of any of those functions (toHash being the main target for
it) if
they're const and pure except in overloads which _aren't_ const,
making those
functions more expensive in comparison to what they could be if they
weren't
required to be const. And lazy initialization becomes almost impossible,
because if the member variables needed in those functions haven't been
initialized yet when they're called, then you _can't_ initialize them. If
getting the value that it _would_ be without actually initializing the
member
variable works, then you can do that if it hasn't been initialized,
but if you
can't do that (e.g. the variable _must_ be set only once), then you're
screwed. And regardless of whether you can make it work with const, it
_will_
be less efficient.



Lazy initialization is more a problem than a solution when it comes to
multithreading.


How to solve the problem of representation of infinite data structures 
otherwise?



And I'm afraid it is the future.



Sharing mutable data frivolously is likely not the future. I guess that 
usually every thread will have its own cache.



BTW, nothing prevent to define toString an non const and another as
const. The const one cannot cache the result, but certainly can read it.
And every method that is aware of the non const version will use it.

This whole thing is overinflated. This isn't a problem. I'm pretty sure
that most code that will broke was actually already a bad idea in the
first place.


That is a rather optimistic assumption. And even if it is true for 
'most' code, that is not enough imho.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Jakob Ovrum

On Monday, 14 May 2012 at 12:22:30 UTC, Steven Schveighoffer
wrote:
On Mon, 14 May 2012 02:35:16 -0400, Jakob Ovrum 
jakobov...@gmail.com wrote:


How about logically constant opEquals, toString etc? 
Currently, this is perfectly possible by just *not using 
const*. Logical constancy goes beyond memoization.


This means you cannot compare two const objects.


Yes, but using const for these objects makes no sense because
they all require temporary mutation (the Lua stack) to do
anything meaningful. This includes opEquals and toString. Thus
the option should be there not to use const.

The issue is, non-const opEquals makes sense on some objects, 
and const opEquals makes sense on others.  However, you must 
make them all come together in Object.opEquals.


I think we already have the hooks to properly compare objects 
without requiring Object.opEquals.


Right now, when two objects are compared, the compiler calls 
object.opEquals (that's little o for object, meaning the module 
function *not* the class method).


So why can't object.opEquals be a template that decides whether 
to use Object.opEquals (which IMO *must be* const) or a derived 
version?  I don't think it's that much of a stretch (not sure 
if we need a compiler fix for this).


-Steve


Right, I think this is the way to go. We have to accommodate both
kind of object.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Walter Bright

On 5/14/2012 10:08 AM, Tove wrote:

but c++ has the 'mutable' keyword as an easy escape route...


The existence of that capability means that 'const' in C++ cannot be 
meaningfully reasoned about.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Jakob Ovrum

On Monday, 14 May 2012 at 18:52:06 UTC, deadalnix wrote:
The only reason I'd see a toSting function as non pure or non 
const is memoize. It can be tackled with lib support in phobos.


What are other uses cases ?


Did you miss my post?

http://forum.dlang.org/post/azevkvzkhhfnpmtzg...@forum.dlang.org



Re: Getting the const-correctness of Object sorted once and for all

2012-05-14 Thread Walter Bright

On 5/14/2012 12:47 AM, Chris Cain wrote:

And all this together culminates into a much greater ability to _reason about
code_.


Exactly. And ditto for the rest of your post - it's spot on.

Being able to reason about code is *intensely* important. All those decorations 
- const, pure, nothrow, out - serve to help that out.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 13-05-2012 18:39, Stewart Gordon wrote:

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

This has gone on for too long.

Object.toString, .toHash, .opCmp and .opEquals should all be const.
(It's also been stated somewhere that they should be pure and nothrow,
or something like that, but I forget where.)

This makes it a nightmare to use const objects in data structures, among
other things, at best forcing the use of ugly workarounds. There are
probably other, more serious effects of this that can't easily be worked
around.

It seems that the main obstacle is rewriting the relevant methods in
std.stream. The current implementation doesn't make sense anyway -
reading the entire contents of a file is certainly not the way to
generate a hash or string representation of the stream. I'm thinking the
hash should probably be the stream handle, and the string representation
could perhaps be the full pathname of the file. Of course, what it
should be for non-file streams is another matter. (This would be a
change at the API level, but when the API's as fundamentally flawed as
this)

Are there any other bits of druntime/Phobos that need to be sorted out
before these methods can be declared const/pure/nothrow once and for all?

Stewart.


I agree with everything but toString(). I'm afraid that forcing 
toString() to be const will have harm flexibility severely. Can't we do 
better, somehow?


--
- Alex


Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Stewart Gordon

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

This has gone on for too long.

Object.toString, .toHash, .opCmp and .opEquals should all be const.  (It's also been 
stated somewhere that they should be pure and nothrow, or something like that, but I 
forget where.)


This makes it a nightmare to use const objects in data structures, among other things, at 
best forcing the use of ugly workarounds.  There are probably other, more serious effects 
of this that can't easily be worked around.


It seems that the main obstacle is rewriting the relevant methods in std.stream.  The 
current implementation doesn't make sense anyway - reading the entire contents of a file 
is certainly not the way to generate a hash or string representation of the stream.  I'm 
thinking the hash should probably be the stream handle, and the string representation 
could perhaps be the full pathname of the file.  Of course, what it should be for non-file 
streams is another matter.  (This would be a change at the API level, but when the API's 
as fundamentally flawed as this)


Are there any other bits of druntime/Phobos that need to be sorted out before these 
methods can be declared const/pure/nothrow once and for all?


Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Stewart Gordon

On 13/05/2012 17:41, Alex Rønne Petersen wrote:
snip

I agree with everything but toString(). I'm afraid that forcing toString() to 
be const
will have harm flexibility severely. Can't we do better, somehow?


How exactly?

If you're talking about memoization, it ought to be possible to make use of 
std.functional.memoize to implement it.


Otherwise, using toString to change the state of an object is bending semantics.  If you 
want a method to generate a string representation of an object in a way that might do 
this, then create your own method to do it.


Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 13-05-2012 19:02, Stewart Gordon wrote:

On 13/05/2012 17:41, Alex Rønne Petersen wrote:
snip

I agree with everything but toString(). I'm afraid that forcing
toString() to be const
will have harm flexibility severely. Can't we do better, somehow?


How exactly?

If you're talking about memoization, it ought to be possible to make use
of std.functional.memoize to implement it.

Otherwise, using toString to change the state of an object is bending
semantics. If you want a method to generate a string representation of
an object in a way that might do this, then create your own method to do
it.

Stewart.


But how would you memoize the value in the instance of the object if 
it's const?


--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Stewart Gordon

On 13/05/2012 18:04, Alex Rønne Petersen wrote:
snip

But how would you memoize the value in the instance of the object if it's const?


By storing the memoized value outside of the object.  Read the code of 
std.functional.memoize - you'll see that it holds the values in a static AA.


Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 13-05-2012 19:27, Stewart Gordon wrote:

On 13/05/2012 18:04, Alex Rønne Petersen wrote:
snip

But how would you memoize the value in the instance of the object if
it's const?


By storing the memoized value outside of the object. Read the code of
std.functional.memoize - you'll see that it holds the values in a static
AA.

Stewart.


Which forces you to:

1) use an AA lookup
2) use the GC

Not an optimal solution IMHO.

--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Stewart Gordon

On 13/05/2012 18:53, Alex Rønne Petersen wrote:
snip

Which forces you to:

1) use an AA lookup
2) use the GC

Not an optimal solution IMHO.


But you're going to need to use the GC anyway.  How else are you going to store 
the string?

Memoization is a form of laziness.  This concept applies to the allocation of memory to 
hold the result as much as to the actual calculation of the result.


Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 13-05-2012 21:11, Stewart Gordon wrote:

On 13/05/2012 18:53, Alex Rønne Petersen wrote:
snip

Which forces you to:

1) use an AA lookup
2) use the GC

Not an optimal solution IMHO.


But you're going to need to use the GC anyway. How else are you going to
store the string?


malloc? (And free it in the destructor or whatever - depends on your 
manual memory management approach.)




Memoization is a form of laziness. This concept applies to the
allocation of memory to hold the result as much as to the actual
calculation of the result.

Stewart.


--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Walter Bright

On 5/13/2012 9:39 AM, Stewart Gordon wrote:

It seems that the main obstacle is rewriting the relevant methods in std.stream.
The current implementation doesn't make sense anyway - reading the entire
contents of a file is certainly not the way to generate a hash or string
representation of the stream. I'm thinking the hash should probably be the
stream handle, and the string representation could perhaps be the full pathname
of the file. Of course, what it should be for non-file streams is another
matter. (This would be a change at the API level, but when the API's as
fundamentally flawed as this)


I'd like to see std.stream dumped. I don't see any reason for it to exist that 
std.stdio doesn't do (or should do).


The only reason it's there at the moment is for backwards compatibility.



Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Era Scarecrow

On Sunday, 13 May 2012 at 19:43:19 UTC, Walter Bright wrote:

On 5/13/2012 9:39 AM, Stewart Gordon wrote:
It seems that the main obstacle is rewriting the relevant 
methods in std.stream. The current implementation doesn't make 
sense anyway - reading the entire contents of a file is 
certainly not the way to generate a hash or string 
representation of the stream. I'm thinking the hash should 
probably be the stream handle, and the string representation 
could perhaps be the full pathname of the file. Of course, 
what it should be for non-file streams is another matter. 
(This would be a change at the API level, but when the API's 
as fundamentally flawed as this)


I'd like to see std.stream dumped. I don't see any reason for 
it to exist that std.stdio doesn't do (or should do).


The only reason it's there at the moment is for backwards 
compatibility.


 Recently in my own code I've started making use of std.stream, 
mostly creating temporary string streams to test IO in functions 
that would otherwise need actual files for unittests.


 Glancing over std.stdio, I only see specific file access 
functions and routines. If I'm doing the wrong approach please 
correct me now.


---
import std.stream;

struct Header {
  char[4] name;
  int size;

  int read(InputStream is_in) {
is_in.readExact(name.ptr, name.sizeof);
is_in.read(size);
return this.sizeof;
  }

  int write(OutputStream os_out) {
os_out.writeExact(name.ptr, name.sizeof);
os_out.write(size);
return this.sizeof;
  }

  unittest {
ubyte[Header.sizeof] buffer;
auto buff_stream = new TArrayStream!(ubyte[])(buffer);

Header x = Header(abcd, 1024);
x.write(buff_stream);
assert(buffer == [97, 98, 99, 100, 0, 4, 0, 0]);

buff_stream.seekSet(0);
x = Header();

assert(x.size == 0);
x.read(buff_stream);
assert(x.name == abcd);
assert(x.size == 1024);
  }
}


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Stewart Gordon

On 13/05/2012 20:42, Walter Bright wrote:
snip

I'd like to see std.stream dumped. I don't see any reason for it to exist that 
std.stdio
doesn't do (or should do).


So std.stdio.File is the replacement for the std.stream stuff?

How does/will it provide all the different kinds of stream that std.stream provides, as 
well as the other kinds of stream that applications will need?


What's the plan for std.cstream?


The only reason it's there at the moment is for backwards compatibility.


So the plan is to deprecate that, then remove it, _then_ fix this issue?

Or make toString and toHash in the stream classes bypass constancy?

Stewart.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Sunday, May 13, 2012 17:39:44 Stewart Gordon wrote:
 http://d.puremagic.com/issues/show_bug.cgi?id=1824
 
 This has gone on for too long.
 
 Object.toString, .toHash, .opCmp and .opEquals should all be const.  (It's
 also been stated somewhere that they should be pure and nothrow, or
 something like that, but I forget where.)
 
 This makes it a nightmare to use const objects in data structures, among
 other things, at best forcing the use of ugly workarounds.  There are
 probably other, more serious effects of this that can't easily be worked
 around.
 
 It seems that the main obstacle is rewriting the relevant methods in
 std.stream.  The current implementation doesn't make sense anyway - reading
 the entire contents of a file is certainly not the way to generate a hash
 or string representation of the stream.  I'm thinking the hash should
 probably be the stream handle, and the string representation could perhaps
 be the full pathname of the file.  Of course, what it should be for
 non-file streams is another matter.  (This would be a change at the API
 level, but when the API's as fundamentally flawed as this)
 
 Are there any other bits of druntime/Phobos that need to be sorted out
 before these methods can be declared const/pure/nothrow once and for all?
 
 Stewart.

toHash, opCmp, opEquals, and toString will _all_ be required to be const @safe 
pure nothrow. Some work has been done towards that, but it hasn't been 
completed. One of the major obstacles is the inability for stuff such as 
format, to!string, or Appender to be pure right now. A number of druntime 
functions are going to need to be marked as pure for that to happen 
(particilurly reserve and capacity, but there are probably others). Some effort 
has been put in that area recently, but it hasn't been completed.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Dmitry Olshansky

On 14.05.2012 0:48, Stewart Gordon wrote:

On 13/05/2012 20:42, Walter Bright wrote:
snip

I'd like to see std.stream dumped. I don't see any reason for it to
exist that std.stdio
doesn't do (or should do).


So std.stdio.File is the replacement for the std.stream stuff?

How does/will it provide all the different kinds of stream that
std.stream provides, as well as the other kinds of stream that
applications will need?



I think I've seen proper replacement (or rather a draft of it). If only 
Steven can be bothered to finish it :)



What's the plan for std.cstream?


The only reason it's there at the moment is for backwards compatibility.


So the plan is to deprecate that, then remove it, _then_ fix this issue?

Or make toString and toHash in the stream classes bypass constancy?

Stewart.



--
Dmitry Olshansky


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Walter Bright

On 5/13/2012 1:48 PM, Stewart Gordon wrote:

On 13/05/2012 20:42, Walter Bright wrote:
snip

I'd like to see std.stream dumped. I don't see any reason for it to exist that
std.stdio
doesn't do (or should do).


So std.stdio.File is the replacement for the std.stream stuff?


Not exactly. Ranges are the replacement. std.stdio.File is merely a range that 
deals with files.




How does/will it provide all the different kinds of stream that std.stream
provides, as well as the other kinds of stream that applications will need?


The future is a range interface, not a stream one.



What's the plan for std.cstream?


I don't see a purpose for std.cstream.



The only reason it's there at the moment is for backwards compatibility.


So the plan is to deprecate that, then remove it, _then_ fix this issue?

Or make toString and toHash in the stream classes bypass constancy?


What do you suggest?



Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Walter Bright

On 5/13/2012 12:57 PM, Era Scarecrow wrote:

On Sunday, 13 May 2012 at 19:43:19 UTC, Walter Bright wrote:

On 5/13/2012 9:39 AM, Stewart Gordon wrote:

It seems that the main obstacle is rewriting the relevant methods in
std.stream. The current implementation doesn't make sense anyway - reading
the entire contents of a file is certainly not the way to generate a hash or
string representation of the stream. I'm thinking the hash should probably be
the stream handle, and the string representation could perhaps be the full
pathname of the file. Of course, what it should be for non-file streams is
another matter. (This would be a change at the API level, but when the API's
as fundamentally flawed as this)


I'd like to see std.stream dumped. I don't see any reason for it to exist that
std.stdio doesn't do (or should do).

The only reason it's there at the moment is for backwards compatibility.


Recently in my own code I've started making use of std.stream, mostly creating
temporary string streams to test IO in functions that would otherwise need
actual files for unittests.


I suggest switching over to a range interface. Then, your code need not care if 
it is getting input from files, arrays, or any containers. Same for output. That 
means you can use arrays to unittest it, or write your own mock input/output ranges.




Glancing over std.stdio, I only see specific file access functions and routines.
If I'm doing the wrong approach please correct me now.


std.stdio's support for ranges needs some improvement.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Sunday, May 13, 2012 13:54:48 Jonathan M Davis wrote:
 On Sunday, May 13, 2012 17:39:44 Stewart Gordon wrote:
  http://d.puremagic.com/issues/show_bug.cgi?id=1824
  
  This has gone on for too long.
  
  Object.toString, .toHash, .opCmp and .opEquals should all be const.  (It's
  also been stated somewhere that they should be pure and nothrow, or
  something like that, but I forget where.)
  
  This makes it a nightmare to use const objects in data structures, among
  other things, at best forcing the use of ugly workarounds.  There are
  probably other, more serious effects of this that can't easily be worked
  around.
  
  It seems that the main obstacle is rewriting the relevant methods in
  std.stream.  The current implementation doesn't make sense anyway -
  reading
  the entire contents of a file is certainly not the way to generate a hash
  or string representation of the stream.  I'm thinking the hash should
  probably be the stream handle, and the string representation could perhaps
  be the full pathname of the file.  Of course, what it should be for
  non-file streams is another matter.  (This would be a change at the API
  level, but when the API's as fundamentally flawed as this)
  
  Are there any other bits of druntime/Phobos that need to be sorted out
  before these methods can be declared const/pure/nothrow once and for all?
  
  Stewart.
 
 toHash, opCmp, opEquals, and toString will _all_ be required to be const
 @safe pure nothrow. Some work has been done towards that, but it hasn't
 been completed. One of the major obstacles is the inability for stuff such
 as format, to!string, or Appender to be pure right now. A number of
 druntime functions are going to need to be marked as pure for that to
 happen (particilurly reserve and capacity, but there are probably others).
 Some effort has been put in that area recently, but it hasn't been
 completed.

There's probably some AA-related stuff which has issues too. I'm not sure what 
all of the major obstacles are, but some of them have been being worked on 
with the goal of making object const-correct in the near future.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Timon Gehr

On 05/13/2012 06:39 PM, Stewart Gordon wrote:

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

This has gone on for too long.

Object.toString, .toHash, .opCmp and .opEquals should all be const.


No, please.

Transitive enforced const + OO - epic fail. (especially for unshared 
data, which is the default and most common)


The lack of head-mutable class references does not make this any better.

I'd rather keep the current state of affairs than blindly mark the 
existing methods const. Caching/lazy evaluation is everywhere. One 
single lazily cached class member implies that all of the relevant 
methods cannot use that member. What if they have to? They certainly 
have to!


Const/immutable are great for simple value-types. Classes can contain 
immutable values as fields.



(It's also been stated somewhere that they should be pure and nothrow,
or something like that, but I forget where.)



Similar concerns apply for them (but less so than for const). I don't 
want to have to violate the type system to get basic stuff done in 
OO-ish style! Forced const invariants are annoying enough already.



This makes it a nightmare to use const objects in data structures,


Why on earth would you want to do that?


among other things, at best forcing the use of ugly workarounds. There are
probably other, more serious effects of this that can't easily be worked
around.



I wouldn't bet on that. Marking the methods const, on the other hand, 
certainly would have serious effects.



It seems that the main obstacle is rewriting the relevant methods in
std.stream. The current implementation doesn't make sense anyway -
reading the entire contents of a file is certainly not the way to
generate a hash or string representation of the stream. I'm thinking the
hash should probably be the stream handle, and the string representation
could perhaps be the full pathname of the file. Of course, what it
should be for non-file streams is another matter. (This would be a
change at the API level, but when the API's as fundamentally flawed as
this)

Are there any other bits of druntime/Phobos that need to be sorted out
before these methods can be declared const/pure/nothrow once and for all?



Almost _all_ of Phobos would have to become const correct, and it 
cannot, because 'const' is an over-approximation and there is no 
const-inference. const on the top-class interface invades _everything_.


Marking the methods const would break every non-trivial D program out 
there and put programmers in a frustrating straitjacket.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Monday, May 14, 2012 00:30:09 Timon Gehr wrote:
 On 05/13/2012 06:39 PM, Stewart Gordon wrote:
  http://d.puremagic.com/issues/show_bug.cgi?id=1824
  
  This has gone on for too long.
  
  Object.toString, .toHash, .opCmp and .opEquals should all be const.
 
 No, please.
 
 Transitive enforced const + OO - epic fail. (especially for unshared
 data, which is the default and most common)
 
 The lack of head-mutable class references does not make this any better.
 
 I'd rather keep the current state of affairs than blindly mark the
 existing methods const. Caching/lazy evaluation is everywhere. One
 single lazily cached class member implies that all of the relevant
 methods cannot use that member. What if they have to? They certainly
 have to!
 
 Const/immutable are great for simple value-types. Classes can contain
 immutable values as fields.

Walter fully intends to require that opEquals, opCmp, toHash, and toString all 
be const @safe pure nothrow - on both classes and structs. And the reality of 
the matter is, if that requirement _isn't_ there on classes, then the usage of 
any of those functions in @safe, const, pure, or nothrow code is seriously 
hampered - especially const and pure. This has been discussed quite a bit 
before, and we're pretty much stuck thanks to transivity. Caching and lazy 
evaluation _will_ be impossible in those functions without breaking the type 
system. Anything that absolutely requires them will probably have to either 
have to break the type system or use _other_ functions with the same 
functionality but without those attributes. In some cases though, providing 
overloads which aren't const, pure, etc. should work though.

If you want it to be otherwise, you're going to have to convince Walter, and I 
think that it's pretty clear that this is the way that it's going to have to 
be thanks to how const et al. work.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Mehrdad

On Sunday, 13 May 2012 at 20:55:13 UTC, Jonathan M Davis wrote:
toHash, opCmp, opEquals, and toString will _all_ be required to 
be const @safe pure nothrow.


And what am I to do if my toString() method cannot be const?


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Mehrdad

On Sunday, 13 May 2012 at 22:51:05 UTC, Jonathan M Davis wrote:
Anything that absolutely requires them will probably have to 
either have to break the type system or use _other_ functions 
with the same functionality but without those attributes. In 
some cases though, providing overloads which aren't const, 
pure, etc. should work though.
If you want it to be otherwise, you're going to have to 
convince Walter, and I think that it's pretty clear that this 
is the way that it's going to have to be thanks to how const et 
al. work.


This is *exactly* the sort of problem I was referring to in my 
const(rant) thread, so to speak.


These const-related issues make D, simply speaking, *HARD TO USE*.
(Yes, that means even harder then C++ in some cases.)

When people say it's painful to find workarounds to problems in 
D, I hope -- at the very least -- no one will be surprised as to 
why.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread H. S. Teoh
On Sun, May 13, 2012 at 12:42:32PM -0700, Walter Bright wrote:
[...]
 I'd like to see std.stream dumped. I don't see any reason for it to
 exist that std.stdio doesn't do (or should do).
 
 The only reason it's there at the moment is for backwards
 compatibility.

+1. I'd love to see std.io done in the near future.


T

-- 
Life is too short to run proprietary software. -- Bdale Garbee


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Era Scarecrow

On Sunday, 13 May 2012 at 22:51:05 UTC, Jonathan M Davis wrote:
Walter fully intends to require that opEquals, opCmp, toHash, 
and toString all be const @safe pure nothrow - on both classes 
and structs. And the reality of the matter is, if that 
requirement _isn't_ there on classes, then the usage of any of 
those functions in @safe, const, pure, or nothrow code is 
seriously hampered - especially const and pure. This has been 
discussed quite a bit before, and we're pretty much stuck 
thanks to transitivity.


 I've been wondering sometimes, if beta builds could be made (but 
not official release builds) that enforce the way it's suppose to 
be, and then we can try to compile against that version and see 
how much difference or how much breaks. The problem may not be 
quite as bad as we think it is. Honestly I don't see why oEquals, 
opCmp and toHash can't be const (as the data shouldn't change). 
toString and toHash may want to cache it's current result 
(perhaps?).





Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread kenji hara
No. toString, toHash, opCmp and opEquals should not change the object
_in default_. It is valuable design.

And, If you want to change object through the methods, you still CAN
define mutable version in derived classes.

class YourClass : Object {
  string toString() {
/*cache calculated string*/ return str;
  }
  striing toString() const {
return super.toString();   // use Object.toString instead mutable version
return (cast(YourClass)this).toString();  // bypass const type system
  }
}

But mutable version doesn't call const version *through Object class reference*
But you cannot call mutable version from a class reference typed Object.

auto my = new YourClass();
my.toString();  // call mutable version
const cy = my;
cy.toString();  // call const version
Object mo = my;
mo.toString();  // call _const_ version.
const co = mo;
co.toString();  // call const version.

Yes, I can agree this may have problem in certain cases. But I argue
that is better design than others from the view of minimum language
design, and provides valuable restriction - that is correct in most
cases - in default.

Kenji Hara

2012/5/14 Timon Gehr timon.g...@gmx.ch:
 On 05/13/2012 06:39 PM, Stewart Gordon wrote:

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

 This has gone on for too long.

 Object.toString, .toHash, .opCmp and .opEquals should all be const.


 No, please.

 Transitive enforced const + OO - epic fail. (especially for unshared data,
 which is the default and most common)

 The lack of head-mutable class references does not make this any better.

 I'd rather keep the current state of affairs than blindly mark the existing
 methods const. Caching/lazy evaluation is everywhere. One single lazily
 cached class member implies that all of the relevant methods cannot use that
 member. What if they have to? They certainly have to!

 Const/immutable are great for simple value-types. Classes can contain
 immutable values as fields.


 (It's also been stated somewhere that they should be pure and nothrow,
 or something like that, but I forget where.)


 Similar concerns apply for them (but less so than for const). I don't want
 to have to violate the type system to get basic stuff done in OO-ish style!
 Forced const invariants are annoying enough already.


 This makes it a nightmare to use const objects in data structures,


 Why on earth would you want to do that?


 among other things, at best forcing the use of ugly workarounds. There are
 probably other, more serious effects of this that can't easily be worked
 around.


 I wouldn't bet on that. Marking the methods const, on the other hand,
 certainly would have serious effects.


 It seems that the main obstacle is rewriting the relevant methods in
 std.stream. The current implementation doesn't make sense anyway -
 reading the entire contents of a file is certainly not the way to
 generate a hash or string representation of the stream. I'm thinking the
 hash should probably be the stream handle, and the string representation
 could perhaps be the full pathname of the file. Of course, what it
 should be for non-file streams is another matter. (This would be a
 change at the API level, but when the API's as fundamentally flawed as
 this)

 Are there any other bits of druntime/Phobos that need to be sorted out
 before these methods can be declared const/pure/nothrow once and for all?


 Almost _all_ of Phobos would have to become const correct, and it cannot,
 because 'const' is an over-approximation and there is no const-inference.
 const on the top-class interface invades _everything_.

 Marking the methods const would break every non-trivial D program out there
 and put programmers in a frustrating straitjacket.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Monday, May 14, 2012 02:11:21 Era Scarecrow wrote:
 On Sunday, 13 May 2012 at 22:51:05 UTC, Jonathan M Davis wrote:
  Walter fully intends to require that opEquals, opCmp, toHash,
  and toString all be const @safe pure nothrow - on both classes
  and structs. And the reality of the matter is, if that
  requirement _isn't_ there on classes, then the usage of any of
  those functions in @safe, const, pure, or nothrow code is
  seriously hampered - especially const and pure. This has been
  discussed quite a bit before, and we're pretty much stuck
  thanks to transitivity.
 
   I've been wondering sometimes, if beta builds could be made (but
 not official release builds) that enforce the way it's suppose to
 be, and then we can try to compile against that version and see
 how much difference or how much breaks. The problem may not be
 quite as bad as we think it is.

It doesn't work right now due to issues with druntime and Phobos. For 
instance, format and to!string can't be pure yet due to lower level constructs 
that they use which aren't marked pure. Appender has similar problems. Without 
those, toString really can't be const. I think that there are some issues with 
toHash as well due to the state of the AA implementation in druntime. Some of 
those issues are being addressed, but there's still a ways to go.

But in a way, it doesn't matter how much making those 4 functions const @safe 
pure nothrow will break, because we _have_ to do it. So, what will break, will 
break. When they're made const @safe pure nothrow on Object, it'll 
_immediately_ break code, and there's _no_ way around it. So, at this point, 
I'd say that it's really a matter of figuring out exactly what needs to be 
fixed 
in druntime and Phobos to make it possible, and once that's been fixed, we 
immediately change Object and and require that those 4 functions have those 4 
attributes. Then everyone can take care of it in their code at once and we 
won't have to worry about it anymore. Lacking a means of phasing it in (like 
we're doing with other stuff like -property), I don't see any other way of 
doing it.

 Honestly I don't see why oEquals,
 opCmp and toHash can't be const (as the data shouldn't change).
 toString and toHash may want to cache it's current result
 (perhaps?).

They can be in almost all cases. The problem is the folks who want to have 
caching and/or lazy initiailization in their classes/structs. You can't cache 
the result of any of those functions (toHash being the main target for it) if 
they're const and pure except in overloads which _aren't_ const, making those 
functions more expensive in comparison to what they could be if they weren't 
required to be const. And lazy initialization becomes almost impossible, 
because if the member variables needed in those functions haven't been 
initialized yet when they're called, then you _can't_ initialize them. If 
getting the value that it _would_ be without actually initializing the member 
variable works, then you can do that if it hasn't been initialized, but if you 
can't do that (e.g. the variable _must_ be set only once), then you're 
screwed. And regardless of whether you can make it work with const, it _will_ 
be less efficient.

So, the folks who really like to use lazy initialization and caching are _not_ 
going to be happy about this. They've complained every time that it's been 
discussed. But that's just one of the costs of const in D. Whether its pros 
outweigh that cost is a matter of opinion.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Mehrdad

On Monday, 14 May 2012 at 02:48:20 UTC, Jonathan M Davis wrote:

But that's just one of the costs of const in D.



The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const, 
because, practically speaking, it's being forced onto the 
programmers by the language.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Era Scarecrow

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

On Monday, 14 May 2012 at 02:48:20 UTC, Jonathan M Davis wrote:

But that's just one of the costs of const in D.


The problem is that it's unavoidable.

I.e. you can't say don't Mark it as const if it isn't const, 
because, practically speaking, it's being forced onto the 
programmers by the language.


 With const and immutable being transitive, there are cases when 
there's things you want to do with an object after you've made it 
and it's in a const structure. I know I've made a struct that was 
intended to hold immutable data, however it refused to allow 
methods to until const was appended to the signatures of the 
functions. At first it was annoying; But when I see bigger 
picture of why it makes much more sense.


 Nothing we make 'has' to have const on it but inevitably they 
may be used in a read-only way. I'm trying to think of it as my 
structs and classes being as const-friendly as possible in those 
cases.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Monday, May 14, 2012 05:09:28 Era Scarecrow wrote:
 On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:
  On Monday, 14 May 2012 at 02:48:20 UTC, Jonathan M Davis wrote:
  But that's just one of the costs of const in D.
  
  The problem is that it's unavoidable.
  
  I.e. you can't say don't Mark it as const if it isn't const,
  because, practically speaking, it's being forced onto the
  programmers by the language.
 
   With const and immutable being transitive, there are cases when
 there's things you want to do with an object after you've made it
 and it's in a const structure. I know I've made a struct that was
 intended to hold immutable data, however it refused to allow
 methods to until const was appended to the signatures of the
 functions. At first it was annoying; But when I see bigger
 picture of why it makes much more sense.
 
   Nothing we make 'has' to have const on it but inevitably they
 may be used in a read-only way. I'm trying to think of it as my
 structs and classes being as const-friendly as possible in those
 cases.

In general, you _can_ avoid const completely if you want to. However, with 
opEquals, opCmp, toHash, and toString being const @safe pure nothrow, you 
_can't_ avoid it completely - at least not entirely cleanly.

You could have non-const overloads which did what you wanted and make the 
normal const ones assert(0) if they're called. But you're basically forced to 
work around const in this case. This is in definite contrast to C++ where you 
_can_ avoid const completely if you want to.

But with the way const in D works, I suspect that the folks who are looking 
for absolutely every CPU cycle and want caching and lazy-loading in their 
types are going to avoid const as completely as possible and figure out how to 
work around it for those 4 functions, whereas in C++, they'd probably use 
const liberally and use mutable where necessary.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Era Scarecrow

On Monday, 14 May 2012 at 03:19:57 UTC, Jonathan M Davis wrote:
But with the way const in D works, I suspect that the folks who 
are looking for absolutely every CPU cycle and want caching and 
lazy-loading in their types are going to avoid const as 
completely as possible and figure out how to work around it for 
those 4 functions, whereas in C++, they'd probably use const 
liberally and use mutable where necessary.


 Seems like perhaps the opposite approach (limited of course) 
could be taken. Assume a 'mutable' keyword was used, where in the 
case an object/struct was made const, it would allow you to break 
the const system only for that variable. Could work, assuming 
it's not accessing ROM or something.


struct A {
  string st;
  mutable uint hash; //always mutable

  uint toHash() {
if (!hash)
  hash = st.toHash();
return hash;
  }
}

unittest{
  A a = A(TEST);
  immutable A b = A(TEST);

  assert(a.toHash() == b.toHash());
}

 But that seems like a badly done workaround, and if you really 
wanted the hash cached, you would have it calculated during 
construction before it was returned. Something like that is 
likely more trouble than it's worth, plus it would easily be 
abused.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Walter Bright

On 5/13/2012 8:19 PM, Jonathan M Davis wrote:

This is in definite contrast to C++ where you
_can_ avoid const completely if you want to.


That's because C++ const is just documentation for the programmer, and offers no 
reliable enforcement.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 14-05-2012 05:19, Jonathan M Davis wrote:

On Monday, May 14, 2012 05:09:28 Era Scarecrow wrote:

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

On Monday, 14 May 2012 at 02:48:20 UTC, Jonathan M Davis wrote:

But that's just one of the costs of const in D.


The problem is that it's unavoidable.

I.e. you can't say don't Mark it as const if it isn't const,
because, practically speaking, it's being forced onto the
programmers by the language.


   With const and immutable being transitive, there are cases when
there's things you want to do with an object after you've made it
and it's in a const structure. I know I've made a struct that was
intended to hold immutable data, however it refused to allow
methods to until const was appended to the signatures of the
functions. At first it was annoying; But when I see bigger
picture of why it makes much more sense.

   Nothing we make 'has' to have const on it but inevitably they
may be used in a read-only way. I'm trying to think of it as my
structs and classes being as const-friendly as possible in those
cases.


In general, you _can_ avoid const completely if you want to. However, with
opEquals, opCmp, toHash, and toString being const @safe pure nothrow, you
_can't_ avoid it completely - at least not entirely cleanly.


Don't forget contracts. The this reference is now const inside those 
too. This can get painful sometimes when you really don't want the this 
reference to be const inside your invariants. With 2.058, I had to 
insert a lot of cast()s to throw const away inside those. So... 
effectively, it's pretty hard to avoid const unless you don't use 
contracts at all.




You could have non-const overloads which did what you wanted and make the
normal const ones assert(0) if they're called. But you're basically forced to
work around const in this case. This is in definite contrast to C++ where you
_can_ avoid const completely if you want to.

But with the way const in D works, I suspect that the folks who are looking
for absolutely every CPU cycle and want caching and lazy-loading in their
types are going to avoid const as completely as possible and figure out how to
work around it for those 4 functions, whereas in C++, they'd probably use
const liberally and use mutable where necessary.

- Jonathan M Davis


It's kinda funny that something which on dlang.org's FAQ is described as 
a compiler optimization hint (http://dlang.org/const-faq.html#const) 
isn't useful at all for this purpose...


--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Chris Cain

On Monday, 14 May 2012 at 02:57:57 UTC, Mehrdad wrote:

The problem is that it's unavoidable.

i.e. you can't say don't mark it as const if it isn't const, 
because, practically speaking, it's being forced onto the 
programmers by the language.


You're really against const in this language, huh?

Let me ask you something: Could you try to name 2 or 3 good
things about const/immutable without looking it up? If not,
you've really not given enough effort to learning about the
benefits of it.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Monday, May 14, 2012 05:47:54 Alex Rønne Petersen wrote:
 It's kinda funny that something which on dlang.org's FAQ is described as
 a compiler optimization hint (http://dlang.org/const-faq.html#const)
 isn't useful at all for this purpose...

Oh, it's definitely useful for optimizations. It just doesn't work with a 
couple of idioms that some programmers like to use in order to optimize their 
code.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Sunday, May 13, 2012 20:40:51 Walter Bright wrote:
 On 5/13/2012 8:19 PM, Jonathan M Davis wrote:
  This is in definite contrast to C++ where you
  _can_ avoid const completely if you want to.
 
 That's because C++ const is just documentation for the programmer, and
 offers no reliable enforcement.


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Sunday, May 13, 2012 20:40:51 Walter Bright wrote:
 On 5/13/2012 8:19 PM, Jonathan M Davis wrote:
  This is in definite contrast to C++ where you
  _can_ avoid const completely if you want to.
 
 That's because C++ const is just documentation for the programmer, and
 offers no reliable enforcement.

True, but the point is that the pros and cons of D's const and C++'s const are 
different, and wihle D's const is more powerful, that power does come with a 
cost, and it's difficult to avoid D's const completely, whereas C++'s const is 
quite easy to avoid.

- Jonathan M Davis


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Era Scarecrow

On Monday, 14 May 2012 at 04:18:32 UTC, Jonathan M Davis wrote:
Oh, it's definitely useful for optimizations. It just doesn't 
work with a couple of idioms that some programmers like to use 
in order to optimize their code.


 Perhaps bad practices and styles hard-coded in their psyche due 
to limitations and issues with the other language?


 If there's something that gets in the way with what's made sense 
to you; You'll either try to figure it out (manual and forums) or 
backtrack to what you do understand and perhaps get even more 
stuck than before.



 I was in the military for a time; In Basic Training they told us 
'forget everything (you think) you know about firing your rifle'. 
They taught you from scratch how they wanted you to do it. In 
programming there's no real 'from scratch' after you're used to 
it a certain set of ways, and relearning something that already 
makes sense to you makes you unconsciously skip things you 
otherwise wouldn't.





Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread H. S. Teoh
On Mon, May 14, 2012 at 06:55:02AM +0200, Era Scarecrow wrote:
[...]
  I was in the military for a time; In Basic Training they told us
 'forget everything (you think) you know about firing your rifle'.
 They taught you from scratch how they wanted you to do it. In
 programming there's no real 'from scratch' after you're used to it a
 certain set of ways, and relearning something that already makes
 sense to you makes you unconsciously skip things you otherwise
 wouldn't.
[...]

Part of the pitfall of D being part of the C-derived language family is
that people (mainly the C++/Java/C# crowd) unconsciously carry over
their assumptions of how things work from the other languages, and they
try to do things the same way in D and keep running into barriers,
because D just isn't designed to work that way.

That's part of the reason I didn't really get comfortable with D
programming until I bought TDPL -- I needed to learn D from scratch,
as it were, to think about it from a fresh perspective instead of
bringing along my years of C/C++ baggage. For that, looking at a bunch
of online reference docs didn't help: you're just learning the
vocabulary, as it were, and not really thinking in the language. As
any foreign language learner knows, you will never speak the language
well if you just keep translating from your native language; you have to
learn to think in that language. I needed to read through TDPL like a
newbie in order to learn to write D the way it's supposed to be written.

Once I started doing that, many things began to make a lot more sense.


T

-- 
Try to keep an open mind, but not so open your brain falls out. -- theboz


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Alex Rønne Petersen

On 14-05-2012 06:18, Jonathan M Davis wrote:

On Monday, May 14, 2012 05:47:54 Alex Rønne Petersen wrote:

It's kinda funny that something which on dlang.org's FAQ is described as
a compiler optimization hint (http://dlang.org/const-faq.html#const)
isn't useful at all for this purpose...


Oh, it's definitely useful for optimizations. It just doesn't work with a
couple of idioms that some programmers like to use in order to optimize their
code.

- Jonathan M Davis


I have yet to see any compiler make sensible use of the information 
provided by both C++'s const and D's const.


const in particular is completely useless to an optimizer because it 
does not give it any information that it can use for anything. The kind 
of information that an optimization pass, in general, wants to see is 
whether something is guaranteed to *never* change. const does not 
provide this information. const simply guarantees that the code working 
on the const data cannot alter it (but at the same time allows *other* 
code to alter it), which, as said, is useless to the optimizer.


immutable is a different story. immutable actually opens the door to 
many optimization opportunities exactly because the optimizer knows that 
the data will not be altered, ever. This allows it to (almost) 
arbitrarily reorder code, fold many computations at compile time, do 
conditional constant propagation, dead code elimination, ...


This seems reasonable. But now consider that the majority of functions 
*are written for const, not immutable*. Thereby, you're throwing away 
the immutable guarantee, which is what the *compiler* (not the 
*programmer*) cares about. immutable is an excellent idea in theory, but 
in practice, it doesn't help the compiler because you'd have to either


a) templatize all functions operating on const/immutable data so the 
compiler can retain the immutable guarantee when the input is such, or

b) explicitly duplicate code for the const and the immutable case.

Both approaches clearly suck. Templates don't play nice with 
polymorphism, and code duplication is...well...duplication. So, most of 
druntime and phobos is written for const because const is the bridge 
between the mutable and immutable world, and writing code against that 
rather than explicitly against mutable/immutable data is just simpler. 
But this completely ruins any opportunity the compiler has to optimize!


(An interesting fact is that even the compiler engineers working on 
compilers for strictly pure functional languages have yet to take full 
advantage of the potential that a pure, immutable world offers. If 
*they* haven't done it yet, I don't think we're going to do it for a 
long time to come.)


Now, you might argue that the compiler could simply say okay, this data 
is const, which means it cannot be changed in this particular piece of 
code and thus nowhere else, since it is not explicitly shared, and 
therefore not touched by any other threads. This would be great if 
shared wasn't a complete design fallacy. Unfortunately, in most real 
world code, shared just doesn't cut it, and data is often shared between 
threads without using the shared qualifier (__gshared is one example).


shared is another can of worms entirely. I can list a few initial 
reasons why it's unrealistic and impractical:


1) It is extremely x86-biased; implementing it on other architectures is 
going to be...interesting (read: on many architectures, impossible at 
ISA level).
2) There is no bridge between shared and unshared like there is for 
mutable and immutable. This means that all code operating on shared data 
has to be templatized (no, casts will not suffice; the compiler can't 
insert memory barriers then) or code has to be explicitly duplicated for 
the shared and unshared case. Funnily, the exact same issue mentioned 
above for const and immutable!
3) It only provides documentation value. The low-level atomicity that it 
is supposed to provide (but doesn't yet...) is of extremely questionable 
value. In my experience, I never actually access shared data from 
multiple threads simultaneously, but rather, transfer the data from one 
thread to another and use it exclusively in the other thread (i.e. 
handing over the ownership). In such scenarios, shared just adds 
overhead (memory barriers are Bad (TM) for performance).


/rant

--
- Alex


Re: Getting the const-correctness of Object sorted once and for all

2012-05-13 Thread Jonathan M Davis
On Monday, May 14, 2012 07:34:44 Alex Rønne Petersen wrote:
 On 14-05-2012 06:18, Jonathan M Davis wrote:
  On Monday, May 14, 2012 05:47:54 Alex Rønne Petersen wrote:
  It's kinda funny that something which on dlang.org's FAQ is described as
  a compiler optimization hint (http://dlang.org/const-faq.html#const)
  isn't useful at all for this purpose...
  
  Oh, it's definitely useful for optimizations. It just doesn't work with a
  couple of idioms that some programmers like to use in order to optimize
  their code.
  
  - Jonathan M Davis
 
 I have yet to see any compiler make sensible use of the information
 provided by both C++'s const and D's const.
 
 const in particular is completely useless to an optimizer because it
 does not give it any information that it can use for anything. The kind
 of information that an optimization pass, in general, wants to see is
 whether something is guaranteed to *never* change. const does not
 provide this information. const simply guarantees that the code working
 on the const data cannot alter it (but at the same time allows *other*
 code to alter it), which, as said, is useless to the optimizer.

Thanks to the fact that everything is thread-local normally, optimizations 
_can_ be made with const within chunks of code that can guarantee that no 
other reference of the same type (and therefore possibly to the same data) is 
used. How often such optimizations actually occur, I don't know.

In addition, it's possible (but not currently implemented) to have functions 
whose parameters are all const or implicitly convertible to const be strongly 
pure if they're passed immutable data (or data which is implicitly convertible 
to immutable). So, optimizations which we're not currently getting are 
definitely possible with just const.

It's definitely true that immutable provides more potential optimizations than 
const does, but const _does_ provide the opportunity for additional 
optimizations, and in cases where you're dealing with a value type, both const 
and immutable (they being more or less equivalent in such a case) allow for 
optimizations which C++ wouldn't, because the compiler not only doesn't have 
to worry about other references to the same data, but it knows that the data 
will never change from _any_ reference.

Regardless, it wouldn't surprise me in the least if the compiler could be 
doing additional optimizations for both const and immutable which it doesn't 
currently do.

- Jonathan M Davis