www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - PhobosWatch: manifest => enum

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Timestamp:
     12/27/07 20:47:21 (5 hours ago)
Author:
     walter
Message:

     manifest => enum
http://www.dsource.org/projects/phobos/changeset/536

--bb
Dec 27 2007
next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Bill Baxter wrote:
 Timestamp:
     12/27/07 20:47:21 (5 hours ago)
 Author:
     walter
 Message:
 
     manifest => enum
 http://www.dsource.org/projects/phobos/changeset/536
 

Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdKTVd0kWM4JG3k8RAvnWAJ9t+pKVObDUmUdJynxKdp3+A3f5gQCfa2tK FQqMoW9i0nPhmzIQpN18E7o= =c0ln -----END PGP SIGNATURE-----
Dec 27 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 	:(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.
Dec 28 2007
next sibling parent Hannibal Lector <hannibal whatseatingu.com> writes:
Walter Bright Wrote:

 Jérôme M. Berger wrote:
 	:(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

"Walter Fricassé" Ingredients (serve six): 3 tablespoons oil, 2 big onions, sliced fine, 1 kg Walter (cut into pieces), Salt & pepper, 3 chopped tomatoes, Thyme & chopped parsley, 1/2 teaspoon ginger/garlic mixture. Method: Heat oil and fry onion until transparent. Add pieces of Walter, season with salt and pepper. Stir-fry until pieces of Walter are lightly brown. Add tomatoes, thyme, chopped parsley and ginger/garlic mixture. Sprinkle over a little more salt and some water (half cup). Mix, cover and cook until Walter is tender and curry, dry. Serve with rice,lentil soup and chutneys.
Dec 28 2007
prev sibling next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This is an artificial distinction: you *are* adding a fourth way to declare a constant, the only question is what syntax to use: either a counter-intuitive extension to enums or a new keyword (or a minor extension to the alias keyword as was suggested by somebody). BTW, I think it was Janice who suggested that the compiler should know whether a constant needs to be manifest or not (depending on whether its address is taken somewhere). This would remove the need for a way to distinguish manifest constants explicitly. Any thoughts on that? Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdNVCd0kWM4JG3k8RAu1kAJ97kgXJW4tIcb0ZQYPHNHjmFe+JiwCggGuI GIlw7vHFlXSNCFo5qI1pSYA= =FcvO -----END PGP SIGNATURE-----
Dec 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This is an artificial distinction: you *are* adding a fourth way to declare a constant,

Not really, just loosened up the restrictions on enum. The implementation code is actually simpler <g>.
 the only question is what syntax to use: either
 a counter-intuitive extension to enums or a new keyword (or a minor
 extension to the alias keyword as was suggested by somebody).

I found the "use an alias when declaring one constant" and "use an enum when declaring more than one constant" to be difficult to justify. It's like saying arrays with only one element should not be allowed.
 	BTW, I think it was Janice who suggested that the compiler should
 know whether a constant needs to be manifest or not (depending on
 whether its address is taken somewhere). This would remove the need
 for a way to distinguish manifest constants explicitly. Any thoughts
 on that?

The reason this won't work is because: const int x = 3; will type x as const(int), not int. There needs to be a way to declare a constant of type int.
Dec 28 2007
next sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 28 Dec 2007 11:15:08 -0000, Walter Bright  =

<newshound1 digitalmars.com> wrote:
 	BTW, I think it was Janice who suggested that the compiler should
 know whether a constant needs to be manifest or not (depending on
 whether its address is taken somewhere). This would remove the need
 for a way to distinguish manifest constants explicitly. Any thoughts
 on that?

The reason this won't work is because: const int x =3D 3; will type x as const(int), not int. There needs to be a way to declare=

 constant of type int.

This doesn't make sense to me. Why would you ever want a constant that w= as = not const? and particularly a manifest constant? The only way you can change a = manifest constant is by changing the source code. Things don't come much more constant than = that. The only conceivable reason I can think of is that there is a bug or = misfeature somewhere where something needs to be type equal when in fact it needs only to be = = type compatible.
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruce Adams wrote:
 On Fri, 28 Dec 2007 11:15:08 -0000, Walter Bright 
 <newshound1 digitalmars.com> wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare 
 a constant of type int.

This doesn't make sense to me. Why would you ever want a constant that was not const? and particularly a manifest constant? The only way you can change a manifest constant is by changing the source code. Things don't come much more constant than that. The only conceivable reason I can think of is that there is a bug or misfeature somewhere where something needs to be type equal when in fact it needs only to be type compatible.

The issue stems from trying to decide if const(T) is always, or only sometimes, a different type than T. It must be a different type if T is a struct or class type. Pulling on the string of trying to make the type system completely consistent (so that generic code will work), and on the idea that a struct can be used to wrap any type, and one is drawn inevitably to conclude that const(T) is always a different type from T.
Dec 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 and one is drawn
 inevitably to conclude that const(T) is always a different type from T.

I don't think anyone's arguing with that. We're just saying that the behavior of auto x = could be tweaked to drop the head constancy.

Yes, but then other things break.
 It's the same with passing things to functions.
 
     void f(int x) { /*...*/ }
     f(DAYS_IN_WEEK);
 
 we want this to compile, right? Even though DAYS_IN_WEEK is likely to
 have type invariant(uint).

It isn't hard to make invariant(uint) implicitly convertible to uint, so that can work.
 You should always be allowed to create a
 mutable copy (though which preserves tail constancy).

There is no way to do this - unless you can come up with a way to declare tail const member functions in a reasonable way. I failed to solved this problem.
Dec 28 2007
prev sibling next sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 
 I found the "use an alias when declaring one constant" and "use an enum 
 when declaring more than one constant" to be difficult to justify. It's 
 like saying arrays with only one element should not be allowed.

The weird thing for me is that this will allow individual enums of any type but grouped enums of only numeric types. Sean
Dec 28 2007
next sibling parent reply Sean Kelly <sean f4.ca> writes:
Sean Kelly wrote:
 Walter Bright wrote:
 I found the "use an alias when declaring one constant" and "use an 
 enum when declaring more than one constant" to be difficult to 
 justify. It's like saying arrays with only one element should not be 
 allowed.

The weird thing for me is that this will allow individual enums of any type but grouped enums of only numeric types.

err... make that unnamed and named. One could argue that support for unnamed enums is simply for ease of porting from C rather than because it's a feature that really makes sense in a new language. So making it even more entrenched is undesirable. But we need the feature and if "enum" is it then... Sean
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 err... make that unnamed and named.  One could argue that support for 
 unnamed enums is simply for ease of porting from C rather than because 
 it's a feature that really makes sense in a new language.  So making it 
 even more entrenched is undesirable.  But we need the feature and if 
 "enum" is it then...

Right. Anonymous enums are already conventionally used not to declare enums, but a bunch of (not necessarily related) manifest constants. In fact, in C++ we see: template<> class factorial<1> { public: enum { result = 1 }; }; where anonymous enums are clearly used to declare manifest constants.
Dec 28 2007
next sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 28 Dec 2007 18:43:38 -0000, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Sean Kelly wrote:
 err... make that unnamed and named.  One could argue that support for=


 unnamed enums is simply for ease of porting from C rather than becaus=


 it's a feature that really makes sense in a new language.  So making =


 even more entrenched is undesirable.  But we need the feature and if =


 "enum" is it then...

Right. Anonymous enums are already conventionally used not to declare =

 enums, but a bunch of (not necessarily related) manifest constants. In=

 fact, in C++ we see:

 template<> class factorial<1>
 {
    public:
      enum { result =3D 1 };
 };

 where anonymous enums are clearly used to declare manifest constants.

That looks similar to the workaround for old compilers which didn't = support static consts. template<> class factorial<1> { public: static const int result =3D 1; }; I can see the desire for manifest constants here. You particularly don't= = want to bloat your code with each intermediate value in a compile time computed expression. I've just tried an experiment on gcc (cygming 3.4.4). Compiled objects a= re = the same size whether enum or static const is used in a factorial template. I see this as = evidence that the enum hack is still just that, a hack to workaround dodgey compilers. So you are = propagating a hack into D if the C++ example is one of your primary reasons.
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruce Adams wrote:
 I can see the desire for manifest constants here. You particularly don't 
 want to bloat your code
 with each intermediate value in a compile time computed expression.
 I've just tried an experiment on gcc (cygming 3.4.4). Compiled objects 
 are the same size whether
 enum or static const is used in a factorial template. I see this as 
 evidence that the enum hack
 is still just that, a hack to workaround dodgey compilers. So you are 
 propagating a hack into D
 if the C++ example is one of your primary reasons.

Doesn't "static const" strike you has a hack? It does to me. It has unique properties that are not deducible from the meaning of static or const by themselves. "const" is also a hack in C++, as "const int" sometimes means "const int" and sometimes just "int".
Dec 28 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 28 Dec 2007 21:32:49 -0000, Walter Bright  
<newshound1 digitalmars.com> wrote:

 Bruce Adams wrote:
 I can see the desire for manifest constants here. You particularly  
 don't want to bloat your code
 with each intermediate value in a compile time computed expression.
 I've just tried an experiment on gcc (cygming 3.4.4). Compiled objects  
 are the same size whether
 enum or static const is used in a factorial template. I see this as  
 evidence that the enum hack
 is still just that, a hack to workaround dodgey compilers. So you are  
 propagating a hack into D
 if the C++ example is one of your primary reasons.

Doesn't "static const" strike you has a hack? It does to me. It has unique properties that are not deducible from the meaning of static or const by themselves. "const" is also a hack in C++, as "const int" sometimes means "const int" and sometimes just "int".

Not really no. The enum hack is a hack because it is a way of making the compiler do something that was not possible in older versions of the language. static const is not a hack in that sense because it is an endorsed part of the C++ standard. The meaning of static const makes perfect sense from the meanings of static and const in C++. static meaning - belonging to the class (actually the current scope) rather than an instance of the class and const meaning a compile time constant. const can of course mean a read-only view in C++ as well which leads to other confusions and contortions. Personally I find the static meaning "compile time" in D a little surprising and not at all related to any of its C++ meanings.
Dec 28 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Walter Bright wrote:
 Sean Kelly wrote:
 err... make that unnamed and named.  One could argue that support for 
 unnamed enums is simply for ease of porting from C rather than because 
 it's a feature that really makes sense in a new language.  So making 
 it even more entrenched is undesirable.  But we need the feature and 
 if "enum" is it then...

Right. Anonymous enums are already conventionally used not to declare enums, but a bunch of (not necessarily related) manifest constants. In fact, in C++ we see: template<> class factorial<1> { public: enum { result = 1 }; }; where anonymous enums are clearly used to declare manifest constants.

Yup. I simply find it troublesome that /conventions/ in C/C++ are being used as justification for new language features in D. For programmers new to D who do not have a C/C++ background, I'm not sure I'd want to resort to a history lesson when asked why 'enum', which is short for 'enumeration' is used to signify manifest constants. Sean
Dec 30 2007
parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Mon, 31 Dec 2007 00:18:19 -0000, Sean Kelly <sean f4.ca> wrote:

 Walter Bright wrote:
 Sean Kelly wrote:
 err... make that unnamed and named.  One could argue that support fo=



 unnamed enums is simply for ease of porting from C rather than becau=



 it's a feature that really makes sense in a new language.  So making=



 it even more entrenched is undesirable.  But we need the feature and=



 if "enum" is it then...



 enums, but a bunch of (not necessarily related) manifest constants. I=


 fact, in C++ we see:
  template<> class factorial<1>
 {
   public:
     enum { result =3D 1 };
 };
  where anonymous enums are clearly used to declare manifest constants=


 Yup.  I simply find it troublesome that /conventions/ in C/C++ are bei=

 used as justification for new language features in D.  For programmers=

 new to D who do not have a C/C++ background, I'm not sure I'd want to =

 resort to a history lesson when asked why 'enum', which is short for  =

 'enumeration' is used to signify manifest constants.


 Sean

As I've tried to point out before. Its not a 'convention' is a hack from= yesteryear. If you have a C++ compiler newer than 6 years old you should= use static const instead of enum for this.
Dec 30 2007
parent Sean Kelly <sean f4.ca> writes:
Bruce Adams wrote:
 
 As I've tried to point out before. Its not a 'convention' is a hack from
 yesteryear. If you have a C++ compiler newer than 6 years old you should
 use static const instead of enum for this.

Yup. And I do. But many long-time C++ programmers still seem to use 'enum' out of habit. What ever happened to Don's suggestion of using 'pure' for this anyway? Sean
Dec 30 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Sean Kelly wrote:
 The weird thing for me is that this will allow individual enums of any 
 type but grouped enums of only numeric types.

The following will work: enum { x = 3, // x is int y = 4L, // y is long } The idea is that for anonymous enums, there is no type for the enumeration as a whole. Therefore, each member of the enumeration can have a different type.
Dec 28 2007
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Walter Bright wrote:
 The idea is that for anonymous enums, there is no type for the
 enumeration as a whole. Therefore, each member of the enumeration can
 have a different type.

... so annonymous enums have no type for the enumeration as a whole, but named enums will? Can named enums have a type other than int?
Dec 28 2007
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Jason House" wrote
 Walter Bright wrote:
 The idea is that for anonymous enums, there is no type for the
 enumeration as a whole. Therefore, each member of the enumeration can
 have a different type.

... so annonymous enums have no type for the enumeration as a whole, but named enums will? Can named enums have a type other than int?

This currently works: enum X : long { one = 1L, two = 2L } This also currently works: enum : long { one = 1L, two = 2L } Enums can currently be based on any integral type. That brings up a couple of questions. Under the new regime, what happens with this code: enum {one, two} enum : long {one, two} enum : float {one, two} enum { float one = 1.0, two } and will enums in curly braces that are manifest constants have to be specified with semicolons or commas? ??? -Steve
Dec 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 That brings up a couple of questions.  Under the new regime, what happens 
 with this code:
 
 enum {one, two}

No change here, one and two are ints with values 0 and 1.
 enum : long {one, two}

No change here, one and two are longs with values 0 and 1.
 enum : float {one, two}

Change here, floats are now allowed as the base type, one and two are floats with values 0.0f and 1.0f.
 enum
 {
   float one = 1.0,
   two
 }

Change here, one is float of value 1.0f, two is float of value 2.0f.
 and will enums in curly braces that are manifest constants have to be 
 specified with semicolons or commas?

commas.
Dec 28 2007
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Jason House wrote:
 Walter Bright wrote:
 The idea is that for anonymous enums, there is no type for the
 enumeration as a whole. Therefore, each member of the enumeration can
 have a different type.

... so annonymous enums have no type for the enumeration as a whole, but named enums will?

Yes. That is the way enums already work.
 Can named enums have a type other than int?

Yes. This has always been possible: enum Foo : long { X, Y }
Dec 28 2007
prev sibling parent Jason House <jason.james.house gmail.com> writes:
Jason House wrote:
 ... so annonymous enums have no type for the enumeration as a whole, but
 named enums will?  Can named enums have a type other than int?

I really meant other than integral types.  More accurately, however, I should have asked if named enums can contain as diverse a set of types as unnamed enums. I'm not sure what can be a manifest constant...  int, long, float, string, struct, etc...
Dec 28 2007
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 28 Dec 2007 10:35:38 -0800, Walter Bright wrote:

 Sean Kelly wrote:
 The weird thing for me is that this will allow individual enums of any 
 type but grouped enums of only numeric types.

The following will work: enum { x = 3, // x is int y = 4L, // y is long } The idea is that for anonymous enums, there is no type for the enumeration as a whole. Therefore, each member of the enumeration can have a different type.

Will your syntax allow declarations of single values? e.g. enum x = 3; enum y = 4L; Or will we have to use the {} form? enum {x = 3} enum {y = 4L} -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 28 2007
next sibling parent Leandro Lucarella <llucax gmail.com> writes:
Derek Parnell, el 29 de diciembre a las 08:43 me escribiste:
 On Fri, 28 Dec 2007 10:35:38 -0800, Walter Bright wrote:
 
 Sean Kelly wrote:
 The weird thing for me is that this will allow individual enums of any 
 type but grouped enums of only numeric types.

The following will work: enum { x = 3, // x is int y = 4L, // y is long } The idea is that for anonymous enums, there is no type for the enumeration as a whole. Therefore, each member of the enumeration can have a different type.

Will your syntax allow declarations of single values? e.g. enum x = 3; enum y = 4L; Or will we have to use the {} form? enum {x = 3} enum {y = 4L}

With this example, I guess Walter will say: enum { x = 3, y = 4L } =) But the original question still remains, will be enum x = 3; supported? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- You can do better than me. You could throw a dart out the window and hit someone better than me. I'm no good! -- George Constanza
Dec 28 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 Will your syntax allow declarations of single values? e.g.
 
   enum x = 3;
   enum y = 4L;

Yes.
Dec 28 2007
prev sibling next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:

 the only question is what syntax to use: either
 a counter-intuitive extension to enums or a new keyword (or a minor
 extension to the alias keyword as was suggested by somebody).

I found the "use an alias when declaring one constant" and "use an enum when declaring more than one constant" to be difficult to justify. It's like saying arrays with only one element should not be allowed.

than one constant. The suggestions were more on the line of: keyword { int x = 42; float y = 42.42; } Where "keyword" could be one of "constant", "invariant", "alias" or anything that means constant (but not "enum", anymore than "abstract" or "cat": those mean something else).
 
     BTW, I think it was Janice who suggested that the compiler should
 know whether a constant needs to be manifest or not (depending on
 whether its address is taken somewhere). This would remove the need
 for a way to distinguish manifest constants explicitly. Any thoughts
 on that?

The reason this won't work is because: const int x = 3; will type x as const(int), not int. There needs to be a way to declare a constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here? Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdQ18d0kWM4JG3k8RAmsFAKCoFDdLY69rxxKh/ByoRR+eKKHtVACfb2lK u2sdS/cU51nKadzhMs1burc= =hxW2 -----END PGP SIGNATURE-----
Dec 28 2007
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
""Jérôme M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare a
 constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here?

I agree with everything you are saying, except I think Walter is thinking of the case: const int x; auto y = x; // y is now const -Steve
Dec 28 2007
next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 28 Dec 2007 14:55:48 -0000, Steven Schveighoffer  =

<schveiguy yahoo.com> wrote:

 ""J=E9r=F4me M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x =3D 3;
 will type x as const(int), not int. There needs to be a way to decla=



 a
 constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x =3D 42; int y =3D x; // <=3D This should work func (x); // <=3D This should work too Or is there something I'm missing here?

I agree with everything you are saying, except I think Walter is =

 thinking of
 the case:

 const int x;
 auto y =3D x;  // y is now const

 -Steve

That would seem to be a 'problem' with auto. Should auto inherit constne= ss or not? i.e. const int x =3D 5; auto y =3D x; y =3D 4; // legal or breaking const correctness? The spec should say either: 1. auto always inherits constness (y is const) 2a. auto always throws away constness (y is not const) 2b. auto assigns the type but leaves const qualification to the user. i.e. const auto y =3D x; and auto y =3D x; are two different declarations one const one not regardless of x. Which is it now? and which makes most sense? I'm not sure I like 1 as a typical usage might be to create a temporary = = for something that is const and thus cannot be used directly. That said if x was a pointer = = rather than a POD type it must absolutely inherit the constness so for constistency it must be = 1. Although, I can't help thinking that values and pointers/references can = = have different behaviour because they are semantically quite different beasts. Regards, Bruce.
Dec 28 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 y is a /copy/ of x, and clearly it should be possible to make a copy
 of a const thing and have the copy be mutable.

That doesn't work for structs or classes.
 Head constness needs to
 be dropped here, exactly as it is dropped in template distinction in
 D2.008 (t!(int) is the same thing as t!(const(int)) unless special
 syntax is used).

The problem with head const is that to make the concept work, you also need the concept of tail const, and you also need to be able to separately manipulate head & tail const. That's how D's first cut at const worked, and it was a disaster. Just for fun, how would we define a tail const member function?
 I don't see why any of this isn't possible. Maybe that's because I'm
 dumb and I'm missing something obvious, but I'm baffled as to what it
 is. And if I /haven't/ missed anything, then we don't need a new
 keyword /at all/ - be it enum, manifest, or anything else.

Believe me, we've been down this road for a year, trying all kinds of things. It doesn't work. We can get tantalizingly close to closing the circle, but cannot quite get there.
Dec 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 y is a /copy/ of x, and clearly it should be possible to make a copy
 of a const thing and have the copy be mutable.


It doesn't? For structs struct S {} const S x; auto y = x;

Imagine you have: struct S { int* p; } Because const is transitive, const(S) implies that now p points to const. But if you strip off the const in the assignment, you've lost the const-ness of p, and now you have a gaping hole in the const-correctness.
 Isn't
 
     class C {}
     enum { C x; }
     auto y = x;
 
 exactly the same problem?

No, because y will be of type C and will have the value null.
 Just for fun, how would we define a tail const member function?

Yeah, I agreed, it wouldn't work for classes. Still don't see any problem for structs though.

You'd need tail const member functions for structs, too.
Dec 28 2007
parent reply Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Walter Bright wrote:
 Janice Caron wrote:
 On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 y is a /copy/ of x, and clearly it should be possible to make a copy
 of a const thing and have the copy be mutable.




*BEEP* Confusion warning: you mean *constant*, not "const". Huge difference. :)
 That doesn't work for structs or classes.

It doesn't? For structs struct S {} const S x; auto y = x;

Imagine you have: struct S { int* p; } Because const is transitive, const(S) implies that now p points to const. But if you strip off the const in the assignment, you've lost the const-ness of p, and now you have a gaping hole in the const-correctness.

The problem with the current const in D is the lack of orthogonality. As far as I can see, those problems would be solved by my orthogonal const proposal posted earlier. Constant values (such as structs) are by their nature always implicitly convertible to mutable values (worst case: just make a copy). The problem with the current const iteration is that there is no way to separate the orthogonal concepts constness (as in constants) from read-only access of references. Also, as far as I can see (not very) there would also not be any need for a third type of constant (the manifest one). const int x = 5; could behave like a template and only be instantiated when the address of x is taken. The result would be a const design, equally powerful but without spoiling the beauty and simplicity of the D1.0 const. In D1.0, when something is *constant*, you simply declare it as const: struct T { const int a = 5; int b; } static assert(T.sizeof == int.sizeof); No need to stop and think. In D2.0 it seems like you have to go through: "Hmm, this value will never change. I will mark it as const.... no wait invariant... or maybe manifest constant using the enumeration hack?" -- Oskar
Jan 04 2008
next sibling parent Oskar Linde <oskar.lindeREM OVEgmail.com> writes:
Janice Caron wrote:
 On 1/4/08, Oskar Linde <oskar.lindeREM ovegmail.com> wrote:
 struct T {
         const int a = 5;
         int b;
 }

 static assert(T.sizeof == int.sizeof);

I see a problem there. You said that a is template-like, and instantiated only if its address is taken. That means that T itself is template-like, because it contains a. But I don't see how it can be.

a is a constant known at compile time. It doesn't need to be part of the run time struct representation. There are two types of constants. Those known at compile time and those whose values are computed at run time. D1 uses two different keywords for those (const / final) and D2.009 defines both using the same keyword (const). In D2.009: struct T { const int a = 5; const int b; int c; } T t; T.sizeof == 8 // a occupies no space &t.a -> error 5 is not a lvalue &t.b -> ok &t.c -> ok I'm not sure this behavior in D2.009 is intentional or not though. &t.a could become legal (the template-like idea) but all instances of T would share the same a. So: T x,y; &x.a == &y.a; &x.b != &y.b; -- Oskar
Jan 04 2008
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
Oskar Linde wrote:
 In D1.0, when 
 something is *constant*, you simply declare it as const:
 
 struct T {
     const int a = 5;
     int b;
 }
 
 static assert(T.sizeof == int.sizeof);
 
 No need to stop and think. In D2.0 it seems like you have to go through: 
 "Hmm, this value will never change. I will mark it as const.... no wait 
 invariant... or maybe manifest constant using the enumeration hack?"

Yup. This is what I don't like about the current design as well. What was your orthogonal const proposal again? I've lost it amid the flood of const posts. Sean
Jan 04 2008
parent Jason House <jason.james.house gmail.com> writes:
Sean Kelly Wrote:

 Oskar Linde wrote:
 In D1.0, when 
 something is *constant*, you simply declare it as const:
 
 struct T {
     const int a = 5;
     int b;
 }
 
 static assert(T.sizeof == int.sizeof);
 
 No need to stop and think. In D2.0 it seems like you have to go through: 
 "Hmm, this value will never change. I will mark it as const.... no wait 
 invariant... or maybe manifest constant using the enumeration hack?"

Yup. This is what I don't like about the current design as well. What was your orthogonal const proposal again? I've lost it amid the flood of const posts.

I looked his proposal up earlier today. It's at http://www.csc.kth.se/~ol/const.pdf I almost posted asking how D 2.009 "const" differs from his "in" and D 2.009 "invariant" differs from his "const". I thought I understood, but then I noticed him use "const in" and "const const(T)"...
Jan 04 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 
 Head constness needs to
 be dropped here, exactly as it is dropped in template distinction in
 D2.008 (t!(int) is the same thing as t!(const(int)) unless special
 syntax is used).

The problem with head const is that to make the concept work, you also need the concept of tail const, and you also need to be able to separately manipulate head & tail const. That's how D's first cut at const worked, and it was a disaster. Just for fun, how would we define a tail const member function?

One wouldn't, because that does not make sense. More exactly, it does not make sense to change the 'this' implicit parameter(as in "this = foo"), so the "head" value of 'this' is always immutable. This means that in a member function, the 'this' parameter should always be either head const, or head+tail const, but never just tail const. Isn't it so? -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 05 2008
prev sibling next sibling parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Steven Schveighoffer wrote:
 ""J�r�me M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare a
 constant of type int.

directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here?

I agree with everything you are saying, except I think Walter is thinking of the case: const int x; auto y = x; // y is now const

consistency with class/struct references and pointers, y should be const. This is due to the fact that it is impossible to define a mutable reference to a const class. So we have the following: const MyClass o = new MyClass(); auto y = o; // <= y must be of type const(MyClass) Which means that for consistency, the following should result in const definitions of y1...y3 too: enum { e = 42 } const int var = 42; auto y1 = var; auto y2 = e; auto y3 = 42; Since we work in the real world, we need to be pragmatic here. IMO all three of these should result in mutable variables, as well as the following: const (const (MyStruct)*) s = new MyStruct; auto y4 = s; // <= y4 should be const (MyStruct)* // ie mutable pointer to const data // if such a beast is allowed by the // language. And in that case, there's nothing that really prevents an object from being a mutable reference to a const instance. If the whole head const / tail const debate gets us a language in which we can't have a mutable pointer (or reference) to const data, then const-correctness means that y4 should be fully const (as well as "auto" object references), but even so y1...y3 should be mutable. However, this has nothing to to with having the compiler detect automatically if a const variable should be allocated or not. This should be possible whatever the choice for "auto" variables. Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdVs9d0kWM4JG3k8RAo0XAKCRO0C37/t0N7H8p8Cj8Zt63GDE7wCglC5L wbRK7avBl1kwVSLU1Y9O5/0= =SXv/ -----END PGP SIGNATURE-----
Dec 28 2007
prev sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Fri, 28 Dec 2007 17:32:32 -0000, Steven Schveighoffer  
<schveiguy yahoo.com> wrote:

 "Janice Caron" wrote

 I don't see why any of this isn't possible. Maybe that's because I'm
 dumb and I'm missing something obvious, but I'm baffled as to what it
 is. And if I /haven't/ missed anything, then we don't need a new
 keyword /at all/ - be it enum, manifest, or anything else.

I don't disagree with you :) I'm merely clarifying Walter's gripe. I think there may be a better reason why your idea wouldn't work, but I am not sure how the compiler is implemented, so I can't really say much. However, it might be a problem when you are defining constants in one module to be used in other modules. How does the compiler know that those constants won't have their address taken somewhere else? When the compiler creates the object file, it has to assume since the symbol is public, it can have its address taken, no? If you had a specific D linker, you could modify the linker to take this into account, but that is not the case today. -Steve

never use any storage. However, that seems a little bizzare.
Dec 28 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
     BTW, I think it was Janice who suggested that the compiler should
 know whether a constant needs to be manifest or not (depending on
 whether its address is taken somewhere). This would remove the need
 for a way to distinguish manifest constants explicitly. Any thoughts
 on that?

const int x = 3; will type x as const(int), not int. There needs to be a way to declare a constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it.

This is where we start to see a problem if we don't type const(int) differently from int - we no longer have a straightforward rule of what the type of &x is. This may not seem consequential in trivial cases, but when you start having more complex generic code, things can get maddening. C++ tries to have it both ways, leading to startling complexity in the language definition, and a lot of intractable problems cropping up in metaprogramming.
 Are you
 telling us that the following code will fail:
 
 void func (int param)
 {
 }
 
 const int x = 42;
 int y = x;              // <= This should work
 func (x);               // <= This should work too
 
 	Or is there something I'm missing here?

These will both still work.
Dec 28 2007
next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
     BTW, I think it was Janice who suggested that the compiler should
 know whether a constant needs to be manifest or not (depending on
 whether its address is taken somewhere). This would remove the need
 for a way to distinguish manifest constants explicitly. Any thoughts
 on that?

const int x = 3; will type x as const(int), not int. There needs to be a way to declare a constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it.

This is where we start to see a problem if we don't type const(int) differently from int - we no longer have a straightforward rule of what the type of &x is. This may not seem consequential in trivial cases, but when you start having more complex generic code, things can get maddening. C++ tries to have it both ways, leading to startling complexity in the language definition, and a lot of intractable problems cropping up in metaprogramming.

What I don't really see is why manifest constant need to be "int" rather than "const (int)". After all, any attempt to use one as non-const will fail (or should) so there's no real reason that they can't be "const (int)", is there? Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdVOgd0kWM4JG3k8RAoKRAJ0SeQvVUmcwputyeK6YImrMm4EiIACgrTjQ Mvv9xUyUjcG8CY5w+7u0+cc= =lJgZ -----END PGP SIGNATURE-----
Dec 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 	I agree that "const (int)" and "int" should be different types.
 What I don't really see is why manifest constant need to be "int"
 rather than "const (int)". After all, any attempt to use one as
 non-const will fail (or should) so there's no real reason that they
 can't be "const (int)", is there?

Since one cannot take the address of a manifest constant, the requirement that it be immutable goes away, since it can never be an lvalue.
Dec 28 2007
parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
     I agree that "const (int)" and "int" should be different types.
 What I don't really see is why manifest constant need to be "int"
 rather than "const (int)". After all, any attempt to use one as
 non-const will fail (or should) so there's no real reason that they
 can't be "const (int)", is there?

Since one cannot take the address of a manifest constant, the requirement that it be immutable goes away, since it can never be an lvalue.

Right. Replace "const" by "invariant" in my comment and the main point still stands: couldn't the compiler determine automatically if we take the address of an invariant variable and allocate memory for it or not based on that? This would remove the need for a special keyword/syntax for manifest constants: just declare them as "invariant" and let the compiler do the work. Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD4DBQFHdWPKd0kWM4JG3k8RAk2kAJ9PDwO5TsX2Sg7pkkVnCGcZP/ty0ACUDgdQ 6Qvm9osRRpRIXQ001Xk2Xw== =92yB -----END PGP SIGNATURE-----
Dec 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 Right. Replace "const" by "invariant" in my comment and the main
 point still stands: couldn't the compiler determine automatically if
 we take the address of an invariant variable and allocate memory for
 it or not based on that? This would remove the need for a special
 keyword/syntax for manifest constants: just declare them as
 "invariant" and let the compiler do the work.

Yes, that could be done, but we're still stymied by the problem that we are unable to declare a constant of type 'int', only 'const(int)'.
Dec 28 2007
parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
 Right. Replace "const" by "invariant" in my comment and the main
 point still stands: couldn't the compiler determine automatically if
 we take the address of an invariant variable and allocate memory for
 it or not based on that? This would remove the need for a special
 keyword/syntax for manifest constants: just declare them as
 "invariant" and let the compiler do the work.

Yes, that could be done, but we're still stymied by the problem that we are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no? Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdlu5d0kWM4JG3k8RAuVHAKCAkmTyAamEpMXMbgAY3IipNrX6qgCdFJ7y Ron+xf58hS43KfbHPsZVq7I= =YEWL -----END PGP SIGNATURE-----
Dec 29 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.
Dec 29 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

Huh? So the fix is to make constants not const? Seems like the problem lies either in auto or in the expectation that const will not be transferred using auto. I would actually expect there to be an unconst!(X) template required in there or something if it's going to strip off const. --bb
Dec 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Bill Baxter wrote:
 Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

Huh? So the fix is to make constants not const?

We don't have to worry about rvalue literals not being const, because: 2 = 3; is illegal anyway. But we do need to be concerned about its type, because of type inference.
 Seems like the problem 
 lies either in auto or in the expectation that const will not be 
 transferred using auto.  I would actually expect there to be an 
 unconst!(X) template required in there or something if it's going to 
 strip off const.

Would you really prefer to do: auto i = Unconst!(X); ? If const were not transferred with auto, then we run smack into the tail const problem again.
Dec 29 2007
prev sibling next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

Except that this is a different issue, on which I rambled at some length here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=64262 Basically, "auto" should drop the tail-most "const" if the language allows such types to exist and keep the "const" when it can't be dropped, e.g: - "const int" => "int"; - "const (T*)" => "(const (T))*" (if such is allowed); - "const (MyClass)" => "const (MyClass)" (since the tailmost type is actually the hidden reference type). This should be done whether the right-hand expression is a manifest constant, a "const" variable or an "invariant" variable. Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdtTnd0kWM4JG3k8RAtvGAKCKVXzhXMe286aco7/2ruWJl4KbiACfYgoE 8UPHk7cbO7bDQt9Q04tDnj0= =DyHZ -----END PGP SIGNATURE-----
Dec 29 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 	Basically, "auto" should drop the tail-most "const" if the language
 allows such types to exist and keep the "const" when it can't be
 dropped, e.g:
  - "const int" => "int";
  - "const (T*)" => "(const (T))*" (if such is allowed);
  - "const (MyClass)" => "const (MyClass)" (since the tailmost type
 is actually the hidden reference type).

I really did try to figure out how to make that work, but it just doesn't. Let's say we make a struct: struct MyInt { int x; ... and appropriate operator overloading ... } to make our own custom implementation of ints. What do we do with: const MyInt m = 3; auto n = m; Does n get const or not? If it is, now it is behaving differently from other types, and so is not a plug-in replacement. If it does not, what happens if MyInt has a pointer member? Suddenly, the const gets stripped from the pointer, and there is no const-correctness.
Dec 29 2007
next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 What do we do with:
 
     const MyInt m = 3;
     auto n = m;
 
 Does n get const or not? If it is, now it is behaving differently from
 other types, and so is not a plug-in replacement. If it does not, what
 happens if MyInt has a pointer member? Suddenly, the const gets stripped
 from the pointer, and there is no const-correctness.

OK, I see your point, but like Bill said I think the issue here is with the "auto" type inference rather than the "const". What is needed is for "auto" to be smarter than it is now, namely: - for atomic types, strip the const; - for pointers, if the language supports head const, then strip the tailmost const (ie make a mutable pointer to const data) otherwise keep the const; - for classes, keep the const; - for struct, the compiler already keeps track of whether the struct contains pointers or not because of the GC, doesn't it? In that case, if the struct contains pointers keep the const, and if it doesn't strip it (1); - in all cases, allow a "const auto" statement to force an "auto" declaration to be "const". This would work even if the right hand expression is not "const": int x = 42; const auto y = x; // y is now "const int" Jerome (1) It would be even nicer if the compiler could keep track of whether there are *mutable* pointers in the struct and strip the "const" from the "auto" variable if all pointers in the struct are "const" anyway, so it wouldn't break const-correctness: struct { int value; const int* pointer; } ConstStruct; struct { int value; int* pointer; } MutableStruct; const ConstStruct cs0; const MutableStruct ms0; auto cs1 = cs0; // <= cs1 is a ConstStruct since all // pointers inside are const anyway. auto ms1 = ms0; // <= ms1 is a const MutableStruct. cs0.value = 0; // Error, cs0 is const cs1.value = 1; // OK ms0.value = 2; // Error, ms0 is const ms1.value = 3; // Error, ms1 is const (2) In all I've said, you can replace "const" with "invariant" and the same arguments would apply. - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHd0yGd0kWM4JG3k8RAqAYAJ9ZAd4zCHvNIaHBsf5zfRlKKJd6WACdGyOo miKzodWMLGxjfeuJNO45Ig4= =NjKB -----END PGP SIGNATURE-----
Dec 29 2007
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Jérôme M. Berger wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Walter Bright wrote:
 What do we do with:

     const MyInt m = 3;
     auto n = m;

 Does n get const or not? If it is, now it is behaving differently from
 other types, and so is not a plug-in replacement. If it does not, what
 happens if MyInt has a pointer member? Suddenly, the const gets stripped
 from the pointer, and there is no const-correctness.

OK, I see your point, but like Bill said I think the issue here is with the "auto" type inference rather than the "const". What is needed is for "auto" to be smarter than it is now, namely: - for atomic types, strip the const; - for pointers, if the language supports head const, then strip the tailmost const (ie make a mutable pointer to const data) otherwise keep the const; - for classes, keep the const; - for struct, the compiler already keeps track of whether the struct contains pointers or not because of the GC, doesn't it? In that case, if the struct contains pointers keep the const, and if it doesn't strip it (1); - in all cases, allow a "const auto" statement to force an "auto" declaration to be "const". This would work even if the right hand expression is not "const": int x = 42; const auto y = x; // y is now "const int" Jerome (1) It would be even nicer if the compiler could keep track of whether there are *mutable* pointers in the struct and strip the "const" from the "auto" variable if all pointers in the struct are "const" anyway, so it wouldn't break const-correctness: struct { int value; const int* pointer; } ConstStruct; struct { int value; int* pointer; } MutableStruct; const ConstStruct cs0; const MutableStruct ms0; auto cs1 = cs0; // <= cs1 is a ConstStruct since all // pointers inside are const anyway. auto ms1 = ms0; // <= ms1 is a const MutableStruct.

That doesn't really make sense. You could push off const one level: ms1.value = 3; // Fine *ms1.pointer = 3; // Error; pointer is const That would make sense, but it would be hard to do, and it would be hard for a programmer to think "okay, I just did this assignment, now what is const in the struct and what isn't?" Structs make const hard. It'd be easier with just classes, I think.
Dec 30 2007
parent =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Christopher Wright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 What do we do with:

     const MyInt m = 3;
     auto n = m;

 Does n get const or not? If it is, now it is behaving differently from
 other types, and so is not a plug-in replacement. If it does not, what
 happens if MyInt has a pointer member? Suddenly, the const gets stripped
 from the pointer, and there is no const-correctness.

OK, I see your point, but like Bill said I think the issue here is with the "auto" type inference rather than the "const". What is needed is for "auto" to be smarter than it is now, namely: - for atomic types, strip the const; - for pointers, if the language supports head const, then strip the tailmost const (ie make a mutable pointer to const data) otherwise keep the const; - for classes, keep the const; - for struct, the compiler already keeps track of whether the struct contains pointers or not because of the GC, doesn't it? In that case, if the struct contains pointers keep the const, and if it doesn't strip it (1); - in all cases, allow a "const auto" statement to force an "auto" declaration to be "const". This would work even if the right hand expression is not "const": int x = 42; const auto y = x; // y is now "const int" Jerome (1) It would be even nicer if the compiler could keep track of whether there are *mutable* pointers in the struct and strip the "const" from the "auto" variable if all pointers in the struct are "const" anyway, so it wouldn't break const-correctness: struct { int value; const int* pointer; } ConstStruct; struct { int value; int* pointer; } MutableStruct; const ConstStruct cs0; const MutableStruct ms0; auto cs1 = cs0; // <= cs1 is a ConstStruct since all // pointers inside are const anyway. auto ms1 = ms0; // <= ms1 is a const MutableStruct.

That doesn't really make sense. You could push off const one level: ms1.value = 3; // Fine *ms1.pointer = 3; // Error; pointer is const That would make sense, but it would be hard to do, and it would be hard for a programmer to think "okay, I just did this assignment, now what is const in the struct and what isn't?"

struct type that the programmer didn't request explicitly: ms1 would be of type struct { int value; const int* pointer; } SomeInternalStructTypeThatIsDifferentFromConstStructAndFromMutableStruct; This could cause lots of other issues like: const MutableStruct ms2 = ms1; // <= Error cannot convert // implicitly, but it should. My suggestion wasn't perfect, but it should be reasonably straightforward to implement in the compiler without introducing any side-effects elsewhere (I hope).
 Structs make const hard. It'd be easier with just classes, I think.

True, but since structs already exist, we're stuck with them so we (well, Walter) must find a way to make const work with them. Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHeCh/d0kWM4JG3k8RAj49AJ9LFyp+AbjjtyVsrgsyMHep0sm8jACfT2Ra 8txL7dsLXvbNoi+EmrbKqew= =blb1 -----END PGP SIGNATURE-----
Dec 30 2007
prev sibling next sibling parent =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Janice Caron wrote:
 On 12/30/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 (1) It would be even nicer if the compiler could keep track of
 whether there are *mutable* pointers in the struct <snip>

I'm in favor of handing responsibility for that decision to the programmer. const T x = whatever; auto y = x.dup; Then it becomes the programmer's responsibility to implement dup for each type T. .dup is already built into arrays. It would be trivial for Walter to supply a dup() function for primative types (dup()ing a const(int) must yield an int result). For structs and classes, it becomes the programmer's problem. I /think/ this will work, and it will definitely work better than making poor "auto" do different things for each type.

do the right thing automagically. After all, it says so in the name! Moreover, this would allow "automatic manifest constants" to work (which was the main idea behind the suggestion: I was looking for a solution that would allow manifest constants to be of type "const int" instead of just "int"). Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHeL7Rd0kWM4JG3k8RAjskAJ44IRFUSZtBTnGEoiM8BgAvyFnKiACeICyB C10W7/hE0Yq5JrfIiWW3FeI= =t+QJ -----END PGP SIGNATURE-----
Dec 31 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jérôme M. Berger wrote:
 	OK, I see your point, but like Bill said I think the issue here is
 with the "auto" type inference rather than the "const". What is
 needed is for "auto" to be smarter than it is now, namely:
 
  - for atomic types, strip the const;
  - for pointers, if the language supports head const, then strip the
 tailmost const (ie make a mutable pointer to const data) otherwise
 keep the const;
  - for classes, keep the const;
  - for struct, the compiler already keeps track of whether the
 struct contains pointers or not because of the GC, doesn't it? In
 that case, if the struct contains pointers keep the const, and if it
 doesn't strip it (1);
  - in all cases, allow a "const auto" statement to force an "auto"
 declaration to be "const". This would work even if the right hand
 expression is not "const":
 	int x = 42;
 	const auto y = x;       // y is now "const int"

I did think of doing that way, but: 1) it's too complicated to explain, too many special cases 2) Andrei pointed out to me that now the const-ness of a struct can depend on its private data types. This would suck for users, as private data is supposed to be opaque to users.
Jan 02 2008
parent =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 I did think of doing that way, but:
 
 1) it's too complicated to explain, too many special cases
 

 2) Andrei pointed out to me that now the const-ness of a struct can
 depend on its private data types. This would suck for users, as private
 data is supposed to be opaque to users.

