www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - The Status of Const

reply dsimcha <dsimcha yahoo.com> writes:
This is from a discussion that originated on the Phobos mailing list, but I
thought I'd bring up the question of what should be done about const on the
newsgroup to see what others think:

Despite its theoretical beauty, I find D's const/immutable system to be
utterly useless for all but the simplest use cases.  I made a serious attempt
a while back to use it in a real multithreaded program.  In hindsight it was
more trouble than it was worth, largely for three reasons:

1.   It's difficult to create non-trivial immutable data structures, and often
impossible without relying on either unchecked casts or unnecessary copying.

2.  Much generic code in Phobos (even things as simple as std.math.pow()
before I recently fixed it) behaves incorrectly when given const/immutable
data.  This also applies to other libraries I use, including ones that I'm the
main author of, so I'm just as guilty of it as anyone.  Given that noone,
including me, seems to be able to get const to interact well with generic
code, perhaps we need a language-level solution.

3.  inout is currently so bug-ridden it's not even funny.  (Though this is
clearly fixable long-term, once we get higher priority stuff off our plates.)

It would have probably been better if this was brought to a head sooner, but
it's better late than never.  Do others agree that D's const system is
difficult to impossible to use properly?  Has anyone successfully used D's
const system in a non-trivial setting despite these limitations?  If so, was
it more trouble than it was worth in hindsight?  How can these limitations be
worked around and/or fixed?
Aug 12 2010
next sibling parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday, August 12, 2010 15:56:50 dsimcha wrote:
 This is from a discussion that originated on the Phobos mailing list, but I
 thought I'd bring up the question of what should be done about const on the
 newsgroup to see what others think:
 
 Despite its theoretical beauty, I find D's const/immutable system to be
 utterly useless for all but the simplest use cases.  I made a serious
 attempt a while back to use it in a real multithreaded program.  In
 hindsight it was more trouble than it was worth, largely for three
 reasons:
 
 1.   It's difficult to create non-trivial immutable data structures, and
 often impossible without relying on either unchecked casts or unnecessary
 copying.
 
 2.  Much generic code in Phobos (even things as simple as std.math.pow()
 before I recently fixed it) behaves incorrectly when given const/immutable
 data.  This also applies to other libraries I use, including ones that I'm
 the main author of, so I'm just as guilty of it as anyone.  Given that
 noone, including me, seems to be able to get const to interact well with
 generic code, perhaps we need a language-level solution.
 
 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our
 plates.)
 
 It would have probably been better if this was brought to a head sooner,
 but it's better late than never.  Do others agree that D's const system is
 difficult to impossible to use properly?  Has anyone successfully used D's
 const system in a non-trivial setting despite these limitations?  If so,
 was it more trouble than it was worth in hindsight?  How can these
 limitations be worked around and/or fixed?

I've tried to use const, but it typically doesn't happen much because of the bugs with inout and the fact that Object isn't yet const-correct (that being a huge const-killer). Also, the fact that you have to use Rebindable is highly annoying. Still, aside from the bugs, const doesn't seem all that bad to deal with (still I just don't have enough experience with it due to aforementioned bugs). immutable is far worse because there's no easy way (certainly no generic way) to get immutable versions of stuff other than arrays. At least with const, you can generally pass non-const stuff to it and it's fine, but you can't do the same with immutable. The lack of a mutable qualifier seems like it could be another big problem, but I haven't yet written enough code in D to run into that issue (still I'm sure I'll run into it eventually and be highly annoyed - it's just needed too often in C++ for me not to be happy about it missing in D). I'm a huge fan of const in C++, and I _really_ want to be able to use it in D (the lack of const in D1 is one of the big reasons that chose to use D2 even when it was new rather than mess with D1), but the bugs related to it have made it difficult, and I just haven't written very much large stuff in D - and that's where you really need it. - Jonathan M Davis
Aug 12 2010
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:
 The lack of a mutable qualifier seems like it could be 
 another big problem,

I was thinking about a Deconst!() template... Bye, bearophile
Aug 12 2010
parent dsimcha <dsimcha yahoo.com> writes:
== Quote from bearophile (bearophileHUGS lycos.com)'s article
 Jonathan M Davis:
 The lack of a mutable qualifier seems like it could be
 another big problem,

Bye, bearophile

Are you sure std.traits.Unqual doesn't do what you want? It shallowly removes const/immutable/shared. For example: static assert(is(Unqual!(immutable(char[])) == immutable(char)[]))); static assert(is(Unqual!(immutable(int) == int));
Aug 12 2010
prev sibling parent Jonathan M Davis <jmdavisProg gmail.com> writes:
Jonathan M Davis wrote:
 The
 lack of a mutable qualifier seems like it could be another big problem,
 but I haven't yet written enough code in D to run into that issue (still
 I'm sure I'll run into it eventually and be highly annoyed - it's just
 needed too often in C++ for me not to be happy about it missing in D).

Or rather, it's just needed too often in C++ for me not to be _un_happy about it missing in D. - Jonathan M Davis
Aug 12 2010
prev sibling next sibling parent Justin Johansson <no spam.com> writes:
dsimcha wrote:
 This is from a discussion that originated on the Phobos mailing list, but I
 thought I'd bring up the question of what should be done about const on the
 newsgroup to see what others think:
 
 Despite its theoretical beauty, I find D's const/immutable system to be
 utterly useless for all but the simplest use cases.  I made a serious attempt
 a while back to use it in a real multithreaded program.  In hindsight it was
 more trouble than it was worth, largely for three reasons:
 
 1.   It's difficult to create non-trivial immutable data structures, and often
 impossible without relying on either unchecked casts or unnecessary copying.
 
 2.  Much generic code in Phobos (even things as simple as std.math.pow()
 before I recently fixed it) behaves incorrectly when given const/immutable
 data.  This also applies to other libraries I use, including ones that I'm the
 main author of, so I'm just as guilty of it as anyone.  Given that noone,
 including me, seems to be able to get const to interact well with generic
 code, perhaps we need a language-level solution.
 
 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our plates.)
 
 It would have probably been better if this was brought to a head sooner, but
 it's better late than never.  Do others agree that D's const system is
 difficult to impossible to use properly?  Has anyone successfully used D's
 const system in a non-trivial setting despite these limitations?  If so, was
 it more trouble than it was worth in hindsight?  How can these limitations be
 worked around and/or fixed?

Unfortunately, from my experience in a non-trivial setting, the work-around seems to be not to use const/immutable. It is very very br o k e n. As the const/immutable implementation currently stands, it definitely not worth the trouble. You will have weeks and weeks of pain only to have to just about throw your work out and start over. Sad but true. Your sentiments are mine also. Justin Johansson
Aug 12 2010
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-12 18:56:50 -0400, dsimcha <dsimcha yahoo.com> said:

 It would have probably been better if this was brought to a head 
 sooner, but it's better late than never.  Do others agree that D's 
 const system is difficult to impossible to use properly?

I agree. Rebindable looks like a hack, as do creating immutable objects or structs.
 Has anyone successfully used D's const system in a non-trivial setting 
 despite these limitations?

I tried to use immutable objects once, to then find out I'd need to use Rebindable everywhere, and not being able to use Rebindable correctly (it was buggy) made me abandon the idea. Related bugs: <http://d.puremagic.com/issues/show_bug.cgi?id=3625> <http://d.puremagic.com/issues/show_bug.cgi?id=3626>
 If so, was it more trouble than it was worth in hindsight?

At one point, I gave up. I don't use const or immutable except for trivial things, such as array of primitive types.
 How can these limitations be worked around and/or fixed?

There need to be a concerted effort to make Phobos more aware of not-so-new-anymore features such as const/immutable. Phobos should be the test bed for any new feature. Currently, it seems that new features got added to the compiler one after another, but they were never really tested in a real setting, only in small synthetic test cases. This is not how you make a practical language. Those "new features" I'm talking about includes the most notable D2 features: const/immutable, shared, safe, pure, did I miss any? It's simple: only once Phobos becomes <new feature>-correct will we be able to use <new feature> in our own programs. And this process of improving Phobos will reveal lacking areas in the specification/implementation of the language that will need to be fixed: I know because I stumbled upon a couple of them myself. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 12 2010
prev sibling next sibling parent reply Graham St Jack <Graham.StJack internode.on.net> writes:
I have tried using const/immutable/shared several times, and am 
currently in the process of giving it another shot, with much more 
success that before, but it is hard work.

The major language hassle for me is that a class reference has the same 
const/immutable/shared type as the object is refers to, unlike pointers. 
I know that Rebindable is a workaround, but I don't find it satisfactory 
- essentially it uses brute-force casting to defeat the type system, and 
isn't syntactically identical to the bound type.

The data I pass between threads has to pass the !hasAliasing test, and I 
haven't found a practical way to make it work for classes. What works 
best for me is a struct with a pointer to immutable data like so:

struct Foo {
     struct Payload {
         ...
    }
    immutable(Payload)* payload;
    ...
}

Foo can then be passed, contained by value and assigned to, just like 
any other value type. Containing an immutable instance of Foo is ok too, 
if you need another layer of !hasAliasing. I tend to define all my 
non-trivial building-block data types this way, and pass them by value.

With careful design of my multi-threaded code, I can define a set of 
messages to pass between threads that don't need to contain complex data 
structures. The resultant design is always an improvement over the more 
complex interface that first comes to mind, so I find that the type 
system is pushing me in a good direction.

The next big hurdle (as you pointed out) is that druntime and phobos 
aren't designed for const/immutable/shared. For example, InternetAddress 
fails the !hasAliasing test, and concurrency Mailboxes defeat the type 
system rather than being shared themselves, and don't insisting that 
message parameters have to be !hasAliasing. So far I have had to 
implement my own concurrency, socket and condition modules to overcome 
these issues, and I'm sure those are just the beginning.

So there are plenty of bumps in the road, but it does look like it is 
finally possible to use const/immutable/shared if you are determined 
enough. That said, it needs to be way, way easier before wide adoption.

The library issues can be addressed easily enough - It didn't take me 
long to pull off versions that worked ok (Linux only I'm afraid). 
However, the issue with classes doesn't look so easy, and that seriously 
limits what can be passed in messages.

Is there any plan to introduce some way of having a mutable reference to 
an immutable class object on the heap? Do others see this as a problem 
at all?

On 13/08/10 08:26, dsimcha wrote:
 This is from a discussion that originated on the Phobos mailing list, but I
 thought I'd bring up the question of what should be done about const on the
 newsgroup to see what others think:

 Despite its theoretical beauty, I find D's const/immutable system to be
 utterly useless for all but the simplest use cases.  I made a serious attempt
 a while back to use it in a real multithreaded program.  In hindsight it was
 more trouble than it was worth, largely for three reasons:

 1.   It's difficult to create non-trivial immutable data structures, and often
 impossible without relying on either unchecked casts or unnecessary copying.

 2.  Much generic code in Phobos (even things as simple as std.math.pow()
 before I recently fixed it) behaves incorrectly when given const/immutable
 data.  This also applies to other libraries I use, including ones that I'm the
 main author of, so I'm just as guilty of it as anyone.  Given that noone,
 including me, seems to be able to get const to interact well with generic
 code, perhaps we need a language-level solution.

 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our plates.)

 It would have probably been better if this was brought to a head sooner, but
 it's better late than never.  Do others agree that D's const system is
 difficult to impossible to use properly?  Has anyone successfully used D's
 const system in a non-trivial setting despite these limitations?  If so, was
 it more trouble than it was worth in hindsight?  How can these limitations be
 worked around and/or fixed?
    

-- Graham St Jack
Aug 12 2010
next sibling parent reply Justin Johansson <no spam.com> writes:
Graham St Jack wrote:
 Is there any plan to introduce some way of having a mutable reference to 
 an immutable class object on the heap? Do others see this as a problem 
 at all?

For embedded microsystems (i.e. with ROM/RAM) this is a problem. It is a common use case to have a mutable reference (in RAM) to some objects that reside in ROM. Obviously anything in ROM is guaranteed by hardware to be immutable. So, yes, this is a problem in a wider sense.
Aug 12 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Justin Johansson" <no spam.com> wrote in message 
news:i424ac$27nb$1 digitalmars.com...
 Graham St Jack wrote:
 Is there any plan to introduce some way of having a mutable reference to 
 an immutable class object on the heap? Do others see this as a problem at 
 all?

For embedded microsystems (i.e. with ROM/RAM) this is a problem. It is a common use case to have a mutable reference (in RAM) to some objects that reside in ROM. Obviously anything in ROM is guaranteed by hardware to be immutable. So, yes, this is a problem in a wider sense.

Would there every really be anything in ROM though that would be appropriate as a class though, as opposed to, say, a struct? I've never heard of an object with a vtable being stored in ROM.
Aug 12 2010
parent reply Justin Johansson <no spam.com> writes:
Nick Sabalausky wrote:
 "Justin Johansson" <no spam.com> wrote in message 
 news:i424ac$27nb$1 digitalmars.com...
 Graham St Jack wrote:
 Is there any plan to introduce some way of having a mutable reference to 
 an immutable class object on the heap? Do others see this as a problem at 
 all?

is a common use case to have a mutable reference (in RAM) to some objects that reside in ROM. Obviously anything in ROM is guaranteed by hardware to be immutable. So, yes, this is a problem in a wider sense.

Would there every really be anything in ROM though that would be appropriate as a class though, as opposed to, say, a struct? I've never heard of an object with a vtable being stored in ROM.

Yes, well back in my embedded C++ days yes did so. But just because I did doesn't necessarily make it a common use case. Strike 'common' above and replace with 'valid. Anyway, what about a mutable reference to an immutable struct (in ROM)? Same argument. Also there is no reason why vtables cannot be stored in ROM along with code. The only stuff to have in RAM is the mutable stuff! :-)
Aug 12 2010
parent reply "Nick Sabalausky" <a a.a> writes:
"Justin Johansson" <no spam.com> wrote in message 
news:i42ba3$1br$1 digitalmars.com...
 Nick Sabalausky wrote:
 "Justin Johansson" <no spam.com> wrote in message 
 news:i424ac$27nb$1 digitalmars.com...
 Graham St Jack wrote:
 Is there any plan to introduce some way of having a mutable reference 
 to an immutable class object on the heap? Do others see this as a 
 problem at all?