True, I hadn't thought of that. Too bad, it would have been a pretty nifty feature. Jerome PS: Just so we're sure what I'm agreeing about here: automatic manifest constants probably can't work (unless we drop the "auto" type inference which is too useful to remove). OTOH, I still think that using "enum" for them is a poor choice. - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHe4cdd0kWM4JG3k8RAm7jAJ4rG2AOe9jOMCzRl/xlSGzBn+kczQCeJGL5 zln2XiVTbC9O5QF3wXlvIPE= =E2Ur -----END PGP SIGNATURE-----
Jan 02 2008
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 And if not, can it be made to work? (That is, can we implement dup for
 all primitive types?)

We could, but my thinking hasn't progressed that far.
Jan 02 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 Jérôme M. Berger wrote:
     Basically, "auto" should drop the tail-most "const" if the language
 allows such types to exist and keep the "const" when it can't be
 dropped, e.g:
  - "const int" => "int";
  - "const (T*)" => "(const (T))*" (if such is allowed);
  - "const (MyClass)" => "const (MyClass)" (since the tailmost type
 is actually the hidden reference type).

I really did try to figure out how to make that work, but it just doesn't. Let's say we make a struct: struct MyInt { int x; ... and appropriate operator overloading ... } to make our own custom implementation of ints. What do we do with: const MyInt m = 3; auto n = m; Does n get const or not? If it is, now it is behaving differently from other types, and so is not a plug-in replacement. If it does not, what happens if MyInt has a pointer member? Suddenly, the const gets stripped from the pointer, and there is no const-correctness.