is a common use case to have a mutable reference (in RAM) to some objects that reside in ROM. Obviously anything in ROM is guaranteed by hardware to be immutable. So, yes, this is a problem in a wider sense.

Would there every really be anything in ROM though that would be appropriate as a class though, as opposed to, say, a struct? I've never heard of an object with a vtable being stored in ROM.

Yes, well back in my embedded C++ days yes did so. But just because I did doesn't necessarily make it a common use case. Strike 'common' above and replace with 'valid.

Interesting.
 Anyway, what about a mutable reference to an immutable struct (in ROM)?

Since structs are value types in D, wouldn't a reference to it (mutable or otherwise) *have* to be a pointer?
Aug 12 2010
parent Justin Johansson <no spam.com> writes:
Nick Sabalausky wrote:
 "Justin Johansson" <no spam.com> wrote in message 
 news:i42ba3$1br$1 digitalmars.com...
 Nick Sabalausky wrote:
 "Justin Johansson" <no spam.com> wrote in message 
 news:i424ac$27nb$1 digitalmars.com...
 Graham St Jack wrote:
 Is there any plan to introduce some way of having a mutable reference 
 to an immutable class object on the heap? Do others see this as a 
 problem at all?

is a common use case to have a mutable reference (in RAM) to some objects that reside in ROM. Obviously anything in ROM is guaranteed by hardware to be immutable. So, yes, this is a problem in a wider sense.

appropriate as a class though, as opposed to, say, a struct? I've never heard of an object with a vtable being stored in ROM.

did doesn't necessarily make it a common use case. Strike 'common' above and replace with 'valid.

Interesting.
 Anyway, what about a mutable reference to an immutable struct (in ROM)?

Since structs are value types in D, wouldn't a reference to it (mutable or otherwise) *have* to be a pointer?

Probably you are right. I was mainly talking in general terms about embedded systems (ROM/RAM architectures) and with C++ experience in this area. I have never used D in an embedded environment so YMMV as far as my comments are concerned.
Aug 13 2010
prev sibling next sibling parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Graham St Jack napisał:

 The major language hassle for me is that a class reference has the same
 const/immutable/shared type as the object is refers to, unlike pointers.
 I know that Rebindable is a workaround, but I don't find it satisfactory
 - essentially it uses brute-force casting to defeat the type system, and
 isn't syntactically identical to the bound type.
 
 [snip]
 
 Is there any plan to introduce some way of having a mutable reference to
 an immutable class object on the heap? Do others see this as a problem
 at all?

I see it as a huge problem too. Crazy idea: introduce tail to be used in combo with const/immutable. I'd find it even useful on structs. tail immutable struct Ref { int[] array; int* pointer; int value; } This would be the same as: struct Ref { immutable(int)[] array; immutable(int)* pointer; int value; } No need to examine types field by field to impose such a popular constraint on your structure. But the real money comes from finally having a language-legal way to express a mutable reference to an immutable class. Not to mention that backwards-compatibility is kept (no book will suffer). Tomek
Aug 13 2010
parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Steven Schveighoffer napisał:

 On Fri, 13 Aug 2010 15:46:30 -0400, Tomek Sowiński <just ask.me> wrote:
 
 I see it as a huge problem too. Crazy idea: introduce  tail to be used
 in combo with const/immutable. I'd find it even useful on structs.

  tail immutable struct Ref {
     int[] array;
     int* pointer;
     int value;
 }

 This would be the same as:

 struct Ref {
     immutable(int)[] array;
     immutable(int)* pointer;
     int value;
 }

 No need to examine types field by field to impose such a popular
 constraint on your structure. But the real money comes from finally
 having a language-legal way to express a mutable reference to an
 immutable class.

 Not to mention that backwards-compatibility is kept (no book will
 suffer).

This exposes another important deficiency for const/immutable. One of the coolest things about arrays is how you can implicitly cast them. For example, I can implicitly cast int[] to const(int)[], or const(int[]) to const(int)[], or immutable(int[]) to const(int)[]. But we have no equivalent way to do this for custom types. Why care? Well, one of the most useful data concepts ever needs this ability -- ranges. It's one of the major reasons dcollections has not yet added const support. If a dcollections container wanted to support const properly, it means I need to define a const range, a const cusror, an immutable range and an immutable cursor. Why? Because a const(range) is utterly useless -- popFront doesn't work (it modifies the range!). In addition, it would be nice to be able to specify that a function accepts a const range, and pass it a non-const range for an implicit conversion. But if I define two ranges, the compiler sees them as unrelated separate types, so it will not allow it. Your way to specify a tail const would be really helpful to achieve this goal, because it's not templated, so you avoid some awkward decisions.

Glad you like it. Time ago I had an overzealous attempt to port QuantLib to D and I quickly found out that the *only* sort of const I needed was tail const. It's what I ended up declaring fields of my structs because if just one field is from-head const, the restrictions are basically the same as if the whole struct was const (which is correct but...). It's what the parameters of pure functions should implicitly be declared to (no big deal, but still). From-head const gives me no upper hand in the multi-threaded domain, just pain in my rectum domain. Sure, from-head const is at times useful, but tail const is the workhorse.
 One thing however, do you have a problem making  tail a type modifier
 instead of a storage class?  Because I need that in order to do the
 implicit casting.  i.e.:
 
 struct Ref {
     int[] array;
     int* pointer;
     int value;
 }
 
  tail immutable Ref x;
 
 is equivalent to declaring x as a struct like this:
 
 struct Ref {
     immutable(int)[] array;
     immutable(int)* pointer;
     int value;
 }
 
 I'm assuming you meant this, because I don't know how you would declare a
 tail-const class reference if it was simply a storage class.

Yes.
 I also liked Michel Fortin's const(Object) ref idea, but it really only
 addresses the tail-const class reference problem.  Having a universal
 method to apply tail const that is valid for all types really makes things
 easier.

Yes.
 Note to all who discussed this before: when we last discussed how to do
 tail-const,  attributes were not available, this would be a good time to
 revisit how we could use attributes as Tomek suggests.

Yes. ;) Tomek
Aug 13 2010
parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Jonathan M Davis napisał:

 On Friday, August 13, 2010 14:10:20 Tomek Sowiński wrote:
 Glad you like it. Time ago I had an overzealous attempt to port QuantLib
 to D and I quickly found out that the *only* sort of const I needed was
 tail const. It's what I ended up declaring fields of my structs because
 if just one field is from-head const, the restrictions are basically the
 same as if the whole struct was const (which is correct but...). It's
 what the parameters of pure functions should implicitly be declared to
 (no big deal, but still). From-head const gives me no upper hand in the
 multi-threaded domain, just pain in my rectum domain. Sure, from-head
 const is at times useful, but tail const is the workhorse.

That's exactly why final in Java is so useless for objects. It makes the wrong thing const. How often do you have const pointers in C++? Very rarely. How often do you have pointers to const in C++? All the time (assuming that you're trying to be at all const-correct anyway). At the moment, D is falling into a pit similar to that of Java with const and references. D's is better since the object _is_ const, but since you usually just want the object const, not the reference, it's incredibly counter-productive.

I agree with the content, but not the tone. D's const makes all other mainstream const systems look petty. Applying the concept of transitivity has been revolutionary (hail Walter). Tail const is just a cable to the socket to make this wonderful device work out-of-box for programming masses. Tomek
Aug 13 2010
parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Jonathan M Davis (jmdavisprog gmail.com)'s article
 On Friday, August 13, 2010 15:03:07 Tomek Sowiński wrote:
 I agree with the content, but not the tone. D's const makes all other
 mainstream const systems look petty. Applying the concept of transitivity
 has been revolutionary (hail Walter). Tail const is just a cable to the
 socket to make this wonderful device work out-of-box for programming
 masses.

ic. It really simplified things in comparison to C++, and is overall a definite improvement. It's just with references that there's a big problem, and with them, they're better than what you get with Java's final, but it's still seriously lacking due to the whole thing becoming const instead of just the referent. - Jonathan M Davis

I still don't understand: What's so bad about Rebindable? Yes, it's not the syntactically prettiest thing in the world, but complaining about it is like complaining about climbing a molehill when you've got Mount Everest to climb next. My previous gripe about it was that it didn't support interfaces, but I just realized that Shin Fujishiro fixed this a while back.
Aug 13 2010
next sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Jonathan M Davis (jmdavisprog gmail.com)'s article
 On Friday, August 13, 2010 16:30:46 dsimcha wrote:
 I still don't understand:  What's so bad about Rebindable?  Yes, it's not
 the syntactically prettiest thing in the world, but complaining about it
 is like complaining about climbing a molehill when you've got Mount
 Everest to climb next. My previous gripe about it was that it didn't
 support interfaces, but I just realized that Shin Fujishiro fixed this a
 while back.

fairly superficial complaint. Part of it is the fact that it really _should_ be in the language itself rather than having to use Rebindable. Honestly, I haven't messed with it in a while, so I don't remember all of the issues. I've never liked it, and I've always had trouble in getting it to work. Now, maybe that's totally due to bugs that may be fixed now, but it's always been problematic, and certainly my gut reaction is that the type system is deficient because it can't do it itself. Having to use Rebindable!() is definitely worse than being able to do things like const (T)*. But maybe with all of the bugs worked out and just getting used it, it's a good and reasonable solution. Honestly though, I've never been able to get it to work right, and I've never been happy about the fact that the type system can't handle it itself. Obviously, I need to go back and try and use it again. However, with inout broken and Object not being const- correct, I'm not sure that I'll get very far with using const and immutable anyway. Still, it would be so nice if it could just be handled cleanly by the type system. - Jonathan M Davis

http://dsource.org/projects/phobos/changeset/1849 I've added some trivial convenience functions to Phobos that should have been there a long time ago and should go a long way towards making Rebindable more usable. The other thing we need is bug fixes in alias this/opDot, which we need eventually anyhow. Now, instead of doing something like this ugly, verbose, specify-things-twice code: auto foo = Rebindable!(LongTypeName)(new LongTypeName); // Shoot me. You can do: auto foo = rebindable(new LongTypeName); // Sanity restored.
Aug 13 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-13 20:45:13 -0400, dsimcha <dsimcha yahoo.com> said:

 http://dsource.org/projects/phobos/changeset/1849
 
 I've added some trivial convenience functions to Phobos that should have been
 there a long time ago and should go a long way towards making Rebindable more
 usable.  The other thing we need is bug fixes in alias this/opDot, 
 which we need
 eventually anyhow.  Now, instead of doing something like this ugly, verbose,
 specify-things-twice code:
 
 auto foo = Rebindable!(LongTypeName)(new LongTypeName);  // Shoot me.
 
 You can do:
 
 auto foo = rebindable(new LongTypeName);  // Sanity restored.

Now the question for genericity's sake is: should Unqual!(const(Object)) give you the type Rebindable!(const(Object))? Unfortunately, you probably can't get this one right without some help from the compiler: immutable(Rebindable!(const(Object)))? And what will Unqual do with this: Unqual!(immutable(Rebindable!(const(Object)))? -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 13 2010
prev sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 On Fri, 13 Aug 2010 19:30:46 -0400, dsimcha <dsimcha yahoo.com> wrote:
 == Quote from Jonathan M Davis (jmdavisprog gmail.com)'s article
 On Friday, August 13, 2010 15:03:07 Tomek Sowiński wrote:
 I agree with the content, but not the tone. D's const makes all other
 mainstream const systems look petty. Applying the concept of

 has been revolutionary (hail Walter). Tail const is just a cable to

 socket to make this wonderful device work out-of-box for programming
 masses.

fantast ic. It really simplified things in comparison to C++, and is overall a definite improvement. It's just with references that there's a big problem, and with them, they're better than what you get with Java's final, but it's still seriously lacking due to the whole thing becoming const instead of just the referent. - Jonathan M Davis

I still don't understand: What's so bad about Rebindable? Yes, it's not the syntactically prettiest thing in the world, but complaining about it is like complaining about climbing a molehill when you've got Mount Everest to climb next. My previous gripe about it was that it didn't support interfaces, but I just realized that Shin Fujishiro fixed this a while back.

example, there's no equivalent custom-range idiom for const(T)[]. You simply can't make a custom range tail-const. I admit I haven't used Rebindable much, but last I checked it was severely out of date. -Steve

Rebindable's gotten a significant facelift lately. Admittedly there are still bugs but these are bugs in alias this/opDot, not in rebindable itself.
Aug 16 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 13 Aug 2010 15:46:30 -0400, Tomek Sowiński <just ask.me> wrote:

 Graham St Jack napisał:

 The major language hassle for me is that a class reference has the same
 const/immutable/shared type as the object is refers to, unlike pointers.
 I know that Rebindable is a workaround, but I don't find it satisfactory
 - essentially it uses brute-force casting to defeat the type system, and
 isn't syntactically identical to the bound type.

 [snip]

 Is there any plan to introduce some way of having a mutable reference to
 an immutable class object on the heap? Do others see this as a problem
 at all?

I see it as a huge problem too. Crazy idea: introduce tail to be used in combo with const/immutable. I'd find it even useful on structs. tail immutable struct Ref { int[] array; int* pointer; int value; } This would be the same as: struct Ref { immutable(int)[] array; immutable(int)* pointer; int value; } No need to examine types field by field to impose such a popular constraint on your structure. But the real money comes from finally having a language-legal way to express a mutable reference to an immutable class. Not to mention that backwards-compatibility is kept (no book will suffer).

This exposes another important deficiency for const/immutable. One of the coolest things about arrays is how you can implicitly cast them. For example, I can implicitly cast int[] to const(int)[], or const(int[]) to const(int)[], or immutable(int[]) to const(int)[]. But we have no equivalent way to do this for custom types. Why care? Well, one of the most useful data concepts ever needs this ability -- ranges. It's one of the major reasons dcollections has not yet added const support. If a dcollections container wanted to support const properly, it means I need to define a const range, a const cusror, an immutable range and an immutable cursor. Why? Because a const(range) is utterly useless -- popFront doesn't work (it modifies the range!). In addition, it would be nice to be able to specify that a function accepts a const range, and pass it a non-const range for an implicit conversion. But if I define two ranges, the compiler sees them as unrelated separate types, so it will not allow it. Your way to specify a tail const would be really helpful to achieve this goal, because it's not templated, so you avoid some awkward decisions. One thing however, do you have a problem making tail a type modifier instead of a storage class? Because I need that in order to do the implicit casting. i.e.: struct Ref { int[] array; int* pointer; int value; } tail immutable Ref x; is equivalent to declaring x as a struct like this: struct Ref { immutable(int)[] array; immutable(int)* pointer; int value; } I'm assuming you meant this, because I don't know how you would declare a tail-const class reference if it was simply a storage class. I also liked Michel Fortin's const(Object) ref idea, but it really only addresses the tail-const class reference problem. Having a universal method to apply tail const that is valid for all types really makes things easier. Note to all who discussed this before: when we last discussed how to do tail-const, attributes were not available, this would be a good time to revisit how we could use attributes as Tomek suggests. -Steve
Aug 13 2010
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
--e0cb4e887827ccb097048da94b21
Content-Type: text/plain; charset=ISO-8859-1

A few years ago I had a look at D, and I remember there were some NG posts
about broken const and such. I honestly thought whatever that was broken was
fixed by now. I haven't used D too much, so I'm yet to be affected by this.
From the looks of posts it seems to be bad though.

On Fri, Aug 13, 2010 at 1:48 AM, bearophile <bearophileHUGS lycos.com>wrote:
 Jonathan M Davis:
 The lack of a mutable qualifier seems like it could be
 another big problem,

I was thinking about a Deconst!() template... Bye, bearophile

--e0cb4e887827ccb097048da94b21 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable A few years ago I had a look at D, and I remember there were some NG posts = about broken const and such. I honestly thought whatever that was broken wa= s fixed by now. I haven&#39;t used D too much, so I&#39;m yet to be affecte= d by this. From the looks of posts it seems to be bad though. <br> <br><div class=3D"gmail_quote">On Fri, Aug 13, 2010 at 1:48 AM, bearophile = <span dir=3D"ltr">&lt;<a href=3D"mailto:bearophileHUGS lycos.com">bearophil= eHUGS lycos.com</a>&gt;</span> wrote:<br><blockquote class=3D"gmail_quote" = style=3D"margin: 0pt 0pt 0pt 0.8ex; border-left: 1px solid rgb(204, 204, 20= 4); padding-left: 1ex;"> Jonathan M Davis:<br> <div class=3D"im">&gt; The lack of a mutable qualifier seems like it could = be<br> &gt; another big problem,<br> <br> </div>I was thinking about a Deconst!() template...<br> <br> Bye,<br> <font color=3D"#888888">bearophile<br> </font></blockquote></div><br> --e0cb4e887827ccb097048da94b21--
Aug 12 2010
prev sibling next sibling parent reply Trass3r <un known.com> writes:
Well isn't it natural that a constness system has a much larger impact  
than just some syntax additions.
I don't see any flaws in its design. The implementation is of course still  
buggy though and needs to mature.
Aug 12 2010
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Trass3r wrote:
 Well isn't it natural that a constness system has a much larger impact 
 than just some syntax additions.
 I don't see any flaws in its design. The implementation is of course 
 still buggy though and needs to mature.

I agree. There are very severe bugs and undue limitations in today's const. Having a comprehensive discussion of const's status and role in current and future D idioms is a great idea. We should start it with a scrutiny of the reported and possibly unreported bugs in the feature. Andrei
Aug 12 2010
parent reply Graham St Jack <Graham.StJack internode.on.net> writes:
On 13/08/10 10:08, Andrei Alexandrescu wrote:
 Trass3r wrote:
 Well isn't it natural that a constness system has a much larger 
 impact than just some syntax additions.
 I don't see any flaws in its design. The implementation is of course 
 still buggy though and needs to mature.

I agree. There are very severe bugs and undue limitations in today's const. Having a comprehensive discussion of const's status and role in current and future D idioms is a great idea. We should start it with a scrutiny of the reported and possibly unreported bugs in the feature. Andrei

Most of the roadblocks are relatively easy to overcome by rolling const-correctness through druntime and phobos. However, I still regard the language design decision of a class reference having the same constness as the object it refers to as a major language design problem. I would be delighted if someone could point out to me how to neatly work around this though. -- Graham St Jack
Aug 12 2010
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Graham St Jack wrote:
 However, I still regard the language design decision of a class 
 reference having the same constness as the object it refers to as a 
 major language design problem.

We tried for months. It just doesn't work to make it any other way than it is now.
Aug 12 2010
parent Kagamin <spam here.lot> writes:
Walter Bright Wrote:

 Graham St Jack wrote:
 However, I still regard the language design decision of a class 
 reference having the same constness as the object it refers to as a 
 major language design problem.

We tried for months. It just doesn't work to make it any other way than it is now.

Is it compiler infrastructure's or syntactical issue?
Aug 12 2010
prev sibling next sibling parent reply Brad Roberts <braddr slice-2.puremagic.com> writes:
On Thu, 12 Aug 2010, dsimcha wrote:

 This is from a discussion that originated on the Phobos mailing list, but I
 thought I'd bring up the question of what should be done about const on the
 newsgroup to see what others think:
 
 Despite its theoretical beauty, I find D's const/immutable system to be
 utterly useless for all but the simplest use cases.  I made a serious attempt
 a while back to use it in a real multithreaded program.  In hindsight it was
 more trouble than it was worth, largely for three reasons:
 
 1.   It's difficult to create non-trivial immutable data structures, and often
 impossible without relying on either unchecked casts or unnecessary copying.
 
 2.  Much generic code in Phobos (even things as simple as std.math.pow()
 before I recently fixed it) behaves incorrectly when given const/immutable
 data.  This also applies to other libraries I use, including ones that I'm the
 main author of, so I'm just as guilty of it as anyone.  Given that noone,
 including me, seems to be able to get const to interact well with generic
 code, perhaps we need a language-level solution.
 
 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our plates.)
 
 It would have probably been better if this was brought to a head sooner, but
 it's better late than never.  Do others agree that D's const system is
 difficult to impossible to use properly?  Has anyone successfully used D's
 const system in a non-trivial setting despite these limitations?  If so, was
 it more trouble than it was worth in hindsight?  How can these limitations be
 worked around and/or fixed?

For discussions like this, I think it's essential to distinguish between the language vs the runtime + core libraries. I recognize what matters is the end result usability, but examining the layers independently is really important. So, which are you talking about (could well be both)?
Aug 12 2010
next sibling parent reply Graham St Jack <Graham.StJack internode.on.net> writes:
On 13/08/10 09:51, Brad Roberts wrote:
 On Thu, 12 Aug 2010, dsimcha wrote:

    
 This is from a discussion that originated on the Phobos mailing list, but I
 thought I'd bring up the question of what should be done about const on the
 newsgroup to see what others think:

 Despite its theoretical beauty, I find D's const/immutable system to be
 utterly useless for all but the simplest use cases.  I made a serious attempt
 a while back to use it in a real multithreaded program.  In hindsight it was
 more trouble than it was worth, largely for three reasons:

 1.   It's difficult to create non-trivial immutable data structures, and often
 impossible without relying on either unchecked casts or unnecessary copying.

 2.  Much generic code in Phobos (even things as simple as std.math.pow()
 before I recently fixed it) behaves incorrectly when given const/immutable
 data.  This also applies to other libraries I use, including ones that I'm the
 main author of, so I'm just as guilty of it as anyone.  Given that noone,
 including me, seems to be able to get const to interact well with generic
 code, perhaps we need a language-level solution.

 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our plates.)

 It would have probably been better if this was brought to a head sooner, but
 it's better late than never.  Do others agree that D's const system is
 difficult to impossible to use properly?  Has anyone successfully used D's
 const system in a non-trivial setting despite these limitations?  If so, was
 it more trouble than it was worth in hindsight?  How can these limitations be
 worked around and/or fixed?
      

the language vs the runtime + core libraries. I recognize what matters is the end result usability, but examining the layers independently is really important. So, which are you talking about (could well be both)?

const/immutable/shared attribute as the object on the heap that it refers to. This is sometimes what you need, but more often you want a non-shared, mutable reference to a const/immutable/shared object. You can achieve this with pointers for arrays, structs and primitive types, but not with classes because a class pointer is just a pointer to a reference. -- Graham St Jack
Aug 12 2010
next sibling parent Graham St Jack <Graham.StJack internode.on.net> writes:
On 13/08/10 10:18, Jonathan M Davis wrote:
 On Thursday, August 12, 2010 17:38:28 Graham St Jack wrote:
    
 For me, the key problem is that a class object reference has the same
 const/immutable/shared attribute as the object on the heap that it
 refers to. This is sometimes what you need, but more often you want a
 non-shared, mutable reference to a const/immutable/shared object.

 You can achieve this with pointers for arrays, structs and primitive
 types, but not with classes because a class pointer is just a pointer to
 a reference.
      

Oh, and you _can_ achieve pointers to classes, but what you normally use are references, which do have the problem of not being able to be split between the reference and referent types. - Jonathan M Davis

object reference gives you a pointer to the reference, not the object, which is fair enough. As far as I know there isn't a way to get a pointer to the object itself, and even if you could, how do you use such a thing? -- Graham St Jack
Aug 12 2010
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
Graham St Jack wrote:
 For me, the key problem is that a class object reference has the same 
 const/immutable/shared attribute as the object on the heap that it 
 refers to. This is sometimes what you need, but more often you want a 
 non-shared, mutable reference to a const/immutable/shared object.

I struggled with this for a long while before I eventually came to the conclusion that I was thinking about class objects as a reference and the instance, and that those two were separable concepts. They are not. A reference type is implicitly treated like a value type as far as accessing its members go. Trying to separate out the two is a fundamental misunderstanding of what reference types are all about. Semantically, they are much more than just a pointer to the instance.
 You can achieve this with pointers for arrays, structs and primitive 
 types, but not with classes because a class pointer is just a pointer to 
 a reference.

The way to do it with class is take a pointer to the class: class C { ... } const(C)*[]; // array of pointers to const classes just like you'd do with a struct.
Aug 13 2010
parent Graham St Jack <Graham.StJack internode.on.net> writes:
On 14/08/10 05:55, Walter Bright wrote:
 Graham St Jack wrote:
 For me, the key problem is that a class object reference has the same
 const/immutable/shared attribute as the object on the heap that it
 refers to. This is sometimes what you need, but more often you want a
 non-shared, mutable reference to a const/immutable/shared object.

I struggled with this for a long while before I eventually came to the conclusion that I was thinking about class objects as a reference and the instance, and that those two were separable concepts. They are not. A reference type is implicitly treated like a value type as far as accessing its members go. Trying to separate out the two is a fundamental misunderstanding of what reference types are all about. Semantically, they are much more than just a pointer to the instance.
 You can achieve this with pointers for arrays, structs and primitive
 types, but not with classes because a class pointer is just a pointer
 to a reference.

The way to do it with class is take a pointer to the class: class C { ... } const(C)*[]; // array of pointers to const classes just like you'd do with a struct.

I get it - I think. I will give this a try and see how it works in practice. -- Graham St Jack
Aug 13 2010
prev sibling parent reply dsimcha <dsimcha yahoo.com> writes:
== Quote from Brad Roberts (braddr slice-2.puremagic.com)'s article
 For discussions like this, I think it's essential to distinguish between
 the language vs the runtime + core libraries.  I recognize what matters is
 the end result usability, but examining the layers independently is really
 important.
 So, which are you talking about (could well be both)?