That is a valid problem. But it is a problem with the const semantics and the type inference functionality. It is not a problem with the declaration of manifest constants, so please don't introduce a problem in one place (making 'enum' not mean "enumeration" anymore, among other things) to fix a problem that has its roots in another place ('auto')... :X -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 05 2008
prev sibling parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Jérôme M. Berger wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

"int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

Except that this is a different issue, on which I rambled at some length here: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=64262 Basically, "auto" should drop the tail-most "const" if the language allows such types to exist and keep the "const" when it can't be dropped, e.g: - "const int" => "int"; - "const (T*)" => "(const (T))*" (if such is allowed); - "const (MyClass)" => "const (MyClass)" (since the tailmost type is actually the hidden reference type). This should be done whether the right-hand expression is a manifest constant, a "const" variable or an "invariant" variable. Jerome - --

You mean head-most const (or just head const), not tail-most const. -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 05 2008
prev sibling next sibling parent reply Frank Benoit <keinfarbton googlemail.com> writes:
Walter Bright schrieb:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

Why is it neccessary to transfer the const for value types? The assignment always makes a copy, so manipulation is OK?
Dec 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Frank Benoit wrote:
 Why is it neccessary to transfer the const for value types? The
 assignment always makes a copy, so manipulation is OK?

Very good question. I tried to answer it in my reply to Jerome.
Dec 29 2007
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sat, 29 Dec 2007 14:43:28 -0800, Walter Bright wrote:

 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

I think I'm confused here. Are you saying Walter, that in this example, 'i' would NOT be const? My simple reasoning goes like this ... const int X = 3; declares that X is a 'const int' data type. auto i = X; declares that 'i' automatically takes on the data type of 'X', thus 'i' is also a 'const int' and its initial (and only) value is the same as in 'X'. i = 4; should be an error because i is immutable. To have 'auto' strip off const should be an explicit thing. This would enable us to have a better definition of APIs too, which is something you want. mutable auto i = X; // by way of example -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Sat, 29 Dec 2007 14:43:28 -0800, Walter Bright wrote:
 
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

"int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

I think I'm confused here. Are you saying Walter, that in this example, 'i' would NOT be const?

No, I'm saying it would be const(int), which is why we need a way to type a constant as just plain int.
 My simple reasoning goes like this ...
 
  const int X = 3;
 
 declares that X is a 'const int' data type.
 
  auto i = X;
 
 declares that 'i' automatically takes on the data type of 'X', thus 'i' is
 also a 'const int' and its initial (and only) value is the same as in 'X'.
 
   i = 4;
 
 should be an error because i is immutable.

Exactly.
Dec 29 2007
prev sibling next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

OTOH, having something that's a constant but has type "int" instead of "const (int)" or "invariant (int)" could cause problems with generic programming. For example with something like this: - -------------------->8==================== static if (is (typeof (x) : int) x = 42; else static if ((is (typeof (x) : const (int)) || (is (typeof (x) : invariant (int))) processImmutableInts(); else static assert (0, "Can only handle ints"); ====================8<-------------------- If "x" is in fact a manifest constant, this code will think it is mutable and try to assign to it but the compiler will then refuse the assignment... Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHeMFHd0kWM4JG3k8RAj9IAKC+m/8C7+QjF3Kza567mGnaGCC15ACfRp4q 1TCS8yIcZuMG9nBgNfnIDaw= =wZ6N -----END PGP SIGNATURE-----
Dec 31 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jérôme M. Berger wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

"int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

OTOH, having something that's a constant but has type "int" instead of "const (int)" or "invariant (int)" could cause problems with generic programming. For example with something like this: - -------------------->8==================== static if (is (typeof (x) : int) x = 42; else static if ((is (typeof (x) : const (int)) || (is (typeof (x) : invariant (int))) processImmutableInts(); else static assert (0, "Can only handle ints"); ====================8<-------------------- If "x" is in fact a manifest constant, this code will think it is mutable and try to assign to it but the compiler will then refuse the assignment...