I disagree. A major reason why the libraries are broken is because the language is broken. const/immutable is very hard to use with generic code, meaning that anything short of impeccable discipline and testing will lead to bugs galore. Fixing the language would make it a lot easier to fix the libraries.
Aug 12 2010
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-12 21:37:11 -0400, Jonathan M Davis <jmdavisprog gmail.com> said:

 3. The lack of a mutable modifier.
 
 There are times when you need a mutable modifier to sanely use const (it
 obviously wouldn't work with immutable regardless). I don't know if there's any
 way to safely do this with a library or not. It just seems like a deficiency in
 the language itself. As I understand it, Walter thinks that there are serious
 issues with mutable in C++, and I don't know what those are, but I know that
 there are going to be cases where I'd like my code to be const-correct, and I
 won't be able to because there is no mutable in D.

Const-correctness can't work the same in D as in C/C++. Even if you had a mutable member in your class, D can't allow you to modify this member from a const member function, because this const member function can be called when your class is immutable, and if the class is immutable it can be shared across threads, in which case modifying the member (without synchronization) would cause races. Basically, D const system isn't build for const-correctness or to enforce what logically looks like const, it's built to ease concurrency and sharing of data between threads. All this to say that when something is const, it *is* const and it can't cause races. Perhaps there could be *synchronized* or *shared* islands that could encapsulate mutable state inside of something immutable -- this would avoid races -- but I think the addition of such a thing should be delayed until we get the current system working and we have some experience with it. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 12 2010
parent Walter Bright <newshound2 digitalmars.com> writes:
Jonathan M Davis wrote:
 That is, unfortunately, a very good point. It's still seriously limiting, 
 however. What it means is that there is going to be code out there (and
possibly 
 a lot of it) which _should_ be const but can't be.

I presume you're talking about "logical constness", for data structures that lazily evaluate or memoize their contents. I think this is a higher level abstraction than what const should be. It is like asking the type system to also distinguish between things like validated data and non-validated data. It can be done, but it's currently beyond the scope of D's type system.
 I confess that the more that I deal with immutable, the less I like it. const 
 doesn't complicate things all that much and brings a lot of improvements. 
 immutable complicates things quite a lot more and does not bring the same
level 
 of improvements to the table. However, it does help with multi-threaded apps 
 which _is_ very important, and increasingly so, so it's probably better to
have 
 it than not. Still, it can be very frustrating. However, if there's a better
way 
 to do it, it'll likely be found with either a future version of D or a
language 
 which replaces D. There's a shortage of languages which have tried to mix
const, 
 mutable, and immutable together (D may be the only one actually), so there
isn't 
 much experience to base improvements on. And is usually the case, it's much 
 easier to see the problems than their solutions.

There are many languages which support immutable - but it's hidden away in how they handle strings. For example, Perl has immutable strings. The great advantage to immutability (aside from threading) is that you can treat a reference type as if it were a value type. This is why so many languages treat strings that way. With D's immutability in the type system, rather than special cased for strings, is you can bring that advantage to any of your user defined types.
Aug 13 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday, August 12, 2010 17:38:28 Graham St Jack wrote:
 For me, the key problem is that a class object reference has the same
 const/immutable/shared attribute as the object on the heap that it
 refers to. This is sometimes what you need, but more often you want a
 non-shared, mutable reference to a const/immutable/shared object.
 
 You can achieve this with pointers for arrays, structs and primitive
 types, but not with classes because a class pointer is just a pointer to
 a reference.

Hence the hack that is Rebindable!(). Oh, and you _can_ achieve pointers to classes, but what you normally use are references, which do have the problem of not being able to be split between the reference and referent types. - Jonathan M Davis
Aug 12 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday, August 12, 2010 18:12:25 dsimcha wrote:
 == Quote from Brad Roberts (braddr slice-2.puremagic.com)'s article
 
 For discussions like this, I think it's essential to distinguish between
 the language vs the runtime + core libraries.  I recognize what matters
 is the end result usability, but examining the layers independently is
 really important.
 So, which are you talking about (could well be both)?

I disagree. A major reason why the libraries are broken is because the language is broken. const/immutable is very hard to use with generic code, meaning that anything short of impeccable discipline and testing will lead to bugs galore. Fixing the language would make it a lot easier to fix the libraries.

I see three primary issues with const/immutable beyond whatever bugs we have, and while improvement to the libraries would help, I'm not sure that they can fix them. 1. The fact that const references make both the reference and the referent const. Rebindable!() works to some extent, but it really feels like a hack and is nowhere near as smooth as some kind of tail const solution would be (though I do understand that having head const and tail const would complicate things considerably). I think that it would have been far better had const and immutable references just made the referent const/immutable. There just _has_ to be a better way to handle it. Maybe have rconst for references or something. It almost tempts me to see if I should just try and use pointers everywhere instead of references. 2. The shear difficulty in getting in getting immutable versions of stuff. There is no automated way to do it other than idup with arrays. I really don't know the cleanest way to solve this. The nature of immutable makes casting to it not work, so you're going to have to make a copy, and if that needs a deep copy, you're going to need clone() for classes and a copy constructor or assignment operator for structs. It's not at all obvious how to make this cleaner. Maybe there could be a function in Phobos that abstracts it out; it would know the correct, standard way to get an immutable copy of classes, structs, arrays, etc. and return the appropriate type. Then generic code could just use that function. Still, getting immutable copies of stuff which isn't always immutable is not pretty. 3. The lack of a mutable modifier. There are times when you need a mutable modifier to sanely use const (it obviously wouldn't work with immutable regardless). I don't know if there's any way to safely do this with a library or not. It just seems like a deficiency in the language itself. As I understand it, Walter thinks that there are serious issues with mutable in C++, and I don't know what those are, but I know that there are going to be cases where I'd like my code to be const-correct, and I won't be able to because there is no mutable in D. I really don't know how many of these issues can be fixed at this stage in the game, and ultimately, the current bugs in dmd make them pretty much a moot issue because the bugs make using const and immutable near impossible in many cases. Still, it would be nice to resolve these issues in a better manner than what we currently have does. - Jonathan M Davis
Aug 12 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday, August 12, 2010 18:16:01 Graham St Jack wrote:
 On 13/08/10 10:18, Jonathan M Davis wrote:
 On Thursday, August 12, 2010 17:38:28 Graham St Jack wrote:
 For me, the key problem is that a class object reference has the same
 const/immutable/shared attribute as the object on the heap that it
 refers to. This is sometimes what you need, but more often you want a
 non-shared, mutable reference to a const/immutable/shared object.
 
 You can achieve this with pointers for arrays, structs and primitive
 types, but not with classes because a class pointer is just a pointer to
 a reference.

Hence the hack that is Rebindable!(). Oh, and you _can_ achieve pointers to classes, but what you normally use are references, which do have the problem of not being able to be split between the reference and referent types. - Jonathan M Davis

So how do you get a pointer to an object? Taking the address of an object reference gives you a pointer to the reference, not the object, which is fair enough. As far as I know there isn't a way to get a pointer to the object itself, and even if you could, how do you use such a thing?

Hmm. I was thinking that you could just do T* t = new T(); but that doesn't work. I know that people have talked about doing it here on the newsgroup, so there must be a way. You can do T t1 = new T(); T* t2 = &t1; but I guess that that's a pointer to a reference rather a pointer to the object itself. Maybe if you want pointers to classes you need to use manual memory manegement rather than the GC and new. Hopefully someone else can enlighten us. I have generally avoided pointers in D. - Jonathan M Davis
Aug 12 2010
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-12 18:56:50 -0400, dsimcha <dsimcha yahoo.com> said:

 How can these limitations be worked around and/or fixed?

Unsatisfaction about Rebindable seems pretty generalized. Here's an idea for a solution. Basically the problem is only in the syntax, where the reference is implicitly part of the object's type and thus impossible to put outside from the type modifier. An easy solution would be to add an explicit reference marker, but this would change the syntax for existing code, and I have to admit the current syntax is nice (up until you try to add a modifier). But we could make the reference marker optional, like this: Object o; // implicitly a reference Object ref o; // explicit reference marker Both would be allowed and equivalent. While the first form is nicer to the eye, the second makes it easy to apply a type modifier while excluding the reference: const(Object)ref o; shared(Object)ref o; -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 12 2010
next sibling parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday 12 August 2010 19:09:51 Michel Fortin wrote:
 On 2010-08-12 18:56:50 -0400, dsimcha <dsimcha yahoo.com> said:
 How can these limitations be worked around and/or fixed?

Unsatisfaction about Rebindable seems pretty generalized. Here's an idea for a solution. Basically the problem is only in the syntax, where the reference is implicitly part of the object's type and thus impossible to put outside from the type modifier. An easy solution would be to add an explicit reference marker, but this would change the syntax for existing code, and I have to admit the current syntax is nice (up until you try to add a modifier). But we could make the reference marker optional, like this: Object o; // implicitly a reference Object ref o; // explicit reference marker Both would be allowed and equivalent. While the first form is nicer to the eye, the second makes it easy to apply a type modifier while excluding the reference: const(Object)ref o; shared(Object)ref o;

Now, _that_ seems like a good idea. It doesn't even require a new keyword. It's also quite clear and understandable. It would be a big improvement I think. There may be downsides of some kind, but I can't think of any. Unless someone can come up with a reason _not_ to do this, it seems to me like it's a really good idea. - Jonathan M Davis P.S. I believe that the word that you were looking for was dissatisfaction, not unsatisfaction (which isn't a real word).
Aug 12 2010
parent Graham St Jack <Graham.StJack internode.on.net> writes:
On 13/08/10 12:16, Jonathan M Davis wrote:
 On Thursday 12 August 2010 19:09:51 Michel Fortin wrote:
    
 On 2010-08-12 18:56:50 -0400, dsimcha<dsimcha yahoo.com>  said:
      
 How can these limitations be worked around and/or fixed?
        

Here's an idea for a solution. Basically the problem is only in the syntax, where the reference is implicitly part of the object's type and thus impossible to put outside from the type modifier. An easy solution would be to add an explicit reference marker, but this would change the syntax for existing code, and I have to admit the current syntax is nice (up until you try to add a modifier). But we could make the reference marker optional, like this: Object o; // implicitly a reference Object ref o; // explicit reference marker Both would be allowed and equivalent. While the first form is nicer to the eye, the second makes it easy to apply a type modifier while excluding the reference: const(Object)ref o; shared(Object)ref o;

also quite clear and understandable. It would be a big improvement I think. There may be downsides of some kind, but I can't think of any. Unless someone can come up with a reason _not_ to do this, it seems to me like it's a really good idea. - Jonathan M Davis P.S. I believe that the word that you were looking for was dissatisfaction, not unsatisfaction (which isn't a real word).

A complimentary change is to automatically apply type modifiers when creating objects, but not when making references to them, like so: immutable class Foo {...} Foo foo = new Foo(); Here, the object on the heap would be immutable and foo would not. Currently class (and struct) type modifiers don't get automatically applied - at least not in ways that I find useful, and I keep having to do laborious stuff like this, which I don't like at all: synchronized class _Foo {} alias shared(_Foo) Foo; Foo foo = new Foo(); or even worse: synchonized class Foo {} shared(Foo) foo = cast(shared) new Foo(); -- Graham St Jack
Aug 12 2010
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Adam Ruppe Wrote:

 On the subject of rebindable, what about:
 
 const Object o; // not rebindable, the whole thing is set once and const
 const(Object) o; // the Object is const, but the reference is not.
 
 So, everything is rebindable unless the declaration has a
 const/immutable on the outside.
 
 int a; // rebindable (obviously)
 const(int) a; // the int never changes... but the variable a might.

int "value" is effectively immutable, it doesn't matter what qualifier you apply to it. The variable can be still reassignable to a different value.
 Meaningless for a value type, but makes sense for a reference type
 const int a; // the whole thing is set once and never changes. The
 const applies to the variable a itself, but the transitive property
 propagates it down to the int type too.

There is a difference between whether you can assign the whole struct or its distinct members. There was some words about this in the docs.
Aug 12 2010
prev sibling next sibling parent Kagamin <spam here.lot> writes:
Adam Ruppe Wrote:

 On the subject of rebindable, what about:
 
 const Object o; // not rebindable, the whole thing is set once and const
 const(Object) o; // the Object is const, but the reference is not.

Oops, this violates the const system because it unclear, whether const(Object)* is a pointer to rebindable or non-rebindable reference.
Aug 12 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Michel Fortin <michel.fortin michelf.com> wrote:

 	Object o; // implicitly a reference
 	Object ref o; // explicit reference marker

 Both would be allowed and equivalent. While the first form is nicer to  
 the eye, the second makes it easy to apply a type modifier while  
 excluding the reference:

 	const(Object)ref o;
 	shared(Object)ref o;

This is awesome. Now, if only W or A could have a look, and not shoot it down... -- Simen
Aug 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 12 Aug 2010 22:23:30 -0400, Adam Ruppe <destructionator gmail.com>  
wrote:

 On the subject of rebindable, what about:

 const Object o; // not rebindable, the whole thing is set once and const
 const(Object) o; // the Object is const, but the reference is not.

 So, everything is rebindable unless the declaration has a
 const/immutable on the outside.

 int a; // rebindable (obviously)
 const(int) a; // the int never changes... but the variable a might.
 Meaningless for a value type, but makes sense for a reference type
 const int a; // the whole thing is set once and never changes. The
 const applies to the variable a itself, but the transitive property
 propagates it down to the int type too.

This was tried in an early version of D2 const. It is too confusing. For const(int) to be modifiable is unacceptable. I know it looks appealing, but trust me, you have about as much chance of convincing Walter to revisit this idea as you have of walking on water. -Steve
Aug 13 2010
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 12 Aug 2010 22:09:51 -0400, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2010-08-12 18:56:50 -0400, dsimcha <dsimcha yahoo.com> said:

 How can these limitations be worked around and/or fixed?

Unsatisfaction about Rebindable seems pretty generalized. Here's an idea for a solution. Basically the problem is only in the syntax, where the reference is implicitly part of the object's type and thus impossible to put outside from the type modifier. An easy solution would be to add an explicit reference marker, but this would change the syntax for existing code, and I have to admit the current syntax is nice (up until you try to add a modifier). But we could make the reference marker optional, like this: Object o; // implicitly a reference Object ref o; // explicit reference marker Both would be allowed and equivalent. While the first form is nicer to the eye, the second makes it easy to apply a type modifier while excluding the reference: const(Object)ref o; shared(Object)ref o;

I really really like this idea. I remember seeing a suggestion like this a long time ago, but I think it was ref const(Object). That is ambiguous because you could have a ref to an object ref. With your idea, I think this would be ref const(Object) ref (a little ugly, but no worse than const const(Object) fn()). I like how it reads naturally. I think it's also syntactically unambiguous. Walter, please give this one some attention, I'd love to see this fixed. -Steve
Aug 13 2010
parent reply Walter Bright <newshound2 digitalmars.com> writes:
Steven Schveighoffer wrote:
 I like how it reads naturally.  I think it's also syntactically 
 unambiguous.  Walter, please give this one some attention, I'd love to 
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work. It wasn't the syntax. There were many syntaxes proposed. The type system loses its coherency with such a special case in it. Generic code has weird problems, type deduction gets strange, types lose their composability, etc. But there is a solution: const(Object)* o; Yes, under the hood it's 2 levels of indirection. But it works, it requires no special syntax, it is completely consistent with the rest of how the type system works, there are no corner holes in it, etc.
Aug 13 2010
next sibling parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
Walter Bright napisał:

 But there is a solution:
 
 const(Object)* o;

Interesting. How do you cook that with polymorphism? This doesn't work: interface I { } class A : I {} void main() { immutable A a = new immutable(A); immutable(A)* ap = &a; // Error: cannot implicitly convert expression (ap) of type immutable(A)* to immutable(I)* immutable(I)* ip = ap; } BTW, should this work?: immutable(A)* ap = &new immutable(A); Now it fails with "new immutable(A) is not an lvalue". Tomek
Aug 13 2010
parent Walter Bright <newshound2 digitalmars.com> writes:
Tomek Sowiński wrote:
 Walter Bright napisał:
 
 But there is a solution:

 const(Object)* o;

Interesting. How do you cook that with polymorphism? This doesn't work: interface I { } class A : I {} void main() { immutable A a = new immutable(A); immutable(A)* ap = &a; // Error: cannot implicitly convert expression (ap) of type immutable(A)* to immutable(I)* immutable(I)* ip = ap; }

The polymorphic bit works on the reference, not the pointer to the reference. A conversion to an interface actually is a change in the bits, so a pointer to an A cannot also be pointed to by a pointer to I.
 BTW, should this work?:
 immutable(A)* ap = &new immutable(A);
 Now it fails with "new immutable(A) is not an lvalue".

Right. You can't take the address of an rvalue. What you do is: immutable(A) a = new immutable(A); immutable(A)* ap = &a; immutable(I) i = a; immutable(I)*pi = &i; Yes, there's another piece of memory involved there (the storage for a, and i).
Aug 13 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work. It wasn't the syntax. There were many syntaxes proposed. The type system loses its coherency with such a special case in it. Generic code has weird problems, type deduction gets strange, types lose their composability, etc. But there is a solution: const(Object)* o; Yes, under the hood it's 2 levels of indirection. But it works, it requires no special syntax, it is completely consistent with the rest of how the type system works, there are no corner holes in it, etc.

Actually this is an even better solution: const(Scoped!Type)* o; The only issue is that you need to know the static type... polymorphism doesn't work with pointers. Andrei
Aug 13 2010
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 08:08:21 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 On Fri, 13 Aug 2010 23:20:55 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:
 
 On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work.


Perhaps part of this is the reluctance to reexamine something which was hard to prove correct given the ideas at the time? However, the idea is attainable. For the simple fact that we have tail-const references in other parts of the language. All that is missing is syntax.
 It wasn't the syntax. There were many syntaxes proposed. The type system
 loses its coherency with such a special case in it. Generic code has
 weird problems, type deduction gets strange, types lose their
 composability, etc.


The fact that all syntaxes proposed back then didn't cut the mustard does not mean that no possible syntax exists. This is not NP != P. Given that we have syntax that works for pointers, it's logical and reasonable to assume that some syntax can work for generic tail-constness of everything. In fact, we had syntax that *worked* it was just confusing.

Indeed. This reflects my opinion too. I'm not saying much about this issue right now because I feel it's no use, or to be precise, so much time convincing is needed to bring something useful I'd rather do something else. I've taken the route where I'll wait until others (Andrei?) hit the same problems and put enough pressure to force the design to be revisited. Hopefully this will happen sooner rather than later. Same situation for safe, same for pure, same for shared. Which means I won't be adopting D2's most interesting features for some time.
 BTW, I like Tomek's idea with  tail const/immutable/shared.  It's not a 
  special case, it's a generic case, you can apply it to any type.

The idea's not bad, but I think it needs some improvments. For instance, in a struct, I might want some members to be part of the tail and some other not. I believe that should be allowed somehow. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 09:08:30 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 On Mon, 16 Aug 2010 08:56:51 -0400, Michel Fortin  
 <michel.fortin michelf.com> wrote:
 
 The idea's not bad, but I think it needs some improvments. For 
 instance,  in a struct, I might want some members to be part of the 
 tail and some  other not. I believe that should be allowed somehow.

What do you mean "not", meaning they are fully const? Because you can't not apply const to references inside a const struct, that's logical const, and while it might be a nice feature, Walter has steadfastly refused to allow it.

I'm not arguing for logical const. Logical const means that you have an island of non-const inside a const structure. That's not what I meant. When you want your struct to be tail-const, what it means is that you want the members inside it to be tail-const. The structure itself is not const, just the members, an just the tail of the members. But do you always want *all* the members to be tail const? Say your struct is a reference counting smart pointer that works by having two pointers: one points to the actual object, the other points to the reference counter (boost::shared_ptr's design). This smart pointer basically has two tails, one for each internal pointer. But if you apply "tail const" to the smart pointer, you probably only want the pointer to the actual object to be tail-const. That's because making the pointer to the reference counter tail-const would just prevent you from updating the counter, which in turn would prevent you from assigning something different to the smart pointer. I think what you want for that is to somehow make SmartPtr!(X) implicitly derived from SmartPtr!(const X), you don't want the compiler applying blindly tail-const to all the members. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 10:03:58 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 On Mon, 16 Aug 2010 09:26:55 -0400, Michel Fortin  
 <michel.fortin michelf.com> wrote:
 
 I think what you want for that is to somehow make SmartPtr!(X)  
 implicitly derived from SmartPtr!(const X), you don't want the compiler 
  applying blindly tail-const to all the members.

Again, it's logical const, no matter how you slice it.

If you see it that way, then D const is logical const too. I'm not proposing we create a mutable island inside a const struct or class (thus not breaking the transitivity), so it does abide by D current definition of const. Here's some definition? struct SmartPointer(T) { T* pointer; uint* refCount; } SmartPointer!(const X) pointer; All I am saying is that the "conceptual" tail-const form of SmartPointer!(X) is SmartPointer!(const X), and that it'd be useful that SmartPointer!(X) and SmartPointer!(immutable X) could implicitly cast to SmartPointer!(const X). That might be "logical const" to you as long as you think "tail const" for struct is "all members become tail const", but I think that definition of tail-const is pointless and that there should not be any real tail-const for structs except the one you can induce through its template arguments. As for constness of ranges, it's quite similar. The thing is that you generally have three possible levels of constness. The range might be const, the container the range points to might be const, and the elements in the container might be const. If ranges were expressed like this: struct Range(ContainerType) {...} then it'd be easy to say whether the container and the element type is const or not just like this: Range!(Container!(const Element)) Range!(const Container!(immutable Element)) Range!(immutable Container!(immutable Element)) Most ranges are not defined like this: the type of the containeris implied in the type of the range, but the constness of the container is simply missing. Wouldn't defining ranges like the above fix the problem? And you could make ranges cast implicitly to their "tail-const" form when needed, exactly like the smart pointer above. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 11:39:15 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 Translating it to your example, should you be able to convert from a  
 const(SmartPointer!X) to a SmartPointer!(const X) ?  I'd say no, 
 because  const(SmartPointer!X) guarantees that the refCount will not 
 change.

Indeed, you can't convert const(SmartPointer!X) to SmartPointer!(const X). Doing that is what I'd call logical-const, and it'd be contrary to the const system in D.
 Implicit casting to your version of tail-const breaks that guarantee.

I only suggested casting the other way around: SmartPointer!(X) to SmartPointer!(const X). You're implying things I didn't meant because your definition of tail-const is different than mine.
 One  of the *essential* properties of tail const is you should always 
 be able  to implicitly convert a const(T) to a tail const(T).

Then, by *your* definition of tail const, the SmartPointer cannot be tail-const. *My* definition of tail-const just means that the tail is const and the head is not, irrespective of what conversions are allowed. So I think we agree except on the correct definition for tail-const. I think mine is simpler and more to the point, but that's just my opinion.
 Wouldn't defining ranges like the above fix the problem? And you could  
 make ranges cast implicitly to their "tail-const" form when needed,  
 exactly like the smart pointer above.

It might make it possible, but it would be a mess. A range would be a templated type inside the templated container type. Or you could define a range outside the container type to avoid odd issues.

The important things is to have the constness of the container "known" by the range type, so all you need is two separate ranges types (template arguments create separate types). The STL uses iterator and const_iterator, that'd work too. Which brings me to...
 But implicit  casting is going to be difficult. You also have to 
 contend with stuff  like this:
 
 struct Range(V)
 {
     static if(isConst!(V)) // not sure if this exists, but assume it does
     {
        bool makeUncastable;
     }
     V *cur;
 }
 
 So a Range!(const(V)) cannot be implicitly converted to Range!V because 
  the layout is different.  I'd rather not rely on templates and  
 compile-time decisions, and just let the compiler enforce the simple 
 cases.

For complicated cases like the one above, wouldn't a layout such as this one work? struct ConstRange(V) { ... } struct Range(V) { ConstRange _constRange; alias _constRange this; ... } We're reinventing static-inheritance using "alias this". :-) Of course, I'm assuming implicit conversion from Range to ConstRange will work somehow; this still need to be fixed. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Michel Fortin wrote:
 We're reinventing static-inheritance using "alias this".

Static this has been from the get-go the intent to achieve subtyping for structs. I, too, think that a solution to tail const for ranges should gravitate around alias this. Andrei
Aug 16 2010
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 Hopefully, Andrei will eventually get around to dealing with const in 
 std.container and see what a mess it will become without some sort of 
 tail-const for ranges.

I believe at some point an approach will come forth. Here are a few quick thoughts on this large exchange: * The discussion this time around reflects a deep and thorough understanding of the issues involved throughout the community. * I think head-const has its usefulness, and tail-const is obviously in need for serious attention. Ideally we should reach, with no or minimal language changes, the point at which full const is the built-in power tool and head-const and tail-const are library artifacts. I think it's quite easy to define a Final!T template that is head-const, and Rebindable!T is a starting point for tail-const classes. We need to figure a pattern for achieving tail constness for general types. * Const and immutable will be used less often than in C++. This might seem a weakness to those coming from C++ where const can and should be sprinkled often, but it is a natural consequence of the relative restrictions imposed by const in C++ vs. D. D's const is more restrictive, and as such will find its way in fewer idioms than C++'s. * Perhaps a good starting point is to look at std.container.SList and see how const(SList!T) can be manipulated. Andrei
Aug 16 2010
next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 11:17:32 -0400, "Steven Schveighoffer" 
<schveiguy yahoo.com> said:

 struct Array(V)
 {
    uint length;
    V *v;
 }
 
 How can we turn Array!V into Array!(const(V)) implicitly...

Shouldn't this work? struct Array(V) { uint length; V *v; Array!(const V) _const() property { ... } alias _const this; } -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 11:23:24 -0400, Michel Fortin <michel.fortin michelf.com> said:

 Shouldn't this work?
 
 	struct Array(V) {
 		uint length;
 		V *v;
 
 		Array!(const V) _const()  property { ... }
 		alias _const this;
 	}

Well, it doesn't, but perhaps it should. I agree that implicit conversions is the core of the problem for tail-const in structs. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
prev sibling next sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2010-08-16 19:06:56 -0400, "Simen kjaeraas" <simen.kjaras gmail.com> said:

 Uhm, it works in D:
 
 class C {}
 class D : C {}
 
 void main( ) {
      D[] a;
      C[] b = a;
 }

Then that's a bug because it'd allow you to do this: void main() { D[]a = new D[1]; C[] b = a; b[0] = new C; // what is the type of a[0] now? Certainly not a D. } This would be fine however: const(C)[] b = a; -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Aug 16 2010
prev sibling parent dsimcha <dsimcha yahoo.com> writes:
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s article
 Steven Schveighoffer wrote:
 Hopefully, Andrei will eventually get around to dealing with const in
 std.container and see what a mess it will become without some sort of
 tail-const for ranges.

quick thoughts on this large exchange: * The discussion this time around reflects a deep and thorough understanding of the issues involved throughout the community. * I think head-const has its usefulness, and tail-const is obviously in need for serious attention. Ideally we should reach, with no or minimal language changes, the point at which full const is the built-in power tool and head-const and tail-const are library artifacts. I think it's quite easy to define a Final!T template that is head-const, and Rebindable!T is a starting point for tail-const classes. We need to figure a pattern for achieving tail constness for general types.

Forgive me for replying to a post that's over a month old, but I was just thinking about this. The only problem I see with making Rebindable a limited form of general tail const is the issue of structs with elaborate assignment. Do you have any tricks up your sleeve for dealing with this?
Sep 18 2010
prev sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Steven Schveighoffer wrote:
 On Fri, 13 Aug 2010 23:20:55 -0400, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 
 On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work.


Perhaps part of this is the reluctance to reexamine something which was hard to prove correct given the ideas at the time? However, the idea is attainable. For the simple fact that we have tail-const references in other parts of the language. All that is missing is syntax.

This seems to be a misquote. You are replying to Walter, not me. Andrei
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 13 Aug 2010 23:20:55 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work.


Perhaps part of this is the reluctance to reexamine something which was hard to prove correct given the ideas at the time? However, the idea is attainable. For the simple fact that we have tail-const references in other parts of the language. All that is missing is syntax.
 It wasn't the syntax. There were many syntaxes proposed. The type system
 loses its coherency with such a special case in it. Generic code has
 weird problems, type deduction gets strange, types lose their
 composability, etc.


The fact that all syntaxes proposed back then didn't cut the mustard does not mean that no possible syntax exists. This is not NP != P. Given that we have syntax that works for pointers, it's logical and reasonable to assume that some syntax can work for generic tail-constness of everything. In fact, we had syntax that *worked* it was just confusing. BTW, I like Tomek's idea with tail const/immutable/shared. It's not a special case, it's a generic case, you can apply it to any type.
 But there is a solution:

 const(Object)* o;

 Yes, under the hood it's 2 levels of indirection. But it works, it
 requires no special syntax, it is completely consistent with the rest of
 how the type system works, there are no corner holes in it, etc.


It obviously doesn't work. We wouldn't be discussing it if the group of people bringing fresh proposals who haven't seen the previous discussions weren't having issues with it. The rest of us who were involved with the discussions are cynical not because the problem can't be solved, but that you have stated that no solution exists and you aren't willing to look at it any more. The main problem with this hack is that the standard way to refer to a mutable class is with just a class reference. That doesn't implicitly cast to a tail-const class reference pointer. So anything that uses tail-const class references has to be specially constructed instead of just passing it. You can't just pass &x because x is a stack variable, which could be escaped. So you have to create a class reference on the heap, and then pass the address to it. Just try creating a class reference on the heap. In essence, you lose all the benefits of classes by making them tail-const with this method. The pain of dealing with tail-const class references via pointers makes you just not use them, because they are so awkward and cumbersome, especially when you see how elegantly D deals with tail const in the rest of the language.
 Actually this is an even better solution:

 const(Scoped!Type)* o;

Wait, isn't this a pointer to data allocated on the stack? I must be misunderstanding something...
 The only issue is that you need to know the static type... polymorphism  
 doesn't work with pointers.

Yeah, for classes, this is a big big problem. For all those dmd hackers out there, I think we are stuck unless someone can create a dmd patch to show it can be done. Without that, I think Walter is simply unwilling to look at any possible solutions, having spent so much time trying to make it work. If we take his time/effort requirement out of the equation, it could be possible to get something through. I might take up dmd hacking just to get this working. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 08:56:51 -0400, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2010-08-16 08:08:21 -0400, "Steven Schveighoffer"  
 <schveiguy yahoo.com> said:

 On Fri, 13 Aug 2010 23:20:55 -0400, Andrei Alexandrescu   
 <SeeWebsiteForEmail erdani.org> wrote:

 On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

over a hundred hours in trying to make it work. It doesn't work.


was hard to prove correct given the ideas at the time? However, the idea is attainable. For the simple fact that we have tail-const references in other parts of the language. All that is missing is syntax.
 It wasn't the syntax. There were many syntaxes proposed. The type  
 system
 loses its coherency with such a special case in it. Generic code has
 weird problems, type deduction gets strange, types lose their
 composability, etc.


does not mean that no possible syntax exists. This is not NP != P. Given that we have syntax that works for pointers, it's logical and reasonable to assume that some syntax can work for generic tail-constness of everything. In fact, we had syntax that *worked* it was just confusing.

Indeed. This reflects my opinion too. I'm not saying much about this issue right now because I feel it's no use, or to be precise, so much time convincing is needed to bring something useful I'd rather do something else. I've taken the route where I'll wait until others (Andrei?) hit the same problems and put enough pressure to force the design to be revisited. Hopefully this will happen sooner rather than later.

Yeah, it's why I let it go before. But I think in light of the new ability to use attributes, and the need for it in my own project (dcollections), I want to try and push it again. I may even try my hand at compiler hacking... Hopefully, Andrei will eventually get around to dealing with const in std.container and see what a mess it will become without some sort of tail-const for ranges.
 Same situation for  safe, same for pure, same for shared. Which means I  
 won't be adopting D2's most interesting features for some time.


 BTW, I like Tomek's idea with  tail const/immutable/shared.  It's not a  
  special case, it's a generic case, you can apply it to any type.

The idea's not bad, but I think it needs some improvments. For instance, in a struct, I might want some members to be part of the tail and some other not. I believe that should be allowed somehow.

What do you mean "not", meaning they are fully const? Because you can't not apply const to references inside a const struct, that's logical const, and while it might be a nice feature, Walter has steadfastly refused to allow it. If you do mean fully const, then I'm not sure why you'd want that. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 09:26:55 -0400, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2010-08-16 09:08:30 -0400, "Steven Schveighoffer"  
 <schveiguy yahoo.com> said:

 On Mon, 16 Aug 2010 08:56:51 -0400, Michel Fortin   
 <michel.fortin michelf.com> wrote:

 The idea's not bad, but I think it needs some improvments. For  
 instance,  in a struct, I might want some members to be part of the  
 tail and some  other not. I believe that should be allowed somehow.

can't not apply const to references inside a const struct, that's logical const, and while it might be a nice feature, Walter has steadfastly refused to allow it.

I'm not arguing for logical const. Logical const means that you have an island of non-const inside a const structure. That's not what I meant. When you want your struct to be tail-const, what it means is that you want the members inside it to be tail-const. The structure itself is not const, just the members, an just the tail of the members. But do you always want *all* the members to be tail const?

Yes. Not doing this violates the promise of const. This is exactly the same as logical const, even though the storage is not with the object itself. It's ok to do it to the head members because the head part is always copied by value.
 Say your struct is a reference counting smart pointer that works by  
 having two pointers: one points to the actual object, the other points  
 to the reference counter (boost::shared_ptr's design). This smart  
 pointer basically has two tails, one for each internal pointer. But if  
 you apply "tail const" to the smart pointer, you probably only want the  
 pointer to the actual object to be tail-const. That's because making the  
 pointer to the reference counter tail-const would just prevent you from  
 updating the counter, which in turn would prevent you from assigning  
 something different to the smart pointer.

That's logical const. I'm not saying it's not useful, but tail-const is not logical const. A property of tail const that logical const does not share is that all referenced data in a const item is also constant in a tail-const reference. The same is not true with logical const. FWIW, I had proposed a complex system to Walter/Andrei that allowed for applying any kind of constancy to an object, and have the compiler deal with proper implicit casts. However, the system is very complex, with people able to arbitrarily name const *flavors*. It wouldn't have been very usable... I think a possible solution is possibly to create a mutable library type that always stays mutable in const and immutable flavors of an object. Such a wrapper type should take into account the undefined aspects of doing such a thing.
 I think what you want for that is to somehow make SmartPtr!(X)  
 implicitly derived from SmartPtr!(const X), you don't want the compiler  
 applying blindly tail-const to all the members.

Again, it's logical const, no matter how you slice it. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 11:00:21 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 On Fri, 13 Aug 2010 23:20:55 -0400, Andrei Alexandrescu  
 <SeeWebsiteForEmail erdani.org> wrote:

 On 08/13/2010 06:35 PM, Walter Bright wrote:
 Steven Schveighoffer wrote:
 I like how it reads naturally. I think it's also syntactically
 unambiguous. Walter, please give this one some attention, I'd love to
 see this fixed.

This was endlessly discussed maybe 3 years ago. I probably invested over a hundred hours in trying to make it work. It doesn't work.


was hard to prove correct given the ideas at the time? However, the idea is attainable. For the simple fact that we have tail-const references in other parts of the language. All that is missing is syntax.

This seems to be a misquote. You are replying to Walter, not me.

I was replying to both your points and Walter's. Walter's name is quoted just below yours. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 10:58:52 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Steven Schveighoffer wrote:
 Hopefully, Andrei will eventually get around to dealing with const in  
 std.container and see what a mess it will become without some sort of  
 tail-const for ranges.

I believe at some point an approach will come forth. Here are a few quick thoughts on this large exchange: * I think head-const has its usefulness, and tail-const is obviously in need for serious attention. Ideally we should reach, with no or minimal language changes, the point at which full const is the built-in power tool and head-const and tail-const are library artifacts. I think it's quite easy to define a Final!T template that is head-const, and Rebindable!T is a starting point for tail-const classes. We need to figure a pattern for achieving tail constness for general types.

Tail-const is already not a library solution. A tail const pointer or array is possible without library support. If a solution can be had via a library, it must still enjoy some compiler support for implicit casting. It must also avoid any undue extra code generation. Const is purely a compile-time feature, it plays no role in the eventual generated code. It simply prevents cases which the developer has asked it to prevent.
 * Const and immutable will be used less often than in C++. This might  
 seem a weakness to those coming from C++ where const can and should be  
 sprinkled often, but it is a natural consequence of the relative  
 restrictions imposed by const in C++ vs. D. D's const is more  
 restrictive, and as such will find its way in fewer idioms than C++'s.

Currently, object is very const incorrect. Once it becomes const-correct, I think this observation may drastically change. But more importantly, many pieces of phobos/druntime simply don't work with const (dup/idup comes to mind). It may not be a case that const isn't required, but more that const can't be correctly implemented without incurring lots of code duplication. So we all "put off" making things const correct until const is fixed. Observing that D currently isn't full of tail-const references may be an artifact of that problem.
 * Perhaps a good starting point is to look at std.container.SList and  
 see how const(SList!T) can be manipulated.

We can start simpler than that. Give a struct: struct Array(V) { uint length; V *v; } How can we turn Array!V into Array!(const(V)) implicitly... This will duplicate array tail-const, and be essential to making tail-const ranges. It should be a good starting test for any library/compiler solution. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 10:50:04 -0400, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2010-08-16 10:03:58 -0400, "Steven Schveighoffer"  
 <schveiguy yahoo.com> said:

 On Mon, 16 Aug 2010 09:26:55 -0400, Michel Fortin   
 <michel.fortin michelf.com> wrote:

 I think what you want for that is to somehow make SmartPtr!(X)   
 implicitly derived from SmartPtr!(const X), you don't want the  
 compiler  applying blindly tail-const to all the members.


If you see it that way, then D const is logical const too.

Logical const fits within the realm of const. I've proven in the past that logical const can be emulated without modification to the type system. But applying tail const is not applying logical const. If you want to apply a different type of const to a type, then that is not const or tail-const. It's not invalid or unsafe, but it's another type of const besides tail const and normal const.
  I'm not proposing we create a mutable island inside a const struct or  
 class (thus not breaking the transitivity), so it does abide by D  
 current definition of const. Here's some definition?

 	struct SmartPointer(T) {
 		T* pointer;
 		uint* refCount;
 	}

 	SmartPointer!(const X) pointer;

 All I am saying is that the "conceptual" tail-const form of  
 SmartPointer!(X) is SmartPointer!(const X), and that it'd be useful that  
 SmartPointer!(X) and SmartPointer!(immutable X) could implicitly cast to  
 SmartPointer!(const X).

Likewise, you could say the const version of SmartPointer only applies const to the pointer part, because refcount is not part of its state. It was the basis for my argument for including a way to do logical const in the past. It's logical const, or rather, logical tail-const :)
 That might be "logical const" to you as long as you think "tail const"  
 for struct is "all members become tail const", but I think that  
 definition of tail-const is pointless and that there should not be any  
 real tail-const for structs except the one you can induce through its  
 template arguments.

Is implicitly converting from const(int[]) to const(int)[] useless? If you think that, then I agree that we disagree. When making a tail-const copy of a const struct, you should be able to simply remove the const decorations of the values you are copying, but not the data it references. That violates the guarantee of const. Translating it to your example, should you be able to convert from a const(SmartPointer!X) to a SmartPointer!(const X) ? I'd say no, because const(SmartPointer!X) guarantees that the refCount will not change. Implicit casting to your version of tail-const breaks that guarantee. One of the *essential* properties of tail const is you should always be able to implicitly convert a const(T) to a tail const(T).
 As for constness of ranges, it's quite similar. The thing is that you  
 generally have three possible levels of constness. The range might be  
 const, the container the range points to might be const, and the  
 elements in the container might be const. If ranges were expressed like  
 this:

 	struct Range(ContainerType) {...}

 then it'd be easy to say whether the container and the element type is  
 const or not just like this:

 	Range!(Container!(const Element))
 	Range!(const Container!(immutable Element))
 	Range!(immutable Container!(immutable Element))

A range on a container is typically already parameterized on the container, because it's usually a subtype of the container. But I think that in order for container ranges to do the right thing, containers of const elements should return tail const ranges of non-const elements. Otherwise, implicit conversion would be impossible. So given a container parameterized with Element, and a function: R opSlice() const {...} The R type should be tail const(Container!Element.range), even on a Container!(const(Element)).
 Most ranges are not defined like this: the type of the containeris  
 implied in the type of the range, but the constness of the container is  
 simply missing.

Yes, that is one of the reasons I have avoided doing const ranges on dcollections, all ranges use the container's parameters for the element type.
 Wouldn't defining ranges like the above fix the problem? And you could  
 make ranges cast implicitly to their "tail-const" form when needed,  
 exactly like the smart pointer above.

It might make it possible, but it would be a mess. A range would be a templated type inside the templated container type. Or you could define a range outside the container type to avoid odd issues. But implicit casting is going to be difficult. You also have to contend with stuff like this: struct Range(V) { static if(isConst!(V)) // not sure if this exists, but assume it does { bool makeUncastable; } V *cur; } So a Range!(const(V)) cannot be implicitly converted to Range!V because the layout is different. I'd rather not rely on templates and compile-time decisions, and just let the compiler enforce the simple cases. Note that it has been proposed in the past to have constancy a template parameter in itself, but this has a host of problems as well. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 11:30:10 -0400, Michel Fortin  
<michel.fortin michelf.com> wrote:

 On 2010-08-16 11:23:24 -0400, Michel Fortin <michel.fortin michelf.com>  
 said:

 Shouldn't this work?
  	struct Array(V) {
 		uint length;
 		V *v;
  		Array!(const V) _const()  property { ... }
 		alias _const this;
 	}

Well, it doesn't, but perhaps it should. I agree that implicit conversions is the core of the problem for tail-const in structs.

If the mechanism isn't automatic, and involves doing stuff like this, I don't see it being used much. The compiler has to help here somewhere. -Steve
Aug 16 2010
prev sibling next sibling parent reply =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= <just ask.me> writes:
Dnia 16-08-2010 o 17:17:32 Steven Schveighoffer <schveiguy yahoo.com>  =

napisa=B3(a):

 * Perhaps a good starting point is to look at std.container.SList and =

 see how const(SList!T) can be manipulated.
  We can start simpler than that.  Give a struct:
  struct Array(V)
 {
   uint length;
   V *v;
 }
  How can we turn Array!V into Array!(const(V)) implicitly...
  This will duplicate array tail-const, and be essential to making  =

 tail-const ranges.
  It should be a good starting test for any library/compiler solution.

Nice litmus test. I thought about it for a while and came up with: Array!int arr =3D ... ; Rebindable!(const Array!V) r =3D arr; It actually compiles if you remove the template constraint off Rebindabl= e. = But tail const/immutable is generally desired so you'd end up with = rebindable everywhere making variable manipulation cumbersome, the code = = less auditable, giving easy counter-arguments in language wars ("D must = = resort to casts, so much for the type system revolution, haha"), etc. Plus, there's the merriness with shared (tail-shared structures, anyone?= = and of course you'd want a tail-const view on it at some point, etc). = Rebindable (this name doesn't scale) would basically have to reimplement= = all of D's type conversions via operator overloading, template = constraints, static ifs, and blunt casts. Like as if implementing all th= is = in a compiler alone wasn't hard enough... Tomek
Aug 16 2010
next sibling parent reply Jonathan M Davis <jmdavisprog gmail.com> writes:
On Monday, August 16, 2010 15:31:23 Steven Schveighoffer wrote:
 We can move onto the next test:
 
 Given:
 
 class C {}
 
 class D : C {}
 
 convert Array!D into Array!(const C)
 
 OK, maybe this is a more advanced test :)
 
 I'd be happy with just proper factoring of const to be honest.

Um, I don't think that I've ever seen that work in _any_ language, and I'm not sure that it would be a good thing if it did. Array!D is a completely different type from Array!C or Array!(const C). I could see an argument that Array!D should work as Array!(const D), but that seems off to me. Converting Array!D to const (Array!D) should work though. In any case, converting a collection of one type to a collection of another type is something that I've never seen done, and I'm not sure that it would be a desirable feature ultimately. I'm not sure that it should _not_ be possible, but it would certainly make me nervous. I'd really have to think about the implications before I agreed that it was a good idea. - Jonathan M Davis
Aug 16 2010
parent reply Pelle <pelle.mansson gmail.com> writes:
On 08/17/2010 01:44 AM, Steven Schveighoffer wrote:
 On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas
 <simen.kjaras gmail.com> wrote:
 Uhm, it works in D:

 class C {}
 class D : C {}

 void main( ) {
 D[] a;
 C[] b = a;
 }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[].

class C { } class D : C { } class E : C { } void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; } D[] ds; append_to(ds, new E); *ahem* D[] can not be converted to const(C). That it works today is pretty terrible. Rewrite the append_to to work with Objects, and well. :-)
Aug 17 2010
next sibling parent reply Mafi <mafi example.org> writes:
Am 17.08.2010 12:05, schrieb Pelle:
 On 08/17/2010 01:44 AM, Steven Schveighoffer wrote:
 On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas
 <simen.kjaras gmail.com> wrote:
 Uhm, it works in D:

 class C {}
 class D : C {}

 void main( ) {
 D[] a;
 C[] b = a;
 }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[].

class C { } class D : C { } class E : C { } void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; } D[] ds; append_to(ds, new E); *ahem* D[] can not be converted to const(C). That it works today is pretty terrible. Rewrite the append_to to work with Objects, and well. :-)

Appending something could reallocate so it's not available through ds. Even when it allocates in place the length of ds is *not* affected so you are not able to fetch the E instance through ds with bounds checking on. foreach(d; ds) {...} // works also without bounds-checking won't get the E instance. When bounds-checking is off indexing is kind of pointer arithmethic which is unsafe anyways. Mafi
Aug 17 2010
parent Mafi <mafi example.org> writes:
Am 17.08.2010 13:48, schrieb Mafi:
 Am 17.08.2010 12:05, schrieb Pelle:
 On 08/17/2010 01:44 AM, Steven Schveighoffer wrote:
 On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas
 <simen.kjaras gmail.com> wrote:
 Uhm, it works in D:

 class C {}
 class D : C {}

 void main( ) {
 D[] a;
 C[] b = a;
 }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[].

class C { } class D : C { } class E : C { } void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; }


 D[] ds;
 append_to(ds, new E);


 *ahem*
 D[] can not be converted to const(C). That it works today is pretty
 terrible. Rewrite the append_to to work with Objects, and well. :-)