Thanks for mentioning that. That's what I was thinking too. Walter said something about how literals are plain types, and it works fine because you can't assign to them since they aren't lvalues. But it seems like for generic programming it would be more useful if literals did get typed as const. Unless there's some other obvious way to test for lvalueness. --bb
Dec 31 2007
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Jérôme M. Berger wrote:
 -----BEGIN PGP SIGNED MESSAGE-----
 Hash: SHA1
 
 Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

"int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

OTOH, having something that's a constant but has type "int" instead of "const (int)" or "invariant (int)" could cause problems with generic programming. For example with something like this: - -------------------->8==================== static if (is (typeof (x) : int) x = 42; else static if ((is (typeof (x) : const (int)) || (is (typeof (x) : invariant (int))) processImmutableInts(); else static assert (0, "Can only handle ints"); ====================8<--------------------

You basically want this to fail: --- enum int foo = 5; template Something (alias arg) { // ... } Something!(foo); --- Since it's the equivalent of: --- template Something (alias arg) { // ... } Something!(5); --- And that would be a substitution failure, hopefully with a special error message telling you it's a compile-time constant so you can't use alias with it. The workaround being, assign it and then pass it. I don't know of a situation where it would be advantageous to use an alias template parameter where a compile-time constant would make sense.
Dec 31 2007
prev sibling parent reply Russell Lewis <webmaster villagersonline.com> writes:
Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Yes, that could be done, but we're still stymied by the problem that we
 are unable to declare a constant of type 'int', only 'const(int)'.

I don't see any situation in which we would need a constant of type "int" instead of "const (int)" or "invariant (int)". After all, if it is a *constant*, it should be either "const" or "invariant", no?

Consider the following: const int X = 3; auto i = X; i = 4; // error, i is const Essentially, it would make type inference far less useful.

I'll ignore for the moment the (very tricky) problem of assigning copies of const structs. Just for basic types, we could easily solve this by requiring the syntax "const auto" when we wanted a constant: const int X = 3; auto i = X; const auto j = X; i = 4; // legal, typeof(i) is int j = 4; // error, j is const Doesn't it make sense that type deduction (which deals with how the variable was declared in the *past*) should be orthogonal from constness (which deals with how the variable will be used in the *future*)?
Jan 02 2008
parent reply Russell Lewis <webmaster villagersonline.com> writes:
Janice Caron wrote:
 On 1/2/08, Russell Lewis <webmaster villagersonline.com> wrote:
 I'll ignore for the moment the (very tricky) problem of assigning copies
 of const structs.  Just for basic types, we could easily solve this by
 requiring the syntax "const auto" when we wanted a constant:

      const int X = 3;
            auto i = X;
      const auto j = X;
      i = 4; // legal, typeof(i) is int

But typeof(i) /isn't/ int. typeof(i) is const(int). The problem is not how to make a const copy of a const primitive - it's how to make a mutable copy of a const primitive.

I was suggesting how things *should* be, not how they are. Sorry if I didn't make that clear.
Jan 02 2008
parent Russell Lewis <webmaster villagersonline.com> writes:
Russell Lewis wrote:
 Janice Caron wrote:
 On 1/2/08, Russell Lewis <webmaster villagersonline.com> wrote:
 I'll ignore for the moment the (very tricky) problem of assigning copies
 of const structs.  Just for basic types, we could easily solve this by
 requiring the syntax "const auto" when we wanted a constant:

      const int X = 3;
            auto i = X;
      const auto j = X;
      i = 4; // legal, typeof(i) is int

But typeof(i) /isn't/ int. typeof(i) is const(int). The problem is not how to make a const copy of a const primitive - it's how to make a mutable copy of a const primitive.

I was suggesting how things *should* be, not how they are. Sorry if I didn't make that clear.

Hmmm...maybe the problem isn't with auto, after all. Maybe what I'm really suggesting is that (with primitive types, remember I'm explicitly not considering structs) when you use a const value on the right-hand side, then the expression is not const (since you are copying the value). OTOH, when you use it on the left side, it of course must be const. Sort of like saying "the variable X" is const, but the value it contains (3) is not. If you think of the variable as a box that holds a value, it makes sense. I haven't thought this one through deeply yet, so there might be a problem with it, but I thought I'd throw the ponder out.
Jan 02 2008
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On Fri, 28 Dec 2007 21:59:54 +0100, "Jérôme M. Berger" wrote:

 	Right. Replace "const" by "invariant" in my comment and the main
 point still stands: couldn't the compiler determine automatically if
 we take the address of an invariant variable and allocate memory for
 it or not based on that? This would remove the need for a special
 keyword/syntax for manifest constants: just declare them as
 "invariant" and let the compiler do the work.

In order to do that, won't the compiler need to have access to all the source code for the application? If so, how can the compiler do it when given some source code and some object code to build the application? - -- Derek Parnell Melbourne, Australia skype: derek.j.parnell -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (MingW32) - WinPT 1.2.0 iD8DBQFHdXOUB2Z2alRbg5gRAgZlAKCna1d2thleArou/ETBguEjzwCUHwCfbCAg uq1e/ExdqDzU33RGI2SDfvU= =Gi+6 -----END PGP SIGNATURE-----
Dec 28 2007
parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 28 Dec 2007 22:56:27 +0000, Janice Caron wrote:

 On 12/28/07, Derek Parnell <derek psych.ward> wrote:
 In order to do that, won't the compiler need to have access to all the
 source code for the application?

No.

Why not? If what we are talking about is having the compiler detect if some code is taking the address of a constant value, doesn't the compiler need to see the code that does that? And if that code is in an object file and not a source file, then how will the compiler find out that the address is being taken? -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 If what we are talking about is having the compiler detect if some code is
 taking the address of a constant value, doesn't the compiler need to see
 the code that does that? And if that code is in an object file and not a
 source file, then how will the compiler find out that the address is being
 taken?

What happens is the compiler emits data to the object file whenever the address of a constant is taken, regardless of what module defines the constant. Then, the linker removes the duplicates.
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, Janice Caron <caron800 googlemail.com> wrote:
 In full agreement with you, Walter, having understood all your points
 (at last), but what if we allowed:

     const MyInt m = 3;
     auto n = m.dup;

 It would be no trouble at all for MyInt to supply a dup() function
 whose return value had type MyInt.

 The only question is, will it work for plain ints?

     const int m = 3;
     auto n = m.dup;

 And if not, can it be made to work? (That is, can we implement dup for
 all primitive types?)

Actually, come to think of it, the implementation of MyInt.dup would have to be struct MyInt { int x; MyInt dup() const { return x.dup; } } so it actually wouldn't work /unless/ dup worked for primitive types.
Dec 29 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
         int x = 42;
         const auto y = x;       // y is now "const int"

The word "auto" is redundant here. int x = 42; const y = x;
 (1) It would be even nicer if the compiler could keep track of
 whether there are *mutable* pointers in the struct <snip>

I'm in favor of handing responsibility for that decision to the programmer. const T x = whatever; auto y = x.dup; Then it becomes the programmer's responsibility to implement dup for each type T. .dup is already built into arrays. It would be trivial for Walter to supply a dup() function for primative types (dup()ing a const(int) must yield an int result). For structs and classes, it becomes the programmer's problem. I /think/ this will work, and it will definitely work better than making poor "auto" do different things for each type.
Dec 30 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/29/07, Walter Bright <newshound1 digitalmars.com> wrote:
 What do we do with:

         const MyInt m = 3;
         auto n = m;

 Does n get const or not? If it is, now it is behaving differently from
 other types, and so is not a plug-in replacement. If it does not, what
 happens if MyInt has a pointer member? Suddenly, the const gets stripped
 from the pointer, and there is no const-correctness.

In full agreement with you, Walter, having understood all your points (at last), but what if we allowed: const MyInt m = 3; auto n = m.dup; It would be no trouble at all for MyInt to supply a dup() function whose return value had type MyInt. The only question is, will it work for plain ints? const int m = 3; auto n = m.dup; And if not, can it be made to work? (That is, can we implement dup for all primitive types?)
Dec 29 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 1/2/08, Russell Lewis <webmaster villagersonline.com> wrote:
 I'll ignore for the moment the (very tricky) problem of assigning copies
 of const structs.  Just for basic types, we could easily solve this by
 requiring the syntax "const auto" when we wanted a constant:

      const int X = 3;
            auto i = X;
      const auto j = X;
      i = 4; // legal, typeof(i) is int

But typeof(i) /isn't/ int. typeof(i) is const(int). The problem is not how to make a const copy of a const primitive - it's how to make a mutable copy of a const primitive. That's why Walter is introducing manifest constants: enum int x = 3; auto i = x; // typeof(i) is int const j = x; // typeof(i) is const(int) and why I suggested allowing primitive types to have a .dup property const int x = 3; auto i = x.dup; // typeof(i) is int auto j = x; // typeof(i) is const(int)
 Doesn't it make sense that type deduction (which deals with how the
 variable was declared in the *past*) should be orthogonal from constness
 (which deals with how the variable will be used in the *future*)?

Type deduction only means that auto x = y; is equivalent to typeof(y) x = y; and if typeof(y) is const(int), well then, so will x be.
Jan 02 2008
prev sibling next sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Jérôme M. Berger wrote:
 Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This is an artificial distinction: you *are* adding a fourth way to declare a constant,

Not really, just loosened up the restrictions on enum. The implementation code is actually simpler <g>.

Surely you don't want to make a language that's less clear and/or pleasant for its users, because it's slightly easier to implement!
 the only question is what syntax to use: either
 a counter-intuitive extension to enums or a new keyword (or a minor
 extension to the alias keyword as was suggested by somebody).

I found the "use an alias when declaring one constant" and "use an enum when declaring more than one constant" to be difficult to justify. It's like saying arrays with only one element should not be allowed.

But I thought from discussion that enum wasn't allowed to be used when declaring multiple manifest constants -- a rule more akin to saying that in some contexts it's legal to define arrays so long as they have exactly one element. -- James
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
James Dennett wrote:
 Walter Bright wrote:
 Not really, just loosened up the restrictions on enum. The
 implementation code is actually simpler <g>.

Surely you don't want to make a language that's less clear and/or pleasant for its users, because it's slightly easier to implement!

No, but a simple, clean implementation can be an indication of a simple, clean design. If the implementation is larded up with special case code, that's a flag that perhaps the users also see that lard.
 the only question is what syntax to use: either
 a counter-intuitive extension to enums or a new keyword (or a minor
 extension to the alias keyword as was suggested by somebody).

when declaring more than one constant" to be difficult to justify. It's like saying arrays with only one element should not be allowed.

But I thought from discussion that enum wasn't allowed to be used when declaring multiple manifest constants -- a rule more akin to saying that in some contexts it's legal to define arrays so long as they have exactly one element.

enum { X = 3, Y = 3L } defines two manifest constants.
Dec 28 2007
parent Bruno Medeiros <brunodomedeiros+spam com.gmail> writes:
Walter Bright wrote:
 James Dennett wrote:
 Walter Bright wrote:
 Not really, just loosened up the restrictions on enum. The
 implementation code is actually simpler <g>.

Surely you don't want to make a language that's less clear and/or pleasant for its users, because it's slightly easier to implement!

No, but a simple, clean implementation can be an indication of a simple, clean design. If the implementation is larded up with special case code, that's a flag that perhaps the users also see that lard.

It can indicate, but it's not a 100% guarantee of such! -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 05 2008
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Derek Parnell <derek psych.ward> wrote:
 On Fri, 28 Dec 2007 22:56:27 +0000, Janice Caron wrote:

 On 12/28/07, Derek Parnell <derek psych.ward> wrote:
 In order to do that, won't the compiler need to have access to all the
 source code for the application?

No.

Why not? If what we are talking about is having the compiler detect if some code is taking the address of a constant value, doesn't the compiler need to see the code that does that? And if that code is in an object file and not a source file, then how will the compiler find out that the address is being taken?

I was going to answer this, but Walter got there first and answered it before me. Ho hum. Still, I'll paraphrase. In file_1: const int x = 42; /could/, if we were applying this logic, compile to an object file in which zero bytes were reserved for x. Then, in file_2: import file_1; const int * px = &x; the object file would contain storage for x, in a segment all by itself. The clever part is that the /linker/, not the compiler, is able to remove duplicate segments, so if the same segment occurs in file_3.obj or file_4.obj, it will only appear in the final .exe once.
Dec 29 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 1/4/08, Oskar Linde <oskar.lindeREM ovegmail.com> wrote:
 struct T {
         const int a = 5;
         int b;
 }

 static assert(T.sizeof == int.sizeof);

I see a problem there. You said that a is template-like, and instantiated only if its address is taken. That means that T itself is template-like, because it contains a. But I don't see how it can be. That static assert cannot compile unless T.sizeof is known. T.sizeof may be different in different modules, since one module may take the address of a, and another may not. For example: // In another module import module_where_T_is_declared; T t; p = &t.a; static assert(T.sizeof != int.sizeof); I would imagine that would make life very difficult for things like struct copying.
Jan 04 2008
prev sibling next sibling parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
Walter Bright wrote:

 Jérôme M. Berger wrote:
 :(

Yeah, I figure I'll get fricasseed over that one.

And rightfully so. This is one of the worse decisions among the bad ones in the D history.
 argument is that we already have 3 ways to declare a constant, adding a
 fourth gets very difficult to justify. As opposed to a minor extension
 to enums.

Not good enough. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Dec 28 2007
parent reply John Reimer <terminal.node gmail.com> writes:
Lars Ivar Igesund wrote:
 Walter Bright wrote:
 
 Jérôme M. Berger wrote:
 :(


And rightfully so. This is one of the worse decisions among the bad ones in the D history.
 argument is that we already have 3 ways to declare a constant, adding a
 fourth gets very difficult to justify. As opposed to a minor extension
 to enums.

Not good enough.

It'll do. I'd say it's bad, but not that bad (I think "manifest" looked better, personally -- almost pays to go back in time and start adopting the ideas that worked several decades ago... if they indeed did work). Like everything in D, it's one of: (1) we eventually get used to it (2) it eventually gets rejected and deprecated by the designer(s) (3) or d gets abandoned. :-P This is only a D 2.0 feature, afterall. It doesn't touch v 1.0. I still think "foreach_reverse" was among the worst... but I guess everybody has a pet peeve. If we need to sacrifice "enum" for the sake of a better "const" design, maybe it's worth it... but this "new" design better be good. :D -JJR
Dec 28 2007
parent reply Lars Ivar Igesund <larsivar igesund.net> writes:
John Reimer wrote:

 Lars Ivar Igesund wrote:
 Walter Bright wrote:
 
 Jérôme M. Berger wrote:
 :(


And rightfully so. This is one of the worse decisions among the bad ones in the D history.
 argument is that we already have 3 ways to declare a constant, adding a
 fourth gets very difficult to justify. As opposed to a minor extension
 to enums.

Not good enough.

It'll do. I'd say it's bad, but not that bad (I think "manifest" looked better, personally -- almost pays to go back in time and start adopting the ideas that worked several decades ago... if they indeed did work). Like everything in D, it's one of: (1) we eventually get used to it (2) it eventually gets rejected and deprecated by the designer(s) (3) or d gets abandoned. :-P

Considering that none of the "established" bad decisions from 1.0 seems to be fixed (yet) in 2.0, we are currently gaining in the bad end? Even if there is a lot of nice stuff in there too. FWIW, I think enum is on the same level as foreach_reverse, although that one exposed the problem with keywording such a special case. The usecase implemented with enum is at least valid enough. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Dec 28 2007
parent John Reimer <terminal.node gmail.com> writes:
Lars Ivar Igesund wrote:

 It'll do.  I'd say it's bad, but not that bad (I think "manifest" looked
 better, personally -- almost pays to go back in time and start adopting
 the ideas that worked several decades ago... if they indeed did work).

 Like everything in D, it's one of:

 (1) we eventually get used to it
 (2) it eventually gets rejected and deprecated by the designer(s)
 (3) or d gets abandoned. :-P

Considering that none of the "established" bad decisions from 1.0 seems to be fixed (yet) in 2.0, we are currently gaining in the bad end? Even if there is a lot of nice stuff in there too. FWIW, I think enum is on the same level as foreach_reverse, although that one exposed the problem with keywording such a special case. The usecase implemented with enum is at least valid enough.

True. Can't argue with that. -JJR
Dec 28 2007
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Jérôme M. Berger wrote:
 :(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify.

Whether you use enum or manifest or some other keyword, you are already adding a fourth method, as Jerome pointed out. But regardless, it is not so much the adding of the new method as it is the shoehorning of the word enum into something it was not meant to be. Enum stands for enumeration, and is traditionally used to define a new type, which can only be one of several values. In this case, we don't want a new type. We want to declare a read-only value of an already defined type.
 As opposed to a minor extension to enums.

Um... this is not minor :) This is a complete redefinition of what enum means. -Steve
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 As opposed to a minor extension to enums.

Um... this is not minor :) This is a complete redefinition of what enum means.

enum is already used to declare arbitrary constants *that do not have an enum type* in C, C++, and D.
Dec 28 2007
next sibling parent John Reimer <terminal.node gmail.com> writes:
Walter Bright wrote:
 Steven Schveighoffer wrote:
 As opposed to a minor extension to enums.

Um... this is not minor :) This is a complete redefinition of what enum means.

enum is already used to declare arbitrary constants *that do not have an enum type* in C, C++, and D.

And, several times this argument has been rebuffed: The reason people do this in D is usually under duress because of problems using "const". It wasn't done because it was a preferred way nor because "enum" was something that clearly indicated its purpose. Laying claim to this argument as a reasonable promotion of "enum" is not the best approach to proving to the D group that "enum" was an optimal choice. Like I said, it will work. But you probably already know that it will be accepted quite begrudgingly. Just wanted to point that out again. :) -JJR
Dec 28 2007
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Steven Schveighoffer wrote:
 As opposed to a minor extension to enums.

Um... this is not minor :) This is a complete redefinition of what enum means.