(...) Mafi

Aug 17 2010
prev sibling parent Pelle <pelle.mansson gmail.com> writes:
On 08/17/2010 02:48 PM, Steven Schveighoffer wrote:
 On Tue, 17 Aug 2010 06:05:55 -0400, Pelle <pelle.mansson gmail.com> wrote:

 On 08/17/2010 01:44 AM, Steven Schveighoffer wrote:
 On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas
 <simen.kjaras gmail.com> wrote:
 Uhm, it works in D:

 class C {}
 class D : C {}

 void main( ) {
 D[] a;
 C[] b = a;
 }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[].

class C { } class D : C { } class E : C { } void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; } D[] ds; append_to(ds, new E); *ahem* D[] can not be converted to const(C). That it works today is pretty terrible. Rewrite the append_to to work with Objects, and well. :-)

It's a bug also :) The general rule of thumb is that you can convert a reference to a type to a reference to a const subtype that's only a one-level reference. *two* level references cannot be converted, because of the example you showed. A ref const(C)[] is a two-level reference (reference to array, which is a reference to a block of const(C)). So you cannot convert a ref const(D)[] into a ref const(C)[], but converting a const(D)[] into a const(C)[] is ok because there is only one level of reference before you reach the const type. -Steve

I just saw that I missed the ref part when I tried the code, and my example does not actually compile. Phew!
Aug 17 2010
prev sibling next sibling parent "Simen kjaeraas" <simen.kjaras gmail.com> writes:
Jonathan M Davis <jmdavisprog gmail.com> wrote:

 On Monday, August 16, 2010 15:31:23 Steven Schveighoffer wrote:
 We can move onto the next test:

 Given:

 class C {}

 class D : C {}

 convert Array!D into Array!(const C)

 OK, maybe this is a more advanced test :)

 I'd be happy with just proper factoring of const to be honest.

Um, I don't think that I've ever seen that work in _any_ language, and I'm not sure that it would be a good thing if it did. Array!D is a completely different type from Array!C or Array!(const C). I could see an argument that Array!D should work as Array!(const D), but that seems off to me. Converting Array!D to const (Array!D) should work though.

Uhm, it works in D: class C {} class D : C {} void main( ) { D[] a; C[] b = a; } -- Simen
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas  
<simen.kjaras gmail.com> wrote:

 Jonathan M Davis <jmdavisprog gmail.com> wrote:

 On Monday, August 16, 2010 15:31:23 Steven Schveighoffer wrote:
 We can move onto the next test:

 Given:

 class C {}

 class D : C {}

 convert Array!D into Array!(const C)

 OK, maybe this is a more advanced test :)

 I'd be happy with just proper factoring of const to be honest.

Um, I don't think that I've ever seen that work in _any_ language, and I'm not sure that it would be a good thing if it did. Array!D is a completely different type from Array!C or Array!(const C). I could see an argument that Array!D should work as Array!(const D), but that seems off to me. Converting Array!D to const (Array!D) should work though.

Uhm, it works in D: class C {} class D : C {} void main( ) { D[] a; C[] b = a; }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[]. See http://d.puremagic.com/issues/show_bug.cgi?id=2095 But aside from that, the test is invalid, I don't think D* can implicitly cast to C* (I'm not sure). This might be a built-in array-only thing. The compiler is going to be hard-pressed to get that one right anyways, as it involves changing types as Jonathan said. This kind of thing is tricky to get right. BTW, I thought about it more, and I don't think Rebindable!(const(Array!V)) is going to work. The public interface that rebindable exposes is the const version, and you want to expose the const version only for the references, and expose the mutable version for the values. Rebindable only allows reassigning the whole struct at once. I feel more and more strongly that this has to be a compiler solution. A library solution may eventually be possible, but it's going to be very nasty/tricky to write. I wouldn't be surprised if using a library tail const type adds 10K to the executable size for each type it's used on... -Steve
Aug 16 2010
prev sibling next sibling parent Michal Minich <michal.minich gmail.com> writes:
On Tue, 17 Aug 2010 12:05:55 +0200, Pelle wrote:

 class C { }
 class D : C { }
 class E : C { }
 
 void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; }
 
 D[] ds;
 append_to(ds, new E);
 
 
 *ahem*
 D[] can not be converted to const(C). That it works today is pretty
 terrible. Rewrite the append_to to work with Objects, and well. :-)