enum is already used to declare arbitrary constants *that do not have an enum type* in C, C++, and D.

The implementation does not follow the semantics. An enum is still conceptually a group of related definitions that have the same type (i.e. enumeration). You are proposing to redefine this as a group of unrelated definitions that can have different types. Yes, people use enum to define constants in code, but they are usually all related, even though they don't have to be, and they are all of the same type because conceptually, *an enumeration is a collection of like-type values that can be assigned to a variable of the same type*. I like the way this is enforced today. This is going to confuse the hell out of all newcomers, and I think there are very few people who use D that actually think this is a good idea (myself included). There are much better ways to solve this problem than destroying the traditional meaning of enum. -Steve
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 This is going to confuse the hell out of all newcomers, and I think there 
 are very few people who use D that actually think this is a good idea 
 (myself included).  There are much better ways to solve this problem than 
 destroying the traditional meaning of enum.

I understand your point, but also consider that adding yet a fourth way to declare constants is not going to be illuminating for newcomers, and it makes D look like a mishmash. Furthermore, it does not destroy the meaning of enum. Already, you can do this in C, C++ and D: enum { x = 1, y = 2 } // x and y are int's we just extend it a bit so that they don't have to be all the same type: enum { x = 1, y = 2L } // x is int, y is long and allow the { } to be dropped if only one declaration is present. We also drop the requirement that enum types be only integral ones.
Dec 28 2007
next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Steven Schveighoffer wrote:
 This is going to confuse the hell out of all newcomers, and I think
 there are very few people who use D that actually think this is a good
 idea (myself included).  There are much better ways to solve this
 problem than destroying the traditional meaning of enum.

I understand your point, but also consider that adding yet a fourth way to declare constants is not going to be illuminating for newcomers, and it makes D look like a mishmash.

constants" so it is already "not illuminating for newcomers" and it already "makes D look like a mishmash". But using "enum" only aggravates the situation: newcomers will face something that they think they know ("enum") but actually don't really.
 Furthermore, it does not destroy the meaning of enum. Already, you can
 do this in C, C++ and D:
 
   enum { x = 1, y = 2 }   // x and y are int's
 

they are either alternate choices for some value or flags that may be or'ed together. No compiler enforces that, but I've never seen it used to declare wildly unrelated values (and I would consider it a poor coding practice). Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdWKbd0kWM4JG3k8RAn8vAKCcYy/hTwQWZ2mstcf2RIzWkni7SACffJzE qlVXlIS+vohfe1JK96Wj2kI= =7jNU -----END PGP SIGNATURE-----
Dec 28 2007
parent Hxal <hxal freenode.d.channel> writes:
Janice Caron Wrote:

 On 12/28/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 I understand your point, but also consider that adding yet a fourth way
 to declare constants


Actually, I think that's a fifth way. You may have forgotten about real pi() { return 3.14159265359; } Since we would hope that real x = pi; will be inlined by the compiler, it would yield the effect of a manifest constant! :)

Except you need a specific set of compiler and linker options for that effect. (-inline, -L--gc-sections) No way to tell the compiler that you want a specific function inlined and omitted from output.
Dec 28 2007
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Steven Schveighoffer wrote:
 This is going to confuse the hell out of all newcomers, and I think there 
 are very few people who use D that actually think this is a good idea 
 (myself included).  There are much better ways to solve this problem than 
 destroying the traditional meaning of enum.

I understand your point, but also consider that adding yet a fourth way to declare constants is not going to be illuminating for newcomers

This is worse. You are now changing the meaning of enum as it was in C or C++. So to a newcomer who knows what enum is, they will breeze through that section of the manual, thinking 'D has enums, cool, I already know how to use those' and be utterly confused when they come across an example like: enum { x = 1, y = "hello" }
 it makes D look like a mishmash.

That's a very subjective statement. I don't think it does. I think multiple uses for the same keyword look messier than multiple keywords that mean distinct things.
 Furthermore, it does not destroy the meaning of enum. Already, you can do 
 this in C, C++ and D:

   enum { x = 1, y = 2 }   // x and y are int's

 we just extend it a bit so that they don't have to be all the same type:

   enum { x = 1, y = 2L }  // x is int, y is long

No, it's a redefinition. Now enum does not mean enumeration, it means manifest constant. Oh and by the way, if you assign a symbol name to an enum, it becomes a traditional enum (with a type and the requirement that all the values have the same type). Having one keyword mean two different things, when used in slightly different ways is very very confusing. Consider this: enum Foo { x = 'a' } Now we remove Foo: enum { x = 'a' } Now x is a char, where Foo.x was an int? That is very confusing. Especially if you are reading a long list of constants, and can't remember whether the type was specified at the top or is specified by the rvalue. Here's an analogy: Imagine a language which did not have pointers. But some clever coder figured out how to use ints as pointers. And then the developer of the langauge says 'were going to support int as a pointer if you put a dot after it, because people already use ints as pointers anyways' The example is simple, and probably unrealistic, but it captures the "oh my god, what is he thinking" reaction that I have to this change.
 We also drop the requirement that enum types be only integral ones.

This, I agree with and support. It's the multiple types in one enum declaration that is really confusing, and the loosening of the definition that an enum is an enumeration of related values instead of a list of constants. -Steve
Dec 28 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 Consider this:
 
 enum Foo { x = 'a' }
 
 Now we remove Foo:
 
 enum { x = 'a' }
 
 Now x is a char, where Foo.x was an int?  That is very confusing.

C, C++, and D already work that way. Such are in common use, and I've not heard a complaint that it is very confusing. For example, it's in std.c.windows.windows.d (among many), and there's not been a single comment on it (pro or con) in 6 years in this n.g. Extending it to allow heterogeneous types is not a big step, nor does it break any existing code or usage.
Dec 28 2007
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Steven Schveighoffer wrote:
 Consider this:

 enum Foo { x = 'a' }

 Now we remove Foo:

 enum { x = 'a' }

 Now x is a char, where Foo.x was an int?  That is very confusing.

C, C++, and D already work that way. Such are in common use, and I've not heard a complaint that it is very confusing. For example, it's in std.c.windows.windows.d (among many), and there's not been a single comment on it (pro or con) in 6 years in this n.g.

You missed the point of my example :) I'm not debating anonymous enumerations and whether they should exist or not. I'm saying that the definition of enum working like an enumeration, or working like a manifest list, based on whether the enumeration is named or anonymous, is confusing. If you look at the example, the type of the value changes from one version to the next, even though no type is specified.
 Extending it to allow heterogeneous types is not a big step, nor does it 
 break any existing code or usage.

Just because you can do something that changes the meaning of a keyword, especially to a meaning that is not described well by the english meaning of the word, and still have existing code compile, doesn't mean you should. Having heterogeneous types in the same enum braces is a big step, because it fundamentally says 'enum is not an enumeration'. -Steve
Dec 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 You missed the point of my example :)  I'm not debating anonymous 
 enumerations and whether they should exist or not.  I'm saying that the 
 definition of enum working like an enumeration, or working like a manifest 
 list, based on whether the enumeration is named or anonymous, is confusing. 
 If you look at the example, the type of the value changes from one version 
 to the next, even though no type is specified.

That's a good point.
 Extending it to allow heterogeneous types is not a big step, nor does it 
 break any existing code or usage.

Just because you can do something that changes the meaning of a keyword, especially to a meaning that is not described well by the english meaning of the word, and still have existing code compile, doesn't mean you should.

It doesn't mean you shouldn't, either. This goes on all the time in programming languages. After all, ! doesn't mean exclamation. The reason we even have to invent programming languages is because english is too imprecise and ambiguous. If anyone tries to learn programming by using Webster's, they're in for some pretty tough sledding :-) That said, I would still shrink from using an utterly contradictory meaning, like having the keyword "and" actually do an "or", but there isn't that problem here.
 Having heterogeneous types in the same enum braces is a big step, because it 
 fundamentally says 'enum is not an enumeration'.

I don't see any fundamental reason why an enumeration's contents must all be the same type. You could convincingly argue that they all must be somehow related to each other, but that doesn't require they be related by type. Grouping semantically related ones together would be the purview of the programmer.
Dec 28 2007
prev sibling parent reply James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Steven Schveighoffer wrote:
 Consider this:

 enum Foo { x = 'a' }

 Now we remove Foo:

 enum { x = 'a' }

 Now x is a char, where Foo.x was an int?  That is very confusing.

C, C++, and D already work that way.

No, they don't. In C, x has integral type, whether the enum is named or not. In C++, x is *not* an int, but rather its type is an enum type (named or not). Naming the enum type does not affect the type of x in C or C++. (The fact that C says that 'a' is an int and C++ says that it is a char is a separate issue.)
 Such are in common use, and I've
 not heard a complaint that it is very confusing. 

It apparently confuses D's designer -- what chance do us mere mortals have?! -- James
Dec 28 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
James Dennett wrote:
 No, they don't.

You're right, I should have gone back and checked the spec.
Dec 28 2007
prev sibling parent Derek Parnell <derek psych.ward> writes:
On Fri, 28 Dec 2007 12:26:53 -0800, Walter Bright wrote:

 Steven Schveighoffer wrote:
 This is going to confuse the hell out of all newcomers, and I think there 
 are very few people who use D that actually think this is a good idea 
 (myself included).  There are much better ways to solve this problem than 
 destroying the traditional meaning of enum.

I understand your point, but also consider that adding yet a fourth way to declare constants is not going to be illuminating for newcomers, and it makes D look like a mishmash. Furthermore, it does not destroy the meaning of enum. Already, you can do this in C, C++ and D: enum { x = 1, y = 2 } // x and y are int's we just extend it a bit so that they don't have to be all the same type: enum { x = 1, y = 2L } // x is int, y is long and allow the { } to be dropped if only one declaration is present. We also drop the requirement that enum types be only integral ones.

So in short, 'enum' no longer stands for 'enumeration'. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 28 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 struct S { char[] str};

 const S x = S("hello".dup);
 auto y = x;

 If y is not const, then y.str is not const, yet it points to the same data
 as x.

Yeah, you're right. But doesn't the same problem occur with enum { S x = S("hello".dup); } auto y = x; ?
Dec 28 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 12/28/07, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 struct S { char[] str};

 const S x = S("hello".dup);
 auto y = x;

 If y is not const, then y.str is not const, yet it points to the same 
 data
 as x.

Yeah, you're right. But doesn't the same problem occur with enum { S x = S("hello".dup); } auto y = x; ?

Is that valid? I didn't think dup could be a compile-time constant because it uses the heap... -Steve
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Derek Parnell <derek psych.ward> wrote:
 In order to do that, won't the compiler need to have access to all the
 source code for the application?

No. (So the rest of your question, "If so..." doesn't apply) That said, Walter has already explained that this idea presents difficulties pertaining to tail-constness, and so can't be done. Just - not for the reason that you're implying.
Dec 28 2007
prev sibling parent reply Don Clugston <dac nospam.com.au> writes:
Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This statement really disturbs me, as this is clearly a major change to an enum. I think the consequences are quite horrible. The primary function of enum is to create a TYPE based on a GROUP of related INTEGRAL constants. You can abuse the facility to declare integral constants, but that's a secondary feature at best. The problem is that using enum for abtritrary types is a very poor match for the primary feature of enums. We don't want to create a type; we don't want a grouping; and the values are not integral. enum : int { A=2, B, C } Having an enum automatically get the 'next' value is one of the key feature of enums, and it relies on the base type being an enumerable type. char [], structs, and floating-point types don't have that behaviour. You're guaranteed that integral types remain as a special case. In another post, you mentioned that this would become allowed, to reduce the effect of the special case: enum : float { A=2, B, C } Which leads to the same nonsense you get with operator ++ on floats; a++ is not necessarily different to a. I would hope that this feature never actually gets used. Does this compile? enum : cfloat { A=2, B, C } The important point is that you are now trying to minimise the effect of the special case which has been created. But there's no way to get rid of it, because it is fundamental to the nature of enums. AFAICT there's also special cases related to the grouping (named vs unnamed enums) and with regard to the typing (name mangling).
Dec 28 2007
next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Don Clugston wrote:
 Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This statement really disturbs me, as this is clearly a major change to an enum.

It doesn't change existing usage of enum.
 I think the consequences are quite horrible.
 
 The primary function of enum is to create a TYPE based on a GROUP of 
 related INTEGRAL constants.

None of the enhancements impair this.
 You can abuse the facility to declare 
 integral constants, but that's a secondary feature at best. The problem 
 is that using enum for abtritrary types is a very poor match for the 
 primary feature of enums.
 
 We don't want to create a type; we don't want a grouping; and the values 
 are not integral.
 
 enum : int { A=2, B, C }
 
 Having an enum automatically get the 'next' value is one of the key 
 feature of enums, and it relies on the base type being an enumerable 
 type.

The only thing it relies on is the ability to add 1 to the previous value. The new enums can automatically get the next value for any type with this characteristic, including UDT's that overload opAdd, as long as they can be evaluated at compile time.
 char [], structs, and floating-point types don't have that 
 behaviour.

You're right about char[], but structs can have that behavior, and fp types do. This means that: enum Color : string { Red="red", Blue } would not compile, whereas: enum Color : string { Red="red", Blue="blue" } would.
 You're guaranteed that integral types remain as a special case.
 
 In another post, you mentioned that this would become allowed, to reduce 
 the effect of the special case:
 enum : float { A=2, B, C }

The above example would result in A being 2.0f, B being 3.0f, and C being 4.0f.
 Which leads to the same nonsense you get with operator ++ on floats; a++ 
 is not necessarily different to a. I would hope that this feature never 
 actually gets used.

You're right that, for nans, infinity, and very small values, (a+1)==a. But this is an inescapable of reality with fp arithmetic, and we don't disallow fp arithmetic because of it. We could, though, add a check that if the 'next' value doesn't change after being incremented, an error message is produced. There already is an error generated if the value being incremented is equal to the underlying type's .max, which prevents unintended overflows.
 Does this compile?
 
 enum : cfloat { A=2, B, C }

Yes. A would be a cfloat with value 2.0+0i, B would be 3.0+0i, C would be 4.0+0i. There isn't a special case, all it does is: (next value)=(previousvalue) + 1 and run it through the usual semantic analysis. I don't know of a cause where one would want to declare complex constants this way, but the aim here is consistency.
 The important point is that you are now trying to minimise the effect of 
 the special case which has been created. But there's no way to get rid 
 of it, because it is fundamental to the nature of enums.

I don't understand exactly what the created special case is you're referring to. The enhanced enums remove the special case that restricted enums to being integral types.
 AFAICT there's also special cases related to the grouping (named vs 
 unnamed enums) and with regard to the typing (name mangling).