what about appending instance of C? Doing this, you would end up with arrays of Ds, where one item will be instance of C! D[] ds; append_to(ds, new C);
Aug 17 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Tue, 17 Aug 2010 06:05:55 -0400, Pelle <pelle.mansson gmail.com> wrote:

 On 08/17/2010 01:44 AM, Steven Schveighoffer wrote:
 On Mon, 16 Aug 2010 19:06:56 -0400, Simen kjaeraas
 <simen.kjaras gmail.com> wrote:
 Uhm, it works in D:

 class C {}
 class D : C {}

 void main( ) {
 D[] a;
 C[] b = a;
 }

Yes, I was about to say that except that is a bug kind of. The type system should only allow casting D[] to const(C)[].

class C { } class D : C { } class E : C { } void append_to(ref const(C)[] cs, const(C) c) { cs ~= c; } D[] ds; append_to(ds, new E); *ahem* D[] can not be converted to const(C). That it works today is pretty terrible. Rewrite the append_to to work with Objects, and well. :-)

It's a bug also :) The general rule of thumb is that you can convert a reference to a type to a reference to a const subtype that's only a one-level reference. *two* level references cannot be converted, because of the example you showed. A ref const(C)[] is a two-level reference (reference to array, which is a reference to a block of const(C)). So you cannot convert a ref const(D)[] into a ref const(C)[], but converting a const(D)[] into a const(C)[] is ok because there is only one level of reference before you reach the const type. -Steve
Aug 17 2010
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 18:12:35 -0400, Tomek Sowiński <just ask.me> wrote:

 Dnia 16-08-2010 o 17:17:32 Steven Schveighoffer <schveiguy yahoo.com>  
 napisał(a):

 * Perhaps a good starting point is to look at std.container.SList and  
 see how const(SList!T) can be manipulated.
  We can start simpler than that.  Give a struct:
  struct Array(V)
 {
   uint length;
   V *v;
 }
  How can we turn Array!V into Array!(const(V)) implicitly...
  This will duplicate array tail-const, and be essential to making  
 tail-const ranges.
  It should be a good starting test for any library/compiler solution.

Nice litmus test. I thought about it for a while and came up with: Array!int arr = ... ; Rebindable!(const Array!V) r = arr;

Wait, is the length mutable in that type? If so, that's better than I thought Rebindable could do... We can move onto the next test: Given: class C {} class D : C {} convert Array!D into Array!(const C) OK, maybe this is a more advanced test :) I'd be happy with just proper factoring of const to be honest.
 It actually compiles if you remove the template constraint off  
 Rebindable. But tail const/immutable is generally desired so you'd end  
 up with rebindable everywhere making variable manipulation cumbersome,  
 the code less auditable, giving easy counter-arguments in language wars  
 ("D must resort to casts, so much for the type system revolution,  
 haha"), etc.

Naming is subject to debate. I think with some functions to surround it, and better naming, Rebindable can be quite usable. With aliases, you have the power to shorten anything. e.g.: template tconst(T) { alias Rebindable!(const T) tconst; } tconst!T is pretty much as easy as tail const(T). But the implicit conversion *has* to be there.
 Plus, there's the merriness with shared (tail-shared structures, anyone?  
 and of course you'd want a tail-const view on it at some point, etc).

tail shared is similar to tail const. But implicit conversions are not there. It's more like tail-immutable.
 Rebindable (this name doesn't scale) would basically have to reimplement  
 all of D's type conversions via operator overloading, template  
 constraints, static ifs, and blunt casts. Like as if implementing all  
 this in a compiler alone wasn't hard enough...

I agree, compiler support would be easiest on the user. In fact, compiler support to make rebindable possible may be more difficult than compiler support for tail-X since tail-X already works for other items. -Steve
Aug 16 2010
prev sibling next sibling parent Adam Ruppe <destructionator gmail.com> writes:
On the subject of rebindable, what about:

const Object o; // not rebindable, the whole thing is set once and const
const(Object) o; // the Object is const, but the reference is not.

So, everything is rebindable unless the declaration has a
const/immutable on the outside.

int a; // rebindable (obviously)
const(int) a; // the int never changes... but the variable a might.
Meaningless for a value type, but makes sense for a reference type
const int a; // the whole thing is set once and never changes. The
const applies to the variable a itself, but the transitive property
propagates it down to the int type too.
Aug 12 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Thursday 12 August 2010 18:57:26 Michel Fortin wrote:
 Const-correctness can't work the same in D as in C/C++. Even if you had
 a mutable member in your class, D can't allow you to modify this member
 from a const member function, because this const member function can be
 called when your class is immutable, and if the class is immutable it
 can be shared across threads, in which case modifying the member
 (without synchronization) would cause races. Basically, D const system
 isn't build for const-correctness or to enforce what logically looks
 like const, it's built to ease concurrency and sharing of data between
 threads. All this to say that when something is const, it *is* const
 and it can't cause races.

That is, unfortunately, a very good point. It's still seriously limiting, however. What it means is that there is going to be code out there (and possibly a lot of it) which _should_ be const but can't be. I confess that the more that I deal with immutable, the less I like it. const doesn't complicate things all that much and brings a lot of improvements. immutable complicates things quite a lot more and does not bring the same level of improvements to the table. However, it does help with multi-threaded apps which _is_ very important, and increasingly so, so it's probably better to have it than not. Still, it can be very frustrating. However, if there's a better way to do it, it'll likely be found with either a future version of D or a language which replaces D. There's a shortage of languages which have tried to mix const, mutable, and immutable together (D may be the only one actually), so there isn't much experience to base improvements on. And is usually the case, it's much easier to see the problems than their solutions. - Jonathan M Davis
Aug 12 2010
prev sibling next sibling parent Walter Bright <newshound2 digitalmars.com> writes:
dsimcha wrote:
 3.  inout is currently so bug-ridden it's not even funny.  (Though this is
 clearly fixable long-term, once we get higher priority stuff off our plates.)

I know. It's just that the 64 bit port has priority at the moment.
Aug 12 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 13 Aug 2010 01:46:51 -0400, Kagamin <spam here.lot> wrote:

 Walter Bright Wrote:

 Graham St Jack wrote:
 However, I still regard the language design decision of a class
 reference having the same constness as the object it refers to as a
 major language design problem.

We tried for months. It just doesn't work to make it any other way than it is now.

Is it compiler infrastructure's or syntactical issue?

Syntactical. There is no way to separate the reference from the data, since the reference is a hidden artifact of the type system. With pointers it is easy, you apply const to the data, and not the pointer. With arrays, same thing. I don't want to open up another discussion of how to do it, we tried and tried for months with different proposals, and nothing seemed very good. The only thing which would work IMO is another const keyword. -Steve
Aug 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 12 Aug 2010 21:45:16 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 On Thursday, August 12, 2010 18:16:01 Graham St Jack wrote:
 On 13/08/10 10:18, Jonathan M Davis wrote:
 On Thursday, August 12, 2010 17:38:28 Graham St Jack wrote:
 For me, the key problem is that a class object reference has the same
 const/immutable/shared attribute as the object on the heap that it
 refers to. This is sometimes what you need, but more often you want a
 non-shared, mutable reference to a const/immutable/shared object.

 You can achieve this with pointers for arrays, structs and primitive
 types, but not with classes because a class pointer is just a  


 a reference.

Hence the hack that is Rebindable!(). Oh, and you _can_ achieve pointers to classes, but what you normally

 are references, which do have the problem of not being able to be  

 between the reference and referent types.

 - Jonathan M Davis

So how do you get a pointer to an object? Taking the address of an object reference gives you a pointer to the reference, not the object, which is fair enough. As far as I know there isn't a way to get a pointer to the object itself, and even if you could, how do you use such a thing?

Hmm. I was thinking that you could just do T* t = new T(); but that doesn't work. I know that people have talked about doing it here on the newsgroup, so there must be a way. You can do T t1 = new T(); T* t2 = &t1; but I guess that that's a pointer to a reference rather a pointer to the object itself. Maybe if you want pointers to classes you need to use manual memory manegement rather than the GC and new. Hopefully someone else can enlighten us. I have generally avoided pointers in D.

This is not a good idea. The type system treats class references specially. Any attempt to use a pointer to reference the actual data will most certainly end up being untyped and pretty much useless. -Steve
Aug 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 12 Aug 2010 21:37:11 -0400, Jonathan M Davis  
<jmdavisprog gmail.com> wrote:

 2. The shear difficulty in getting in getting immutable versions of  
 stuff. There is
 no automated way to do it other than idup with arrays.

Note that idup and dup are broken wrt const: http://d.puremagic.com/issues/show_bug.cgi?id=3550 -Steve
Aug 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 13 Aug 2010 09:32:49 -0400, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 On Fri, 13 Aug 2010 01:46:51 -0400, Kagamin <spam here.lot> wrote:

 Walter Bright Wrote:

 Graham St Jack wrote:
 However, I still regard the language design decision of a class
 reference having the same constness as the object it refers to as a
 major language design problem.

We tried for months. It just doesn't work to make it any other way than it is now.

Is it compiler infrastructure's or syntactical issue?

Syntactical. There is no way to separate the reference from the data, since the reference is a hidden artifact of the type system. With pointers it is easy, you apply const to the data, and not the pointer. With arrays, same thing. I don't want to open up another discussion of how to do it, we tried and tried for months with different proposals, and nothing seemed very good. The only thing which would work IMO is another const keyword.

Or rather another const, immutable and shared kewords. -Steve
Aug 13 2010
prev sibling next sibling parent reply Tomek =?UTF-8?B?U293acWEc2tp?= <just ask.me> writes:
dsimcha napisał:

 1.   It's difficult to create non-trivial immutable data structures, and
 often impossible without relying on either unchecked casts or unnecessary
 copying.

Yes. I think the only true solution is 'unique' in the language. This is my most wanted from Bartosz's type system proposal. Ah well, so when's D3 coming out? :) Tomek
Aug 13 2010
next sibling parent =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= <just ask.me> writes:
Dnia 19-08-2010 o 20:02:58 Andrej Mitrovic <andrej.mitrovich gmail.com> =
 =