Anonymous enums don't have a type (and didn't in D1.0, either). There's no way to get a grip on such a type anyway, as it has no name and: enum { FOO, BAR } x; style declarations are not allowed in D. Let me enumerate (!) the enhancements to enum: 1) The enum 'base type' is no longer restricted to being integral types only. 2) Members of anonymous enums can now be of heterogeneous types, the types being deduced from their initializers. 3) .init, .min and .max have no meaning for anonymous enums, and so are computed only for tagged enums. 4) For anonymous enum members, a type can prefix the identifier as a convenience. 5) If there is only one member in an anonymous enum, the { } can be omitted. 6) If .init, .min, .max or 'next' values are not required, then the base type doesn't have to support the operations required to produce those values. None of these are takeaways.
Dec 29 2007
next sibling parent reply BLS <nanali nospam-wanadoo.fr> writes:
Why not keeping enum as it was and use manfifest as enhanced enum ?
Seems to be a clean solution.
Bjoern
Dec 29 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BLS wrote:
 Why not keeping enum as it was and use manfifest as enhanced enum ?
 Seems to be a clean solution.

Because, then there's a fourth way to declare constants. 3 seems to be enough.
Dec 29 2007
next sibling parent =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 BLS wrote:
 Why not keeping enum as it was and use manfifest as enhanced enum ?
 Seems to be a clean solution.

Because, then there's a fourth way to declare constants. 3 seems to be enough.

You are already adding a fourth way to declare constants. In fact, I believe the "enum hack" way of declaring constants should be deprecated and only kept around for backward compatibility and new code should use "manifest" (or whatever new keyword). Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdmFud0kWM4JG3k8RAihuAKCwAbFRPUSDCEOdi9r+GoO0BtESZQCgjH3B sfSgIzCAQEYCQvCfkw5tzG4= =4XdK -----END PGP SIGNATURE-----
Dec 29 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
John Reimer wrote:
 The manifest constant is really quite different from the other semantics 
 of constant;

The previous problems we were having with the design of the const system all seemed to revolve, one way or another, around trying to accommodate two different kinds of constants with one scheme. Separating the two, i.e. the notion of "manifest constant" from "constant type", seems to finally allow things to work.
 and I think that it deserves to be set apart from the rest 
 as a completely different entity (afterall this used to be achieved by 
 something like #define's in C/C++).

Yes.
 But then, I'm not sure if you have 
 set goals on how many keywords you're allowed to add per year.  If you 
 have some sort of limit, I can see how you necessarily must show 
 restraint. :)

As I remarked elsewhere, there is no language design problem that cannot be resolved by adding more keywords. Just like with aircraft design, there is no problem that cannot be fixed by adding more thrust <g>. D already has quite a lot of keywords. Trying to stem the flood if possible is a reasonable goal.
 My question is: at what point do D keywords reach critical mass (in terms 
 of keyword hijacking for new functionality)?  This seems to happen 
 repeatedly as D struggles to avoid keyword additions... at what /appears/ 
 to be the expense of the programmer.

As I suggested, all the enum enhancements do is remove restrictions placed on its use. I don't see how that is costing the programmer.
 This new manifest enum could work and eventually people might just get 
 used to it... but it's so strange, so foreign, and so seemingly 
 inconsistant that I think your betting heavily on the good-graces of your 
 d community (who likely will forgive you and move on).

I think it'll seem strange for about 5 minutes, and then will seem normal. After all, that's what happened with the !( ) syntax for templates rather than < >.
 But I do wonder if 
 this is the case for all those users that are still deciding whether to 
 adopt D or not.  D 2.0 is an indicator of what is to come... so decisions 
 made here are going to speak volumes about the future.
 
 I don't know how newcomers would react or what confusion it would cause 
 novices, so I won't use that as argument against it.  But it's a gamble 
 and a seemingly very risky gamble.  Some would say high-risk gambles 
 don't make sense, especially when the payback is nominal. 

I would argue it is less confusing than introducing yet another keyword, especially a keyword whose usage overlaps 3 other keywords, but only time will tell.
Dec 29 2007
parent reply John Reimer <terminal.node gmail.com> writes:
Walter Bright wrote:
 But then, I'm not sure if you have set goals on how many keywords 
 you're allowed to add per year.  If you have some sort of limit, I can 
 see how you necessarily must show restraint. :)

As I remarked elsewhere, there is no language design problem that cannot be resolved by adding more keywords. Just like with aircraft design, there is no problem that cannot be fixed by adding more thrust <g>.

LOL! That's a good analogy... :) Although, there's probably an equally astute analogy pointing out the the shortcomings of re-using keywords. ;) But I still like what you say here.
 D already has quite a lot of keywords. Trying to stem the flood if 
 possible is a reasonable goal.
 

Agreed.
 My question is: at what point do D keywords reach critical mass (in 
 terms of keyword hijacking for new functionality)?  This seems to 
 happen repeatedly as D struggles to avoid keyword additions... at what 
 /appears/ to be the expense of the programmer.

As I suggested, all the enum enhancements do is remove restrictions placed on its use. I don't see how that is costing the programmer.

I guess that's to be found out.
 This new manifest enum could work and eventually people might just get 
 used to it... but it's so strange, so foreign, and so seemingly 
 inconsistant that I think your betting heavily on the good-graces of 
 your d community (who likely will forgive you and move on).

I think it'll seem strange for about 5 minutes, and then will seem normal. After all, that's what happened with the !( ) syntax for templates rather than < >.

I hate to be a pushover on this one <g>, but I'll just accept this enum thing and hope for the best.
 But I do wonder if this is the case for all those users that are still 
 deciding whether to adopt D or not.  D 2.0 is an indicator of what is 
 to come... so decisions made here are going to speak volumes about the 
 future.

 I don't know how newcomers would react or what confusion it would 
 cause novices, so I won't use that as argument against it.  But it's a 
 gamble and a seemingly very risky gamble.  Some would say high-risk 
 gambles don't make sense, especially when the payback is nominal. 

I would argue it is less confusing than introducing yet another keyword, especially a keyword whose usage overlaps 3 other keywords, but only time will tell.

I think this is the whole source of the disagreement. A number of us were arguing that we think this is more confusing... but I think we'll survive. -JJR
Dec 29 2007
parent Lars Ivar Igesund <larsivar igesund.net> writes:
John Reimer wrote:
 
 I think this is the whole source of the disagreement.  A number of us
 were arguing that we think this is more confusing... but I think we'll
 survive.

Of course we will, but it is yet another feature that will build annoyance (no, I doubt it will go over after 5 minutes), and with a young language that you want to have a broader exposure, it is very unnecessary to choose the annoying (bad) solution. -- Lars Ivar Igesund blog at http://larsivi.net DSource, #d.tango & #D: larsivi Dancing the Tango
Dec 30 2007
prev sibling parent BLS <nanali nospam-wanadoo.fr> writes:
Walter Bright schrieb:
 BLS wrote:
 Why not keeping enum as it was and use manfifest as enhanced enum ?
 Seems to be a clean solution.

Because, then there's a fourth way to declare constants. 3 seems to be enough.

In case that you can use a keyword in 4 different ways this will increase the change for wrong use to exactly 300 percent ;-) I agree with Bill B;, who mentioned in another thread that the number of keywords is not nessesarly an indicator for a complex language. Bjoern
Dec 30 2007
prev sibling next sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 29 Dec 2007 08:59:46 -0000, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Let me enumerate (!) the enhancements to enum:

 1) The enum 'base type' is no longer restricted to being integral type=

 only.
 2) Members of anonymous enums can now be of heterogeneous types, the  =

 types being deduced from their initializers.
 3) .init, .min and .max have no meaning for anonymous enums, and so ar=

 computed only for tagged enums.
 4) For anonymous enum members, a type can prefix the identifier as a  =

 convenience.
 5) If there is only one member in an anonymous enum, the { } can be  =

 omitted.
 6) If .init, .min, .max or 'next' values are not required, then the ba=

 type doesn't have to support the operations required to produce those =

 values.

 None of these are takeaways.

Personally I always name my types but there may be those that don't. Is this currently illegal then? class Colour { private: // private helper type defining colour state variable // using an anonymous enum. enum { red, green, blue } colour; };
 Don Clugston wrote:
  Having an enum automatically get the 'next' value is one of the key =


 feature of enums, and it relies on the base type being an enumerable =


 type.

The only thing it relies on is the ability to add 1 to the previous =

 value. The new enums can automatically get the next value for any type=

 with this characteristic, including UDT's that overload opAdd, as long=

 as they can be evaluated at compile time.

opAdd(int) seems unnatural for user defined types. They would have to = ignore the argument and it would lead to some odd bugs and confusions. Very contrived and poorly chosen example: class Foo { public: // helper type enum FooType { A =3D "foo", B =3D "bar", C =3D "snafu" } private: // state - bar may be one o string Bar; public: Foo() { Bar =3D A; } // only used to allow creation of Foo based enums. Foo opAdd(int) { Bar++; } } enum FooBar: Foo { A =3D Foo("foo"), B =3D Foo("bar"), C =3D Foo("snafu") } FooBar a =3D FooBar.A; FooBar b =3D FooBar.B; FooBar c =3D FooBar.C; assert(b =3D=3D (a+1)); // okay assert(c =3D=3D (b+1)); // okay assert(b =3D=3D (a+2)); // surprising!
Dec 29 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Bruce Adams wrote:
 Personally I always name my types but there may be those that don't.
 Is this currently illegal then?
 
 class Colour
 {
 private:
   // private helper type defining colour state variable
   // using an anonymous enum.
   enum { red, green, blue } colour;
 };

Yes, it is illegal.
 Why opAdd and not opIncrement?

opIncrement is redundant, as it's a subset of opAdd.
 opAdd(int) seems unnatural for user defined types. They would have to 
 ignore the
 argument and it would lead to some odd bugs and confusions.

I don't know why it would be unnatural. To me, a type that can be incremented but not added would seem very strange indeed.
 Very contrived and poorly chosen example:
 
 class Foo
 {
 public:
   // helper type
   enum FooType
   {
     A = "foo",
     B = "bar",
     C = "snafu"
   }
 private:
   // state - bar may be one o
   string Bar;
 
 public:
   Foo()
   {
     Bar = A;
   }
 
   // only used to allow creation of Foo based enums.
   Foo opAdd(int)
   {
     Bar++;

A string cannot be incremented.
   }
 }
 
 enum FooBar: Foo
 {
    A = Foo("foo"),
    B = Foo("bar"),
    C = Foo("snafu")
 }
 
 FooBar a = FooBar.A;
 FooBar b = FooBar.B;
 FooBar c = FooBar.C;
 
 assert(b == (a+1)); // okay
 assert(c == (b+1)); // okay
 assert(b == (a+2)); // surprising!
 

Dec 29 2007
next sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sun, 30 Dec 2007 01:47:03 -0000, Walter Bright  =

<newshound1 digitalmars.com> wrote:

 Bruce Adams wrote:
 Personally I always name my types but there may be those that don't.
 Is this currently illegal then?
  class Colour
 {
 private:
   // private helper type defining colour state variable
   // using an anonymous enum.
   enum { red, green, blue } colour;
 };

Yes, it is illegal.

 Why opAdd and not opIncrement?

opIncrement is redundant, as it's a subset of opAdd.
 opAdd(int) seems unnatural for user defined types. They would have to=


 ignore the
 argument and it would lead to some odd bugs and confusions.

I don't know why it would be unnatural. To me, a type that can be =

 incremented but not added would seem very strange indeed.

= optionally predecessor) operations to cycle through the entities in a particular order but addin= g = them is unnatural. Granted you can use a sucessor operation to define addition that's the = typical way number theory is derived from set theory but we are talking about programmers who can = = make errors. enum Colour { red, green, blue, indigo } Colour c =3D red; c++; // c =3D blue Colour c2 =3D red+green; //a bad thing to allow.
 Very contrived and poorly chosen example:
  class Foo
 {
 public:
   // helper type
   enum FooType
   {
     A =3D "foo",
     B =3D "bar",
     C =3D "snafu"
   }
 private:
   // state - bar may be one o
   string Bar;
  public:
   Foo()
   {
     Bar =3D A;
   }
    // only used to allow creation of Foo based enums.
   Foo opAdd(int)
   {
     Bar++;

A string cannot be incremented.

= tempted to defined opAdd with the semantics of an increment operation if they only = = wanted this behaviour at all to allow them to define enumerations with a = different successor operation. Here is another less poor but still non-optimal example. class OddNumbers { // helper type typedef int FooType; // state FooType bar; Foo() { bar =3D 1; } // bad semantics - hacked to value OddEnum as required. Foo opAdd(int) { bar +=3D 2; } } enum OddEnum: OddNumbers { one =3D 1, three, // =3D=3D 3 five // =3D=3D 5 } This is not a brilliant example as it is sensible to have a genuine = addition operator for odd numbers but for many types you could be defining opAdd(int) for = = this purpose when int is not sensibly addable to the class itself.
Dec 29 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sun, 30 Dec 2007 03:41:40 -0000, Janice Caron <caron800 googlemail.co=
m>  =

wrote:

 On 12/30/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Surely that is what we mean with an enumeration. We allow successor (=


 optionally predecessor)
 operations to cycle through the entities in a particular order but  =


 adding
 them is unnatural.

In a true enumeration, it isn't that unreasonable to equate ++ or +1 with the successor function, and -- or -1 with the predecessor function. Given that, for an enum type E, what you want to allow is E opAdd(int) E opSub(int) but you want to disallow E opAdd(E) E opSub(E) So to use your example: Colour c2 =3D red+green; //WRONG Colour c2 =3D red+2; //OK the latter meaning the successor of the successor. Of course D enums aren't true enums, so we might end up having to allow "red+green" by accident. :-)

Which is a bad thing. I would also like the option to prevent red+2 at compile time. Its not going to be terribly helpful to pick up an out of range error if= someone foolishly adds an integer that takes the value out of the permit= ted range for the type. enumeraitons are not closed under addition. Okay fix= ed = length binary integers aren't either but they are less closed and have well = defined if often undesireable overflow semantics.
Dec 29 2007
prev sibling parent James Dennett <jdennett acm.org> writes:
Walter Bright wrote:
 Bruce Adams wrote:
 Personally I always name my types but there may be those that don't.
 Is this currently illegal then?

 class Colour
 {
 private:
   // private helper type defining colour state variable
   // using an anonymous enum.
   enum { red, green, blue } colour;
 };

Yes, it is illegal.
 Why opAdd and not opIncrement?

opIncrement is redundant, as it's a subset of opAdd.

Except if you care about efficiency, in which case you might well have types which admit an efficient (constant time) increment but not a constant-time addition in general. (Contiguous enumerated types should not have that issue, however, nor should any type which has an underlying integral representation.) -- James
Dec 29 2007
prev sibling next sibling parent reply =?ISO-8859-1?Q?=22J=E9r=F4me_M=2E_Berger=22?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Walter Bright wrote:
 Don Clugston wrote:
 Walter Bright wrote:
 Jérôme M. Berger wrote:
     :(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

This statement really disturbs me, as this is clearly a major change to an enum.

It doesn't change existing usage of enum.

However, it confuses things for the programmer. Enums used to have a clearly delimited function: creating a type based on a group of related items (the fact that it is implemented as an integral type should have remained an implementation detail IMO). The fact that enums may be used (or abused) otherwise is more a misfeature which allowed the programmer to work around some language limitations. I'm all in favor of removing those limitations and I understand keeping the old enum behavior around for compatibility reasons, but extending the "enum hack" is not the way to do it. In other words: some current usages of enum are a hack to work around some language limitations. Those usages should be deprecated in favor of another keyword that would allow them plus extra functionality (e.g. manifest constants) while "enum" is relegated to its proper usage. I believe anything else would be too confusing (particularly to newcomers). Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdmBVd0kWM4JG3k8RAlzPAJ0QKHazsbtH3oheKMnKIccvjBAihACfUDhQ qQK9A0r3FjLE4WYp6mR9CJc= =sXTi -----END PGP SIGNATURE-----
Dec 29 2007
parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
Janice Caron wrote:
 On 12/29/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 Those usages should be deprecated
 in favor of another keyword that would allow them plus extra
 functionality (e.g. manifest constants) while "enum" is relegated to
 its proper usage. I believe anything else would be too confusing
 (particularly to newcomers).


Yes, please!
 It would be /really/ cool if Walter just implemented everything as he
 suggested, except for deprecating the word "enum" and replacing it
 with "manifest" everywhere.

"manifest", if not "define" (my preference) or "pure".
 Then "enum" could be redeployed for true enumerations only. e.g.
 
     enum Primary { red, blue, green }
     Primary col = red;
     int x = col; /* ERROR */
     int x = cast(int)col /* STILL AN ERROR */
     col = 1; /* ALSO AN ERROR */
     col = cast(Primary)1; /* NOPE! */
 
     Primary col2 = col++; /* successor */
     assert(col2 = Primary.blue);
     if (col == col2) { /*...*/ }
     if (col < col2) { /*...*/ }
 
     enum { x = 2 }; /* ERROR */
     enum E { x = 2 }; /* ERROR */
     enum E : int { x }; /* ERROR */
 
 ...but it will never happen! (Sigh!)

Why not? It will break existing code, but given the length of these threads, D users may be prepared to cope with it where it does... Bastiaan.
Dec 29 2007
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Walter Bright" wrote
 Don Clugston wrote:
 You can abuse the facility to declare integral constants, but that's a 
 secondary feature at best. The problem is that using enum for abtritrary 
 types is a very poor match for the primary feature of enums.

 We don't want to create a type; we don't want a grouping; and the values 
 are not integral.

 enum : int { A=2, B, C }

 Having an enum automatically get the 'next' value is one of the key 
 feature of enums, and it relies on the base type being an enumerable 
 type.

The only thing it relies on is the ability to add 1 to the previous value. The new enums can automatically get the next value for any type with this characteristic, including UDT's that overload opAdd, as long as they can be evaluated at compile time.

Correct me if I'm wrong, but isn't opAdd disallowed at compile time because it uses a this pointer? Or are you changing that behavior for this feature? -Steve
Dec 29 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Steven Schveighoffer wrote:
 Correct me if I'm wrong, but isn't opAdd disallowed at compile time because 
 it uses a this pointer?  Or are you changing that behavior for this feature?

You're quite right. But I do intend to eventually extend CTFE to get this to work. I think CTFE is only just starting to show its potential.
Dec 29 2007
prev sibling next sibling parent reply Bastiaan Veelo <Bastiaan Veelo.net> writes:
Walter Bright wrote:
 Let me enumerate (!) the enhancements to enum:
 
 1) The enum 'base type' is no longer restricted to being integral types 
 only.
 2) Members of anonymous enums can now be of heterogeneous types, the 
 types being deduced from their initializers.
 3) .init, .min and .max have no meaning for anonymous enums, and so are 
 computed only for tagged enums.
 4) For anonymous enum members, a type can prefix the identifier as a 
 convenience.
 5) If there is only one member in an anonymous enum, the { } can be 
 omitted.
 6) If .init, .min, .max or 'next' values are not required, then the base 
 type doesn't have to support the operations required to produce those 
 values.