napisa=B3(a):

 Where can I read about this type system proposal you speak of?

On his blog. On 'unique': http://bartoszmilewski.wordpress.com/2009/05/21/unique_ptr-how-unique-is= -it/ http://bartoszmilewski.wordpress.com/2009/11/30/unique-objects/ And on the type system in general: http://bartoszmilewski.wordpress.com/2009/06/02/race-free-multithreading= -ownership/ Snoop around this date, I remember he wrote a sequence of posts. Tomek
Aug 19 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
Thanks.

2010/8/20 Tomek Sowi=F1ski <just ask.me>:
 Dnia 19-08-2010 o 20:02:58 Andrej Mitrovic <andrej.mitrovich gmail.com>
 napisa=B3(a):

 Where can I read about this type system proposal you speak of?

On his blog. On 'unique': http://bartoszmilewski.wordpress.com/2009/05/21/unique_ptr-how-unique-is-=

 http://bartoszmilewski.wordpress.com/2009/11/30/unique-objects/

 And on the type system in general:
 http://bartoszmilewski.wordpress.com/2009/06/02/race-free-multithreading-=

 Snoop around this date, I remember he wrote a sequence of posts.


 Tomek

Aug 19 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Friday, August 13, 2010 13:37:09 Walter Bright wrote:
 There are many languages which support immutable - but it's hidden away in
 how they handle strings. For example, Perl has immutable strings.
 
 The great advantage to immutability (aside from threading) is that you can
 treat a reference type as if it were a value type. This is why so many
 languages treat strings that way. With D's immutability in the type
 system, rather than special cased for strings, is you can bring that
 advantage to any of your user defined types.

There are many languages which support immutable but few support it explicitly (the most obvious cases where you have immutable are functional languages where _everything_ is immutable), and I'm not aware of any other language that tries to mix mutable, const, and immutable like D does. For that matter, const is a bit of a rarity in my experience (though obviously there are some major languages with it). It's more frequent that either all data is mutable, all data is immutable, or most data is mutable with certain types being immutable (generally in an implicit manner rather than explictly). So, D's attempt to mix all of these options (on top of other stuff like shared) in an explicit manner is, to my knowledge, unique. - Jonathan M Davis
Aug 13 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Friday, August 13, 2010 14:10:20 Tomek Sowi=C5=84ski wrote:
 Glad you like it. Time ago I had an overzealous attempt to port QuantLib =

 D and I quickly found out that the *only* sort of const I needed was tail
 const. It's what I ended up declaring fields of my structs because if just
 one field is from-head const, the restrictions are basically the same as
 if the whole struct was const (which is correct but...). It's what the
 parameters of pure functions should implicitly be declared to (no big
 deal, but still). From-head const gives me no upper hand in the
 multi-threaded domain, just pain in my rectum domain. Sure, from-head
 const is at times useful, but tail const is the workhorse.

That's exactly why final in Java is so useless for objects. It makes the wr= ong=20 thing const. How often do you have const pointers in C++? Very rarely. How = often=20 do you have pointers to const in C++? All the time (assuming that you're tr= ying=20 to be at all const-correct anyway). At the moment, D is falling into a pit= =20 similar to that of Java with const and references. D's is better since the= =20 object _is_ const, but since you usually just want the object const, not th= e=20 reference, it's incredibly counter-productive. =2D Jonathan M Davis
Aug 13 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Friday, August 13, 2010 15:03:07 Tomek Sowi=C5=84ski wrote:
 I agree with the content, but not the tone. D's const makes all other
 mainstream const systems look petty. Applying the concept of transitivity
 has been revolutionary (hail Walter). Tail const is just a cable to the
 socket to make this wonderful device work out-of-box for programming
 masses.

In pretty much all cases other than references, D's const system is fantast= ic.=20 It really simplified things in comparison to C++, and is overall a definite= =20 improvement. It's just with references that there's a big problem, and with= =20 them, they're better than what you get with Java's final, but it's still=20 seriously lacking due to the whole thing becoming const instead of just the= =20 referent. =2D Jonathan M Davis
Aug 13 2010
prev sibling next sibling parent Jonathan M Davis <jmdavisprog gmail.com> writes:
On Friday, August 13, 2010 16:30:46 dsimcha wrote:
 I still don't understand:  What's so bad about Rebindable?  Yes, it's not
 the syntactically prettiest thing in the world, but complaining about it
 is like complaining about climbing a molehill when you've got Mount
 Everest to climb next. My previous gripe about it was that it didn't
 support interfaces, but I just realized that Shin Fujishiro fixed this a
 while back.

Well, first of all, the code is much uglier with it, though that's arguably a fairly superficial complaint. Part of it is the fact that it really _should_ be in the language itself rather than having to use Rebindable. Honestly, I haven't messed with it in a while, so I don't remember all of the issues. I've never liked it, and I've always had trouble in getting it to work. Now, maybe that's totally due to bugs that may be fixed now, but it's always been problematic, and certainly my gut reaction is that the type system is deficient because it can't do it itself. Having to use Rebindable!() is definitely worse than being able to do things like const (T)*. But maybe with all of the bugs worked out and just getting used it, it's a good and reasonable solution. Honestly though, I've never been able to get it to work right, and I've never been happy about the fact that the type system can't handle it itself. Obviously, I need to go back and try and use it again. However, with inout broken and Object not being const- correct, I'm not sure that I'll get very far with using const and immutable anyway. Still, it would be so nice if it could just be handled cleanly by the type system. - Jonathan M Davis
Aug 13 2010
prev sibling next sibling parent Leandro Lucarella <luca llucax.com.ar> writes:
Tomek Sowiński, el 14 de agosto a las 02:52 me escribiste:
 Walter Bright napisał:
 
 But there is a solution:
 
 const(Object)* o;

Interesting. How do you cook that with polymorphism? This doesn't work: interface I { } class A : I {} void main() { immutable A a = new immutable(A); immutable(A)* ap = &a; // Error: cannot implicitly convert expression (ap) of type immutable(A)* to immutable(I)* immutable(I)* ip = ap; } BTW, should this work?: immutable(A)* ap = &new immutable(A); Now it fails with "new immutable(A) is not an lvalue".

I don't think so, you're taking the address of a temporary. -- Leandro Lucarella (AKA luca) http://llucax.com.ar/ ---------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------- Did you see the frightened ones? Did you hear the falling bombs? Did you ever wonder why we had to run for shelter when the promise of a brave new world unfurled beneath a clear blue sky?
Aug 13 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 13 Aug 2010 19:30:46 -0400, dsimcha <dsimcha yahoo.com> wrote:

 == Quote from Jonathan M Davis (jmdavisprog gmail.com)'s article
 On Friday, August 13, 2010 15:03:07 Tomek Sowiński wrote:
 I agree with the content, but not the tone. D's const makes all other
 mainstream const systems look petty. Applying the concept of  

 has been revolutionary (hail Walter). Tail const is just a cable to  

 socket to make this wonderful device work out-of-box for programming
 masses.

fantast ic. It really simplified things in comparison to C++, and is overall a definite improvement. It's just with references that there's a big problem, and with them, they're better than what you get with Java's final, but it's still seriously lacking due to the whole thing becoming const instead of just the referent. - Jonathan M Davis

I still don't understand: What's so bad about Rebindable? Yes, it's not the syntactically prettiest thing in the world, but complaining about it is like complaining about climbing a molehill when you've got Mount Everest to climb next. My previous gripe about it was that it didn't support interfaces, but I just realized that Shin Fujishiro fixed this a while back.

There are other reasons to have tail-const other than classes. For example, there's no equivalent custom-range idiom for const(T)[]. You simply can't make a custom range tail-const. I admit I haven't used Rebindable much, but last I checked it was severely out of date. -Steve
Aug 16 2010
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Mon, 16 Aug 2010 09:11:20 -0400, dsimcha <dsimcha yahoo.com> wrote:

 == Quote from Steven Schveighoffer (schveiguy yahoo.com)'s article
 There are other reasons to have tail-const other than classes.  For
 example, there's no equivalent custom-range idiom for const(T)[].  You
 simply can't make a custom range tail-const.
 I admit I haven't used Rebindable much, but last I checked it was  
 severely
 out of date.
 -Steve

Rebindable's gotten a significant facelift lately. Admittedly there are still bugs but these are bugs in alias this/opDot, not in rebindable itself.

How do you tail-const a range? struct Node(V) { V v; Node * next; } struct Range(V) { Node!V *n; void popFront() {n = n.next}; } How does one implement popFront so it works on: Rebindable!(const(Range!V)) r; ??? Rebindable is specifically for class references, it does nothing for ranges. -Steve
Aug 16 2010
prev sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
--e0cb4e887f45109116048e30fe54
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

Where can I read about this type system proposal you speak of?

2010/8/13 Tomek Sowi=C5=84ski <just ask.me>

 dsimcha napisa=C5=82:

 1.   It's difficult to create non-trivial immutable data structures, an=


 often impossible without relying on either unchecked casts or unnecessa=


 copying.

Yes. I think the only true solution is 'unique' in the language. This is =

 most wanted from Bartosz's type system proposal. Ah well, so
 when's D3 coming out? :)


 Tomek

--e0cb4e887f45109116048e30fe54 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Where can I read about this type system proposal you speak of?<br><br><div = class=3D"gmail_quote">2010/8/13 Tomek Sowi=C5=84ski <span dir=3D"ltr">&lt;<= a href=3D"mailto:just ask.me">just ask.me</a>&gt;</span><br><blockquote cla= ss=3D"gmail_quote" style=3D"margin: 0pt 0pt 0pt 0.8ex; border-left: 1px sol= id rgb(204, 204, 204); padding-left: 1ex;"> dsimcha napisa=C5=82:<br> <div class=3D"im"><br> &gt; 1. =C2=A0 It&#39;s difficult to create non-trivial immutable data stru= ctures, and<br> &gt; often impossible without relying on either unchecked casts or unnecess= ary<br> &gt; copying.<br> <br> </div>Yes. I think the only true solution is &#39;unique&#39; in the langua= ge. This is my most wanted from Bartosz&#39;s type system proposal. Ah well= , so<br> when&#39;s D3 coming out? :)<br> <font color=3D"#888888"><br> <br> Tomek<br> </font></blockquote></div><br> --e0cb4e887f45109116048e30fe54--
Aug 19 2010