You can regard me as a newcomer. In my view, enums are often used in support of switch statements. If you are loosening up enums so its members can be of heterogeneous types, will you be loosening up switch/case as well? If not, that would be confusing to me, as you will be able to switch on some enums but not on others. Sincerely, Bastiaan.
Dec 30 2007
next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Sun, 30 Dec 2007 11:27:17 +0000, Janice Caron wrote:

 On 12/30/07, Bastiaan Veelo <Bastiaan veelo.net> wrote:
 If not, that would be confusing to me, as you will
 be able to switch on some enums but not on others.

You can't switch on an anonymous enum. Anonymous enums have no type, and therefore it is not even /possible/ to generate an expression whose type is that of an anonymous enum! So switch/case survives unharmed.

And yet this compiles and runs ... // ------------- import std.stdio; enum { one = 1, two = 2, three = 3 } void main() { auto x = three; switch (x) { case one: writefln("ONE"); break; case two: writefln("TWO"); break; case three: writefln("THREE"); break; } } // ------------- -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 30 2007
next sibling parent 0ffh <frank youknow.what.todo.interNETz> writes:
Derek Parnell wrote:
 On Sun, 30 Dec 2007 11:27:17 +0000, Janice Caron wrote:
 
 On 12/30/07, Bastiaan Veelo <Bastiaan veelo.net> wrote:
 If not, that would be confusing to me, as you will
 be able to switch on some enums but not on others.

and therefore it is not even /possible/ to generate an expression whose type is that of an anonymous enum! So switch/case survives unharmed.

And yet this compiles and runs ... [source: switch with anon enum]

And I'd have been surprised if not... if it was called "manifest", would anyone at all get the idea that you can't case against them, because they are something oh so entirely different from a literal number? Ooops, wait... wasn't it supposed to be just like one? =) regards, frank --- Making a statement bold and confident doesn't make it true.
Dec 30 2007
prev sibling next sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
Derek Parnell wrote:
 On Sun, 30 Dec 2007 11:27:17 +0000, Janice Caron wrote:
 
 On 12/30/07, Bastiaan Veelo <Bastiaan veelo.net> wrote:
 If not, that would be confusing to me, as you will
 be able to switch on some enums but not on others.

You can't switch on an anonymous enum. Anonymous enums have no type, and therefore it is not even /possible/ to generate an expression whose type is that of an anonymous enum! So switch/case survives unharmed.

And yet this compiles and runs ... // ------------- import std.stdio; enum { one = 1, two = 2, three = 3 } void main() { auto x = three; switch (x) { case one: writefln("ONE"); break; case two: writefln("TWO"); break; case three: writefln("THREE"); break; } } // -------------

Yes, and although the extended enum would allow three to be defined as 3f, then case three would not compile according to the current spec. That is obvious in this example, but it is a confusing inconsistency in the relation between enum and switch. enum and switch have always been compatible in my mind, but that is about to change... Bastaan.
Dec 30 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Derek Parnell wrote:
 On Sun, 30 Dec 2007 11:27:17 +0000, Janice Caron wrote:
 
 On 12/30/07, Bastiaan Veelo <Bastiaan veelo.net> wrote:
 If not, that would be confusing to me, as you will
 be able to switch on some enums but not on others.

and therefore it is not even /possible/ to generate an expression whose type is that of an anonymous enum! So switch/case survives unharmed.

And yet this compiles and runs ... // ------------- import std.stdio; enum { one = 1, two = 2, three = 3 } void main() { auto x = three; switch (x) { case one: writefln("ONE"); break; case two: writefln("TWO"); break; case three: writefln("THREE"); break; } }

Yes, and x is of type int, not anonymous enum.
Dec 30 2007
prev sibling next sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
Janice Caron wrote:
 What I meant was: you can't switch on a heterogenous anonymous enum,
 because heterogenous anonymous enums have no type.

with enum { int one = 1; float two = 2.0; string three = "three"; } I'd expect void foo(int x) { switch (x) { case one: writefln("ONE"); break; // this should be legal case two: writefln("TWO"); break; // but not this case three: writefln("THREE"); break; // much less this } } because to me the rule "case guards can only be of integral type" seems to make the most sense. I'd be rather disappointed if case one didn't work... regards, frank
Dec 30 2007
next sibling parent 0ffh <frank youknow.what.todo.interNETz> writes:
0ffh wrote:
   enum
   {
     int    one   = 1;
     float  two   = 2.0;
     string three = "three";
   }

Ooops, I suppose there should be commas here not semicolons... regards, frank
Dec 30 2007
prev sibling next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
0ffh wrote:
 because to me the rule "case guards can only be of integral type"
 seems to make the most sense. I'd be rather disappointed if case
 one didn't work...

Actually, D also allows string switch. However, I'm pretty sure you can't mix int "switch" with string "case"s :P. Spec reference: http://www.digitalmars.com/d/1.0/statement.html#SwitchStatement --- /Expression/ is evaluated. The result type T must be of integral type or char[], wchar[] or dchar[]. The result is compared against each of the case expressions. If there is a match, the corresponding case statement is transferred to. ---
Dec 30 2007
parent 0ffh <frank youknow.what.todo.interNETz> writes:
Frits van Bommel wrote:
 0ffh wrote:
 because to me the rule "case guards can only be of integral type"
 seems to make the most sense. I'd be rather disappointed if case
 one didn't work...

Actually, D also allows string switch. However, I'm pretty sure you can't mix int "switch" with string "case"s :P.

Hmmm, that's rather... cool. :) You live and learn. regards, frank
Dec 30 2007
prev sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
Janice Caron wrote:
 On 12/30/07, 0ffh <frank youknow.what.todo.internetz> wrote:
 because to me the rule "case guards can only be of integral type"
 seems to make the most sense. I'd be rather disappointed if case
 one didn't work...

This bit of conversation seems to have lost track of the original point, which, to remind all, was that Bastiaan Veelo said:
 You can regard me as a newcomer. In my view, enums are often
 used in support of switch statements. If you are loosening up
 enums so its members can be of heterogeneous types, will you
 be loosening up switch/case as well? If not, that would be
 confusing to me, as you will be able to switch on some enums
 but not on others.

So, the answer is that anonymous enums don't create new types. The type is simply the underlying type. In the case of heterogenous anonymous enums, the type of each element can be different. This is a very different kettle of fish from /named/ enums, which do, in fact, create a new type, allowing the compiler to look at code like enum Primary { red, green, blue } Primary x = whatever; switch (x) { case Primary.red: /*...*/ case Primary.green: /*...*/ } and detect that case blue is missing, and hence a default is needed. It cannot do that for anonymous enums, because it has no way to figure out what the complete set should be. At least, I /think/ that's what Bastiaan was getting at. Anyway, the point is that the new extensions make no difference to switch/case.

Thank you for reminding me of the proper way of switching on enums. And you are right of course, no difference here. Bastiaan.
Dec 30 2007
prev sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Bastiaan Veelo wrote:
 You can regard me as a newcomer. In my view, enums are often used in 
 support of switch statements. If you are loosening up enums so its 
 members can be of heterogeneous types, will you be loosening up 
 switch/case as well? If not, that would be confusing to me, as you will 
 be able to switch on some enums but not on others.

The heterogeneous types for enums would only be for anonymous enums. I don't think it would make much sense to support case statements of different types, as the switched value is necessarily of only one type.
Dec 30 2007
prev sibling parent reply Sascha Katzner <sorry.no spam.invalid> writes:
Walter Bright wrote:
 Anonymous enums don't have a type (and didn't in D1.0, either). There's 
 no way to get a grip on such a type anyway, as it has no name and:
     enum { FOO, BAR } x;
 style declarations are not allowed in D.

Now I'm confused. I thought an anonymous enum was an enum without a name. But it is entirely possible to create an anonymous enum with a type, for example: enum : uint {red, green, blue} At least I use that kind of enums a lot and I allways thought of these as anonymous enums. I call enums without a type "typeless enums". LLAP, Sascha Katzner
Dec 30 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Sascha Katzner wrote:
 Walter Bright wrote:
 Anonymous enums don't have a type (and didn't in D1.0, either). 
 There's no way to get a grip on such a type anyway, as it has no name 
 and:
     enum { FOO, BAR } x;
 style declarations are not allowed in D.

Now I'm confused. I thought an anonymous enum was an enum without a name. But it is entirely possible to create an anonymous enum with a type, for example: enum : uint {red, green, blue} At least I use that kind of enums a lot and I allways thought of these as anonymous enums.

I meant that: enum { FOO, BAR } x; is not allowed because enum{FOO,BAR} is not a type declarator. This isn't allowed either: enum ABC { FOO, BAR } x; while: enum ABC { FOO, BAR } ABC x; is allowed.
Dec 30 2007
prev sibling next sibling parent Walter Bright <newshound1 digitalmars.com> writes:
Janice Caron wrote:
 On 12/29/07, Don Clugston <dac nospam.com.au> wrote:
 Does this compile?

 enum : cfloat { A=2, B, C }

Even more interestingly, what about enum : ifloat { a = 2i, b }; If b has to equal (a+1), then there is no way it can be the same type as a.

It would give the same result as: ifloat b = a + 1;
Dec 29 2007
prev sibling parent reply "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 29 Dec 2007 08:36:07 -0000, Janice Caron <caron800 googlemail.co=
m>  =

wrote:

 On 12/29/07, Don Clugston <dac nospam.com.au> wrote:
 Does this compile?

 enum : cfloat { A=3D2, B, C }

Even more interestingly, what about enum : ifloat { a =3D 2i, b }; If b has to equal (a+1), then there is no way it can be the same type =

 a.

I think Doug's arguments are the must compelling yet. However, just to play Devil's advocate with your complaint about proper = = enumerations. What if rather than being +1. The successor operation was always = opIncrement and what if opIncrement could be implemented as a free function extending even built= in = types. E.g. ifloat opIncrement(ifloat this) { return this+1i; } I'm not sure if the conference proposal of using free functions to exten= d = classes went as far as allowing operators to be declared this way but I don't see why= = it shouldn't.
Dec 29 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 29 Dec 2007 12:34:15 -0000, Bruce Adams  =

<tortoise_74 yeah.who.co.uk> wrote:

 On Sat, 29 Dec 2007 08:36:07 -0000, Janice Caron  =

 <caron800 googlemail.com> wrote:

 On 12/29/07, Don Clugston <dac nospam.com.au> wrote:
 Does this compile?

 enum : cfloat { A=3D2, B, C }

Even more interestingly, what about enum : ifloat { a =3D 2i, b }; If b has to equal (a+1), then there is no way it can be the same type=


 as a.

I think Doug's arguments are the must compelling yet.

Obviously I meant Don's arguments. Oops. Which by the way is exactly the "enum hack" we were sometimes forced to = = use in C++ before it was sanitised prior to the ISO standard. On 12/29/07, Don Clugston <dac nospam.com.au> wrote: "The primary function of enum is to create a TYPE based on a GROUP of = related INTEGRAL constants. You can abuse the facility to declare integr= al = constants, but that's a secondary feature at best. The problem is that = using enum for abtritrary types is a very poor match for the primary = feature of enums. We don't want to create a type; we don't want a grouping; and the values= = are not integral."
Dec 29 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 ""Jérôme M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare a
 constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here?

I agree with everything you are saying, except I think Walter is thinking of the case: const int x; auto y = x; // y is now const

Oooh - I think I ought to say something here, since I was one of the folk arguing that const(T) and const T should be interchangable. The thing is, I just don't see the problem. Given code like: const int x; auto y = x; it should be possible to end up with x being a manifest constant, unless the address is taken by some other piece of code somewhere else. As I've mentioned before, x is not merely const, it's actually /invariant/, despite the const declaration, because there is no conceivable way for any piece of code anywhere in the program to change it, ever (...except by doing stuff which is undefined, obviously, but that doesn't count). And that's good, right? If the compiler is really smart, it might be able to treat is as invariant(int). If not, treating it as const(int) is harmless. But as for y... y is a /copy/ of x, and clearly it should be possible to make a copy of a const thing and have the copy be mutable. Head constness needs to be dropped here, exactly as it is dropped in template distinction in D2.008 (t!(int) is the same thing as t!(const(int)) unless special syntax is used). In fact, x is very much like a template, and could be implemented in a similar way - don't instantiate it (allocate it storage space in the ROM segment) unless it is referenced (its address is taken). I don't see why any of this isn't possible. Maybe that's because I'm dumb and I'm missing something obvious, but I'm baffled as to what it is. And if I /haven't/ missed anything, then we don't need a new keyword /at all/ - be it enum, manifest, or anything else.
Dec 28 2007
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 12/28/07, Steven Schveighoffer wrote:
 ""Jérôme M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare 
 a
 constant of type int.

Er, why? Taking "&x" should return a "const (int)*" and using "x" directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here?

I agree with everything you are saying, except I think Walter is thinking of the case: const int x; auto y = x; // y is now const

Oooh - I think I ought to say something here, since I was one of the folk arguing that const(T) and const T should be interchangable. The thing is, I just don't see the problem. Given code like: const int x; auto y = x; it should be possible to end up with x being a manifest constant, unless the address is taken by some other piece of code somewhere else. As I've mentioned before, x is not merely const, it's actually /invariant/, despite the const declaration, because there is no conceivable way for any piece of code anywhere in the program to change it, ever (...except by doing stuff which is undefined, obviously, but that doesn't count). And that's good, right? If the compiler is really smart, it might be able to treat is as invariant(int). If not, treating it as const(int) is harmless. But as for y... y is a /copy/ of x, and clearly it should be possible to make a copy of a const thing and have the copy be mutable. Head constness needs to be dropped here, exactly as it is dropped in template distinction in D2.008 (t!(int) is the same thing as t!(const(int)) unless special syntax is used). In fact, x is very much like a template, and could be implemented in a similar way - don't instantiate it (allocate it storage space in the ROM segment) unless it is referenced (its address is taken). I don't see why any of this isn't possible. Maybe that's because I'm dumb and I'm missing something obvious, but I'm baffled as to what it is. And if I /haven't/ missed anything, then we don't need a new keyword /at all/ - be it enum, manifest, or anything else.

I don't disagree with you :) I'm merely clarifying Walter's gripe. I think there may be a better reason why your idea wouldn't work, but I am not sure how the compiler is implemented, so I can't really say much. However, it might be a problem when you are defining constants in one module to be used in other modules. How does the compiler know that those constants won't have their address taken somewhere else? When the compiler creates the object file, it has to assume since the symbol is public, it can have its address taken, no? If you had a specific D linker, you could modify the linker to take this into account, but that is not the case today. -Steve
Dec 28 2007
parent =?UTF-8?B?IkrDqXLDtG1lIE0uIEJlcmdlciI=?= <jeberger free.fr> writes:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Steven Schveighoffer wrote:
 "Janice Caron" wrote
 On 12/28/07, Steven Schveighoffer wrote:
 ""J�r�me M. Berger""  wrote
 Walter Bright wrote:
 The reason this won't work is because:
     const int x = 3;
 will type x as const(int), not int. There needs to be a way to declare 
 a
 constant of type int.

directly should always work so long as you don't modify it. Are you telling us that the following code will fail: void func (int param) { } const int x = 42; int y = x; // <= This should work func (x); // <= This should work too Or is there something I'm missing here?

of the case: const int x; auto y = x; // y is now const

folk arguing that const(T) and const T should be interchangable. The thing is, I just don't see the problem. Given code like: const int x; auto y = x; it should be possible to end up with x being a manifest constant, unless the address is taken by some other piece of code somewhere else. As I've mentioned before, x is not merely const, it's actually /invariant/, despite the const declaration, because there is no conceivable way for any piece of code anywhere in the program to change it, ever (...except by doing stuff which is undefined, obviously, but that doesn't count). And that's good, right? If the compiler is really smart, it might be able to treat is as invariant(int). If not, treating it as const(int) is harmless. But as for y... y is a /copy/ of x, and clearly it should be possible to make a copy of a const thing and have the copy be mutable. Head constness needs to be dropped here, exactly as it is dropped in template distinction in D2.008 (t!(int) is the same thing as t!(const(int)) unless special syntax is used). In fact, x is very much like a template, and could be implemented in a similar way - don't instantiate it (allocate it storage space in the ROM segment) unless it is referenced (its address is taken). I don't see why any of this isn't possible. Maybe that's because I'm dumb and I'm missing something obvious, but I'm baffled as to what it is. And if I /haven't/ missed anything, then we don't need a new keyword /at all/ - be it enum, manifest, or anything else.

I don't disagree with you :) I'm merely clarifying Walter's gripe. I think there may be a better reason why your idea wouldn't work, but I am not sure how the compiler is implemented, so I can't really say much. However, it might be a problem when you are defining constants in one module to be used in other modules. How does the compiler know that those constants won't have their address taken somewhere else? When the compiler creates the object file, it has to assume since the symbol is public, it can have its address taken, no? If you had a specific D linker, you could modify the linker to take this into account, but that is not the case today.

for the associated type information data): you allocate it in each module that uses it, but you put it in a special section that tells the linker to merge duplicate definitions. Jerome - -- +------------------------- Jerome M. BERGER ---------------------+ | mailto:jeberger free.fr | ICQ: 238062172 | | http://jeberger.free.fr/ | Jabber: jeberger jabber.fr | +---------------------------------+------------------------------+ -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (GNU/Linux) iD8DBQFHdV10d0kWM4JG3k8RArGnAKCZomZsyJjTDsxLiu8BkIgb6SdzewCff3bx Oi1ANAeonj5JW8M6JgbMcCs= =8FdR -----END PGP SIGNATURE-----
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 and one is drawn
 inevitably to conclude that const(T) is always a different type from T.

I don't think anyone's arguing with that. We're just saying that the behavior of auto x = could be tweaked to drop the head constancy. It's the same with passing things to functions. void f(int x) { /*...*/ } f(DAYS_IN_WEEK); we want this to compile, right? Even though DAYS_IN_WEEK is likely to have type invariant(uint). You should always be allowed to create a mutable copy (though which preserves tail constancy).
Dec 28 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 y is a /copy/ of x, and clearly it should be possible to make a copy
 of a const thing and have the copy be mutable.

That doesn't work for structs or classes.

It doesn't? For structs struct S {} const S x; auto y = x; By my reckoning x has type invariant(S) (which you could treat as const(S) if you wanted), and y would have type S. And for classes class C {} const C x; auto y = x; ...You're right, it doesn't work for classes. But is that really a problem? Isn't class C {} enum { C x; } auto y = x; exactly the same problem?
 Just for fun, how would we define a tail const member function?

Yeah, I agreed, it wouldn't work for classes. Still don't see any problem for structs though. I guess you're going to remind me that structs and classes must behave the same though - in which case you'll win the argument! :-)
 Believe me, we've been down this road for a year, trying all kinds of
 things. It doesn't work. We can get tantalizingly close to closing the
 circle, but cannot quite get there.

Hey ho. I guess we can live with enum then. But I still think you're going to have a problem with class C {} enum { C x; } auto y = x;
Dec 28 2007
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 On 12/28/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Janice Caron wrote:
 y is a /copy/ of x, and clearly it should be possible to make a copy
 of a const thing and have the copy be mutable.

That doesn't work for structs or classes.

It doesn't? For structs struct S {} const S x; auto y = x;

What about: struct S { char[] str}; const S x = S("hello".dup); auto y = x; If y is not const, then y.str is not const, yet it points to the same data as x. -Steve
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 What I don't really see is why manifest constant need to be "int"
 rather than "const (int)"

Manifest constants are invariant, so in practice it will be invariant(int), even if not explicitly declared as such. For this reason, I would hope that the compiler should be able to realise that const int x = 1; creates an x which could be stored in ROM, and automagically types x as invariant(int). Because of implicit casting, this cannot possibly break anything.
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/28/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 I understand your point, but also consider that adding yet a fourth way
 to declare constants


Actually, I think that's a fifth way. You may have forgotten about real pi() { return 3.14159265359; } Since we would hope that real x = pi; will be inlined by the compiler, it would yield the effect of a manifest constant! :)
Dec 28 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/29/07, Don Clugston <dac nospam.com.au> wrote:
 Does this compile?

 enum : cfloat { A=2, B, C }

Even more interestingly, what about enum : ifloat { a = 2i, b }; If b has to equal (a+1), then there is no way it can be the same type as a.
Dec 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/29/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Why opAdd and not opIncrement?

There is no opIncrement(). There's only opPostInc(), which requires that its input be an lvalue. Note that (x++) evaluates to x, not to x+1 (but x itself will be incremented). For rvalues, opAdd() is all you have. In /real/ enumerations, the successor is (by definition) the next item in the list. That is, given the enumeration { r, o, y, g, b, i v }, the successor of r is o, the successor of o is y, and so on, and this will be true regardless of the underlying implementation (which the application should neither know nor care). Of course, C, C++ and D enums are not true enumerations, and we don't have successor nor predecessor functions. The "trick" of figuring out a default implementation value for the next element is just that.
Dec 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/29/07, "Jérôme M. Berger" <jeberger free.fr> wrote:
 Those usages should be deprecated
 in favor of another keyword that would allow them plus extra
 functionality (e.g. manifest constants) while "enum" is relegated to
 its proper usage. I believe anything else would be too confusing
 (particularly to newcomers).

It would be /really/ cool if Walter just implemented everything as he suggested, except for deprecating the word "enum" and replacing it with "manifest" everywhere. Then "enum" could be redeployed for true enumerations only. e.g. enum Primary { red, blue, green } Primary col = red; int x = col; /* ERROR */ int x = cast(int)col /* STILL AN ERROR */ col = 1; /* ALSO AN ERROR */ col = cast(Primary)1; /* NOPE! */ Primary col2 = col++; /* successor */ assert(col2 = Primary.blue); if (col == col2) { /*...*/ } if (col < col2) { /*...*/ } enum { x = 2 }; /* ERROR */ enum E { x = 2 }; /* ERROR */ enum E : int { x }; /* ERROR */ ...but it will never happen! (Sigh!)
Dec 29 2007
prev sibling next sibling parent John Reimer <terminal.node gmail.com> writes:
On Sat, 29 Dec 2007 02:00:26 -0800, Walter Bright wrote:

 BLS wrote:
 Why not keeping enum as it was and use manfifest as enhanced enum ?
 Seems to be a clean solution.

Because, then there's a fourth way to declare constants. 3 seems to be enough.

I think the distinction comes down to adding another keyword, not a fourth way (which, as others mention, is happening nonetheless). I know you try very hard to avoid adding more keywords to D. It's always been that way and will always be that way. So I guess the contention is (and will always be) * whether it's worth the clarity that yet-another-keyword offers (YAK, for short :) ) * whether more overloading of type semantics makes sense (how much longer can D handle this) The manifest constant is really quite different from the other semantics of constant; and I think that it deserves to be set apart from the rest as a completely different entity (afterall this used to be achieved by something like #define's in C/C++). But then, I'm not sure if you have set goals on how many keywords you're allowed to add per year. If you have some sort of limit, I can see how you necessarily must show restraint. :) My question is: at what point do D keywords reach critical mass (in terms of keyword hijacking for new functionality)? This seems to happen repeatedly as D struggles to avoid keyword additions... at what /appears/ to be the expense of the programmer. This new manifest enum could work and eventually people might just get used to it... but it's so strange, so foreign, and so seemingly inconsistant that I think your betting heavily on the good-graces of your d community (who likely will forgive you and move on). But I do wonder if this is the case for all those users that are still deciding whether to adopt D or not. D 2.0 is an indicator of what is to come... so decisions made here are going to speak volumes about the future. I don't know how newcomers would react or what confusion it would cause novices, so I won't use that as argument against it. But it's a gamble and a seemingly very risky gamble. Some would say high-risk gambles don't make sense, especially when the payback is nominal. -JJR
Dec 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Surely that is what we mean with an enumeration. We allow successor (and
 optionally predecessor)
 operations to cycle through the entities in a particular order but adding
 them is unnatural.

In a true enumeration, it isn't that unreasonable to equate ++ or +1 with the successor function, and -- or -1 with the predecessor function. Given that, for an enum type E, what you want to allow is E opAdd(int) E opSub(int) but you want to disallow E opAdd(E) E opSub(E) So to use your example: Colour c2 = red+green; //WRONG Colour c2 = red+2; //OK the latter meaning the successor of the successor. Of course D enums aren't true enums, so we might end up having to allow "red+green" by accident. :-)
Dec 29 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, Bastiaan Veelo <Bastiaan veelo.net> wrote:
 If not, that would be confusing to me, as you will
 be able to switch on some enums but not on others.

You can't switch on an anonymous enum. Anonymous enums have no type, and therefore it is not even /possible/ to generate an expression whose type is that of an anonymous enum! So switch/case survives unharmed.
Dec 30 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, Janice Caron <caron800 googlemail.com> wrote:
 You can't switch on an anonymous enum. Anonymous enums have no type,
 and therefore it is not even /possible/ to generate an expression
 whose type is that of an anonymous enum!

 So switch/case survives unharmed.

OK, so everyone pointed out I got that wrong. Thanks, guys. What I meant was: you can't switch on a heterogenous anonymous enum, because heterogenous anonymous enums have no type. Sorry for the confusion.
Dec 30 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 12/30/07, 0ffh <frank youknow.what.todo.internetz> wrote:
 because to me the rule "case guards can only be of integral type"
 seems to make the most sense. I'd be rather disappointed if case
 one didn't work...

This bit of conversation seems to have lost track of the original point, which, to remind all, was that Bastiaan Veelo said:
 You can regard me as a newcomer. In my view, enums are often
 used in support of switch statements. If you are loosening up
 enums so its members can be of heterogeneous types, will you
 be loosening up switch/case as well? If not, that would be
 confusing to me, as you will be able to switch on some enums
 but not on others.

So, the answer is that anonymous enums don't create new types. The type is simply the underlying type. In the case of heterogenous anonymous enums, the type of each element can be different. This is a very different kettle of fish from /named/ enums, which do, in fact, create a new type, allowing the compiler to look at code like enum Primary { red, green, blue } Primary x = whatever; switch (x) { case Primary.red: /*...*/ case Primary.green: /*...*/ } and detect that case blue is missing, and hence a default is needed. It cannot do that for anonymous enums, because it has no way to figure out what the complete set should be. At least, I /think/ that's what Bastiaan was getting at. Anyway, the point is that the new extensions make no difference to switch/case.
Dec 30 2007
prev sibling parent Jarrod <qwerty ytre.wq> writes:
On Fri, 28 Dec 2007 01:36:30 -0800, Walter Bright wrote:

 Jérôme M. Berger wrote:
 	:(

Yeah, I figure I'll get fricasseed over that one. The most compelling argument is that we already have 3 ways to declare a constant, adding a fourth gets very difficult to justify. As opposed to a minor extension to enums.

I hate to jump in so late to this already very discussed issue, but I'm going to have to add my voice to those against it. I came to D from C/C++. If someone told me that D replaces #define with enum, I would have laughed in disbelief and then tried to define a bunch of 'static invariant' variables instead. It just seems so counter- intuitive to use enum. I should also add that more keywords is hardly a bad thing. Perl uses a quite large amount of keywords and nobody minds, because each keyword is generally given a meaningful name and a useful purpose. For example in my recent perl script I have the following line:
"print "Couldn't get $url\n" and return undef unless defined $content;"

ugly. I couldn't say the same thing for using enum to define static constants. Please reconsider manifest, Walter. :(
Jan 02 2008
prev sibling next sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 28 Dec 2007 16:15:47 +0900, Bill Baxter wrote:

 Timestamp:
      12/27/07 20:47:21 (5 hours ago)
 Author:
      walter
 Message:
 
      manifest => enum
 http://www.dsource.org/projects/phobos/changeset/536
 
 --bb

I'm reminded of a Coca-Cola bottle falling from the sky. -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Dec 28 2007
parent John Reimer <terminal.node gmail.com> writes:
Derek Parnell wrote:
 On Fri, 28 Dec 2007 16:15:47 +0900, Bill Baxter wrote:
 
 Timestamp:
      12/27/07 20:47:21 (5 hours ago)
 Author:
      walter
 Message:

      manifest => enum
 http://www.dsource.org/projects/phobos/changeset/536

 --bb

I'm reminded of a Coca-Cola bottle falling from the sky.

And we represent all the pygmies sitting in a circle, glumly looking at the "enum"? :) -JJR
Dec 28 2007
prev sibling next sibling parent Dawid =?UTF-8?B?Q2nEmcW8YXJraWV3aWN6?= <dawid.ciezarkiewicz rootnode.eu> writes:
Bill Baxter wrote:

 http://www.dsource.org/projects/phobos/changeset/536

Good work. I may be in minority, but I think this is a good choice.
Dec 28 2007
prev sibling next sibling parent guslay <guslay gmail.com> writes:
Bill Baxter Wrote:

 Timestamp:
      12/27/07 20:47:21 (5 hours ago)
 Author:
      walter
 Message:
 
      manifest => enum
 http://www.dsource.org/projects/phobos/changeset/536
 
 --bb

My only concern with enum is, will people use it? In C++, using enum to define integral constants is actually the closest match to a #DEFINE. However most people use "const int", I guess either because the syntax is more convenient, it's easier to remember or because they don't know any better.
Dec 28 2007
prev sibling parent reply 0ffh <frank youknow.what.todo.interNETz> writes:
Okay, just let's reintroduce #define! [duck=]

regards, frank
Dec 29 2007
next sibling parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 29 Dec 2007 08:45:09 -0000, 0ffh  =

<frank youknow.what.todo.interNETz> wrote:

 Okay, just let's reintroduce #define! [duck=3D]

 regards, frank

The way mixin's can be abused 'we' kind of have already [crawls into hid= ey = hole]
Dec 29 2007
prev sibling parent reply John Reimer <terminal.node gmail.com> writes:
0ffh wrote:
 
 Okay, just let's reintroduce #define! [duck=]
 
 regards, frank

Heh, it seems that might be the only way to avoid addition of a new keyword AND overload of an old keyword. Maybe once D is cleaned up, we'll end up with C again.... -JJR
Dec 29 2007
parent "Bruce Adams" <tortoise_74 yeah.who.co.uk> writes:
On Sat, 29 Dec 2007 20:58:43 -0000, John Reimer <terminal.node gmail.com=
  =

wrote:
 0ffh wrote:
  Okay, just let's reintroduce #define! [duck=3D]
  regards, frank

Heh, it seems that might be the only way to avoid addition of a new =

 keyword AND overload of an old keyword.

 Maybe once D is cleaned up, we'll end up with C again....

 -JJR

Never a grassy knoll around when you need one.
Dec 29 2007