www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const as default for variables

reply Walter Bright <newshound2 digitalmars.com> writes:
I've often thought, as do many others here, that immutability should be the 
default for variables.

[This is a long term issue. Just thought maybe it's time for a conversation 
about it.]

Because immutable is transitive, declaring variables as immutable by default 
would be problematic. A more practical way would be to make them const.

As it is now:

1.    int x = 1;       // mutable
2.    auto x = 1;      // mutable
3.    const x = 1;     // const
4.    immutable x = 1; // immutable

Case (1) is what I'm talking about here. If it is made const, then there are a 
couple ways forward in declaring a mutable variable:

a) Introduce a new storage class, called 'var' or 'mut'. (Please, no 
bikeshedding on names at the moment. Let's stay on topic.)

b) Use 'auto' as meaning 'mutable' if the initializer is also mutable. Extend 
'auto' to allow an optional type,

     auto T t = initializer;

There may be some ambiguity issues with 'auto ref', haven't thought it through.


Once there is a non-default way to declare variables as mutable, a compiler 
switch can be added to change the default to be const. Eventually, the language 
can default to them being const, with a legacy switch to support the mutable 
default.
Mar 14 2015
next sibling parent reply "Douglas Peterson" <doug nowhere.jo> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical way 
 would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

     auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them being 
 const, with a legacy switch to support the mutable default.
Seriously: http://giphy.com/gifs/KmrpxSVxTB9Ty ? It sounds a bit like the bad idea of the month (not totally because at least a bad idea initially sounds good). What would be the rationale for such a change ? In fact, this would imply a huge breakage on any existing code, right ?
Mar 14 2015
parent "weaselcat" <weaselcat gmail.com> writes:
On Saturday, 14 March 2015 at 21:18:33 UTC, Douglas Peterson 
wrote:
 What would be the rationale for such a change ?
safety, the same reason safe should be the default. see: const correctness, virtually any ML-inspired language.
Mar 14 2015
prev sibling next sibling parent "extrawurst" <stephan extrawurst.org> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical way 
 would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

     auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them being 
 const, with a legacy switch to support the mutable default.
I like it! In fact I thought a lot of people asked to take the const-by-default route when D2 introduced it? ~S
Mar 14 2015
prev sibling next sibling parent reply "Xinok" <xinok live.com> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical way 
 would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

     auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them being 
 const, with a legacy switch to support the mutable default.
My two and a half cents, I think this is going to lead to all sorts of complications and simply wouldn't be worth the hassle. While I believe that immutable by default is a fine choice for any new language, D was designed with "mutable by default" in mind and it's simply too late to try and change that now. For example, this could be an issue for generic functions: void foo(T)(T[] arr){ T value = arr[0]; // Mutable or immutable? } Also, you forgot a fifth case where only part of the type is const/immutable: 5. const(T)[] x = ...
Mar 14 2015
parent Walter Bright <newshound2 digitalmars.com> writes:
On 3/14/2015 3:06 PM, Xinok wrote:
 For example, this could be an issue for generic functions:

      void foo(T)(T[] arr){
          T value = arr[0]; // Mutable or immutable?
      }
Not really. const T value = arr[0]; means that value isn't going to be subsequently reassigned or anything mutated through it.
 Also, you forgot a fifth case where only part of the type is const/immutable:

 5. const(T)[] x = ...
The default const'ness would apply to the [] part.
Mar 14 2015
prev sibling next sibling parent "Almighty Bob" <bob almighty.com> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.
There should be "constants" and "variables" but no such thing as... a constant variable or an immutable variable. It's either constant/immutable or it's variable, it cant be both.
Mar 14 2015
prev sibling next sibling parent reply "Orvid King" <blah38621 gmail.com> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical way 
 would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

     auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them being 
 const, with a legacy switch to support the mutable default.
Why would this be even slightly useful? If a value is constant, it _should not_ be a variable.
Mar 14 2015
parent "weaselcat" <weaselcat gmail.com> writes:
On Sunday, 15 March 2015 at 00:03:37 UTC, Orvid King wrote:
 On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical 
 way would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

    auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them 
 being const, with a legacy switch to support the mutable 
 default.
Why would this be even slightly useful? If a value is constant, it _should not_ be a variable.
C heritage.
Mar 14 2015
prev sibling next sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
Const is the inconvenience of immutability without its benefit. 
That would be a poor default, IMO.
Mar 14 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/14/2015 8:50 PM, deadalnix wrote:
 Const is the inconvenience of immutability without its benefit. That would be a
 poor default, IMO.
It's utility is in self-documenting encapsulation. It also turns out that it offers optimization possibilities for ARC systems.
Mar 14 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 15 March 2015 at 05:08:54 UTC, Walter Bright wrote:
 On 3/14/2015 8:50 PM, deadalnix wrote:
 Const is the inconvenience of immutability without its 
 benefit. That would be a
 poor default, IMO.
It's utility is in self-documenting encapsulation. It also turns out that it offers optimization possibilities for ARC systems.
You'll have to explain yourself, as there is nothing obvious here. I see how immutable can be leveraged to optimize, but I don't think const offers the same capabilities.
Mar 14 2015
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/14/2015 10:23 PM, deadalnix wrote:
 On Sunday, 15 March 2015 at 05:08:54 UTC, Walter Bright wrote:
 On 3/14/2015 8:50 PM, deadalnix wrote:
 Const is the inconvenience of immutability without its benefit. That would be a
 poor default, IMO.
It's utility is in self-documenting encapsulation. It also turns out that it offers optimization possibilities for ARC systems.
You'll have to explain yourself, as there is nothing obvious here. I see how immutable can be leveraged to optimize, but I don't think const offers the same capabilities.
"const ref" can tell the optimizer that that path for a ref counted object cannot alter its ref count.
Mar 15 2015
next sibling parent reply =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= <sludwig rejectedsoftware.com> writes:
Am 15.03.2015 um 08:44 schrieb Walter Bright:
 On 3/14/2015 10:23 PM, deadalnix wrote:
 On Sunday, 15 March 2015 at 05:08:54 UTC, Walter Bright wrote:
 On 3/14/2015 8:50 PM, deadalnix wrote:
 Const is the inconvenience of immutability without its benefit. That
 would be a
 poor default, IMO.
It's utility is in self-documenting encapsulation. It also turns out that it offers optimization possibilities for ARC systems.
You'll have to explain yourself, as there is nothing obvious here. I see how immutable can be leveraged to optimize, but I don't think const offers the same capabilities.
"const ref" can tell the optimizer that that path for a ref counted object cannot alter its ref count.
My proposal would be to make "scope" (with strong recursive guarantees) the default instead to achieve that goal. That would give the compiler a lot of optimization potential (much more than just for RC), and would at the same time most probably be a lot less limiting. It would also be a nice documentation trait to see where references are stored for later use. And it could even be used to implement checked safety for calling C/C++ functions with GC references. I agree with Dicebot that any such change should ideally be done as a single big step for all attributes at once. However, instead of a major version bump, we could have a normal deprecation path à la "opt-in only" -> "opt-in + opt-out" -> "opt-out only" -> remove old defaults.
Mar 15 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 15 March 2015 at 13:21:11 UTC, Sönke Ludwig wrote:
 My proposal would be to make "scope" (with strong recursive 
 guarantees) the default instead to achieve that goal. That 
 would give the compiler a lot of optimization potential (much 
 more than just for RC), and would at the same time most 
 probably be a lot less limiting. It would also be a nice 
 documentation trait to see where references are stored for 
 later use. And it could even be used to implement checked 
 safety for calling C/C++ functions with GC references.
Please see my newest scope proposal, which I just posted here: http://forum.dlang.org/thread/tlupkiiarrabqaxtayci forum.dlang.org#post-tlupkiiarrabqaxtayci:40forum.dlang.org It does almost what you suggest, but only for safe functions, to avoid breaking too many things. A change à la " safe by default" would then have the same effect.
Mar 15 2015
parent reply =?UTF-8?B?U8O2bmtlIEx1ZHdpZw==?= <sludwig rejectedsoftware.com> writes:
Am 15.03.2015 um 15:16 schrieb "Marc =?UTF-8?B?U2Now7x0eiI=?= 
<schuetzm gmx.net>":
 On Sunday, 15 March 2015 at 13:21:11 UTC, Sönke Ludwig wrote:
 My proposal would be to make "scope" (with strong recursive
 guarantees) the default instead to achieve that goal. That would give
 the compiler a lot of optimization potential (much more than just for
 RC), and would at the same time most probably be a lot less limiting.
 It would also be a nice documentation trait to see where references
 are stored for later use. And it could even be used to implement
 checked safety for calling C/C++ functions with GC references.
Please see my newest scope proposal, which I just posted here: http://forum.dlang.org/thread/tlupkiiarrabqaxtayci forum.dlang.org#post-tlupkiiarrabqaxtayci:40forum.dlang.org It does almost what you suggest, but only for safe functions, to avoid breaking too many things. A change à la " safe by default" would then have the same effect.
My idea was to make the change explicit first, but module wide (e.g. using a pragma or similar), so that there is no immediate breakage at all. Only the last stage of the deprecation plan would finally switch over the defaults for unannotated modules. I'll make sure to have a look at the proposal later!
Mar 15 2015
parent "Dicebot" <public dicebot.lv> writes:
On Sunday, 15 March 2015 at 22:48:18 UTC, Sönke Ludwig wrote:
 My idea was to make the change explicit first, but module wide 
 (e.g. using a pragma or similar), so that there is no immediate 
 breakage at all. Only the last stage of the deprecation plan 
 would finally switch over the defaults for unannotated modules.
This can be an interesting approach to breaking change that don't introduce new concepts but it can become tricky with mixins and mixin templates because semantic context is different from declaration context.
Mar 16 2015
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref 
 counted object cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
Mar 15 2015
next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 3/15/2015 1:09 PM, deadalnix wrote:
 On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref counted object
 cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
Passing rc objects by const ref, for example, precludes the disastrous cases enumerated in the "RCArray is unsafe" thread.
Mar 15 2015
parent "deadalnix" <deadalnix gmail.com> writes:
On Monday, 16 March 2015 at 00:07:32 UTC, Walter Bright wrote:
 On 3/15/2015 1:09 PM, deadalnix wrote:
 On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref 
 counted object
 cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
Passing rc objects by const ref, for example, precludes the disastrous cases enumerated in the "RCArray is unsafe" thread.
That is true.
Mar 16 2015
prev sibling parent reply "Zach the Mystic" <reachzach gggmail.com> writes:
On Sunday, 15 March 2015 at 20:09:56 UTC, deadalnix wrote:
 On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref 
 counted object cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
There are two cases here. One is when the reference is copied to new variable, which would actually break const because the reference count of the original data would have to be incremented (which is a separate issue). But the other case is where the original is reassigned, in which the counter for the data it used to point to gets decremented, possibly to zero. `const` would guarantee against this. But even this is a blunt force weapon, because it would also stop you from mutating the original data, even though that wouldn't change the reference count.
Mar 16 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Monday, 16 March 2015 at 14:40:51 UTC, Zach the Mystic wrote:
 On Sunday, 15 March 2015 at 20:09:56 UTC, deadalnix wrote:
 On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref 
 counted object cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
There are two cases here. One is when the reference is copied to new variable, which would actually break const because the reference count of the original data would have to be incremented (which is a separate issue).
I think we should provide library solution for this kind of things.
Mar 16 2015
parent "Zach the Mystic" <reachzach gggmail.com> writes:
On Monday, 16 March 2015 at 19:52:00 UTC, deadalnix wrote:
 On Monday, 16 March 2015 at 14:40:51 UTC, Zach the Mystic wrote:
 On Sunday, 15 March 2015 at 20:09:56 UTC, deadalnix wrote:
 On Sunday, 15 March 2015 at 07:44:50 UTC, Walter Bright wrote:
 "const ref" can tell the optimizer that that path for a ref 
 counted object cannot alter its ref count.
That is not clear why. const ref is supposed to protect against escaping when ref does not ?
There are two cases here. One is when the reference is copied to new variable, which would actually break const because the reference count of the original data would have to be incremented (which is a separate issue).
I think we should provide library solution for this kind of things.
Changing the reference count is a very low-level operation. I'm not sure how to go about breaking the type system in order to support `const` variations on it.
Mar 16 2015
prev sibling next sibling parent "Dicebot" <public dicebot.lv> writes:
I like it but would prefer to have it as part of combined "strict 
attribute as default" major version bump. It is likely to require 
lot of attention  during upgrade (really lot) even with dfix 
automation, better to spend that time and get something polished 
than have many similar but separate breaking steps.
Mar 14 2015
prev sibling next sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Saturday, March 14, 2015 13:14:50 Walter Bright via Digitalmars-d wrote:
 I've often thought, as do many others here, that immutability should be the
 default for variables.

 [This is a long term issue. Just thought maybe it's time for a conversation
 about it.]

 Because immutable is transitive, declaring variables as immutable by default
 would be problematic. A more practical way would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, then there are a
 couple ways forward in declaring a mutable variable:

 a) Introduce a new storage class, called 'var' or 'mut'. (Please, no
 bikeshedding on names at the moment. Let's stay on topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also mutable. Extend
 'auto' to allow an optional type,

      auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't thought it through.


 Once there is a non-default way to declare variables as mutable, a compiler
 switch can be added to change the default to be const. Eventually, the language
 can default to them being const, with a legacy switch to support the mutable
 default.
Given how restricted const in D is and how many things don't work well with it (e.g. it is a royal pain to make ranges work when const is involved), I seriously question that this is a good idea. And I agree with deadalnix that if we were to change the default, immutabale would make more sense. const has all of the negatives of immutable without providing many of its benefits. Yes, const provides _some_ benefit, but it's pretty limited and very limiting. So, if we're considering something like this, why not just go straight to immutable and actually get the full benefits rather than get the pain with very few benefits? But really, given how limiting const and immutable are, I seriously question that making either of them the default is a good idea - _especially_ when you consider how abysmally they interact with ranges and how big ranges are for us. Also, if we're thinking about making a change like this, it would be _way_ more beneficial to do something like make safe and pure the default for functions rather than make const the default for variables. At least in that case we'd be reducing the amount of attribute clutter in the language, whereas what you're suggesting would probably make code more verbose in general rather than less (due to having to mark so much as explicitly mutable), and even if it didn't generally make code any more verbose, it certainly wouldn't make it _less_ verbose. Making safe and pure the default would actually help reduce one of the major issues that we have - attribute proliferation, whereas this suggestion pushes one of the more limiting features as the default. - Jonathan M Davis
Mar 15 2015
parent "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Sunday, 15 March 2015 at 08:29:36 UTC, Jonathan M Davis wrote:
 Given how restricted const in D is and how many things don't 
 work well with
 it (e.g. it is a royal pain to make ranges work when const is 
 involved), I
 seriously question that this is a good idea. And I agree with 
 deadalnix that
 if we were to change the default, immutabale would make more 
 sense.  const
 has all of the negatives of immutable without providing many of 
 its
 benefits.  Yes, const provides _some_ benefit, but it's pretty 
 limited and
 very limiting. So, if we're considering something like this, 
 why not just go
 straight to immutable and actually get the full benefits rather 
 than get the
 pain with very few benefits?

 But really, given how limiting const and immutable are, I 
 seriously question
 that making either of them the default is a good idea - 
 _especially_ when
 you consider how abysmally they interact with ranges and how 
 big ranges are
 for us.

 Also, if we're thinking about making a change like this, it 
 would be _way_
 more beneficial to do something like make  safe and pure the 
 default for
 functions rather than make const the default for variables. At 
 least in that
 case we'd be reducing the amount of attribute clutter in the 
 language,
 whereas what you're suggesting would probably make code more 
 verbose in
 general rather than less (due to having to mark so much as 
 explicitly
 mutable), and even if it didn't generally make code any more 
 verbose, it
 certainly wouldn't make it _less_ verbose. Making  safe and 
 pure the default
 would actually help reduce one of the major issues that we have 
 - attribute
 proliferation, whereas this suggestion pushes one of the more 
 limiting
 features as the default.
I concur with Jonathan. There might be an argument for const-ness of foreach variables by default, but seeing how that still would require additional syntax to opt out, and would be just another special case, it's probably not a good idea either. ` safe` on the other hand looks promising, and is anyway a good direction to go. About `pure`, I'm not sure. That's very limiting wrt I/O. If `pure` only implied no access to globals/static locals, it would be less restrictive, while still providing useful guarantees. But full purity is still worth considering, of course.
Mar 15 2015
prev sibling next sibling parent reply "Dejan Lekic" <dejan.lekic gmail.com> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 [This is a long term issue. Just thought maybe it's time for a 
 conversation about it.]

 Because immutable is transitive, declaring variables as 
 immutable by default would be problematic. A more practical way 
 would be to make them const.

 As it is now:

 1.    int x = 1;       // mutable
 2.    auto x = 1;      // mutable
 3.    const x = 1;     // const
 4.    immutable x = 1; // immutable

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:

 a) Introduce a new storage class, called 'var' or 'mut'. 
 (Please, no bikeshedding on names at the moment. Let's stay on 
 topic.)

 b) Use 'auto' as meaning 'mutable' if the initializer is also 
 mutable. Extend 'auto' to allow an optional type,

     auto T t = initializer;

 There may be some ambiguity issues with 'auto ref', haven't 
 thought it through.


 Once there is a non-default way to declare variables as 
 mutable, a compiler switch can be added to change the default 
 to be const. Eventually, the language can default to them being 
 const, with a legacy switch to support the mutable default.
I definitely think this is a good idea. And if someone wants mutable variable, we simply use proposed 'var' storage class. Brilliant!
Mar 17 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 17 March 2015 at 13:55:36 UTC, Dejan Lekic wrote:
 I definitely think this is a good idea. And if someone wants 
 mutable variable, we simply use proposed 'var' storage class.

 Brilliant!
This is going to break pretty much all the code that use auto. The benefice for the compiler is hypothetical. Walter is right when mentioning that is can remove some refcount boilerplate, which is right but, we have no idea how much, and how good the compiler would be at recognizing them. With existing languag feature, the compiler CANNOT leverage this change for optimization. I'd consider this if it was from scratch, but in the current state of affair, it is a change that does not pay for itself. Switching to safe by default for instance will break less code while providing more.
Mar 17 2015
parent reply "Zach the Mystic" <reachzach gggmail.com> writes:
On Tuesday, 17 March 2015 at 19:53:14 UTC, deadalnix wrote:
 On Tuesday, 17 March 2015 at 13:55:36 UTC, Dejan Lekic wrote:
 I definitely think this is a good idea. And if someone wants 
 mutable variable, we simply use proposed 'var' storage class.

 Brilliant!
This is going to break pretty much all the code that use auto. The benefice for the compiler is hypothetical. Walter is right when mentioning that is can remove some refcount boilerplate, which is right but, we have no idea how much, and how good the compiler would be at recognizing them. With existing languag feature, the compiler CANNOT leverage this change for optimization.
The real devil against safe reference counting is in the assignment operators, when they do destructive moves. I think those have to be the focus of any effort here. I'm trying to imagine a parameter attribute ` destroy`, for example, indicating that its reference may get destroyed. Not sure if it will work, or even help, but it's a start.
Mar 17 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Tuesday, 17 March 2015 at 22:25:30 UTC, Zach the Mystic wrote:
 The real devil against safe reference counting is in the 
 assignment operators, when they do destructive moves. I think 
 those have to be the focus of any effort here.

 I'm trying to imagine a parameter attribute ` destroy`, for 
 example, indicating that its reference may get destroyed. Not 
 sure if it will work, or even help, but it's a start.
That is the wrong approach. This is a know problem and there is a known solution: ownership. If we are going to add something in the language to handle it, then it has to be ownership.
Mar 17 2015
parent reply "Zach the Mystic" <reachzach gggmail.com> writes:
On Tuesday, 17 March 2015 at 22:53:20 UTC, deadalnix wrote:
 On Tuesday, 17 March 2015 at 22:25:30 UTC, Zach the Mystic 
 wrote:
 The real devil against safe reference counting is in the 
 assignment operators, when they do destructive moves. I think 
 those have to be the focus of any effort here.

 I'm trying to imagine a parameter attribute ` destroy`, for 
 example, indicating that its reference may get destroyed. Not 
 sure if it will work, or even help, but it's a start.
That is the wrong approach. This is a know problem and there is a known solution: ownership. If we are going to add something in the language to handle it, then it has to be ownership.
Just so we're clear, there are two problems. One is making ref-counting safe. The other is making it fast, by eliding unnecessary operations. The issue I'm worried about is when you pass an RC'd type as an argument by value, for example, you make a copy. To be safe the compiler should wrap the original in an inc/dec cycle for the duration of the call. But this is a waste if there's no risk of reassignment, if you're just mutating the original data, or if the type isn't even an RC'd type but has some other kind of destructor. My guess was that ` destroy` could help the compiler elide unnecessary cycles this way. If you always pass by reference (e.g. `ref`), you're sending the original, rather than copying it. This needs no wrapping therefore, since any reassignment will affect the original. What good would ownership do in that case? Any normal copying will increase the refcount anyway. I'm starting to think that refcounting is precisely the opposite of ownership, useful only for when its *impossible* to track ownership easily. Otherwise why would you need a refcount? What would be really interesting is a combined system, where the compiler detects the ownership properties of any given variable, and automatically decides whether it needs to be refcounted or not. There could be a built-in template in the runtime, e.g. a `_refCounted(T)`, which must be a perfect drop-in replacement for a regular `T` in all cases -- difficult, yes, but interesting to imagine at least -- which the compiler would swap in at its discretion. Obviously a huge flight of fancy, given that D is not in the habit of altering the basic type of a variable based on how it used... but it would be very efficient if it worked. Do you agree that refcounting and ownership oppose each other, that refcounting only makes sense when ownership is impossible? That refcounting is a runtime mechanism for tracking precisely what a compile time ownership system can't? In other words, what problems does ownership solve, and how?
Mar 17 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 18 March 2015 at 06:24:38 UTC, Zach the Mystic 
wrote:
 I'm starting to think that refcounting is precisely the 
 opposite of ownership, useful only for when its *impossible* to 
 track ownership easily. Otherwise why would you need a refcount?
It is not the language's problem. If the language defines ownership, the you can define all kind of RC systems as library type by deferring the ownership of things to the RC library. The good thing about it is that it doesn't limit the library solution to be ref counting, but it can be anything else, or any refcounting strategy. Indeed, internally, the RC system have to play unsafe, but as long as it has to free, it has to play unsafe anyway. The important point is that it can provides a safe interface to the outside world. The inc/dec elision problem is simply a copy optimization problem. Framing it as a refcounting problem is the wrong way to think about it. You would like to elide copy as much as possible. The first element for this is borrowing. You can pass borrowed things around without needing to have copies. The general problem of the assignation comes up when something is borrowed several time and assigned. const is obviously a situation where we can elide when borrowing, but that is not the only one. In that situation, only borrowing the RC wrapper require a copy (borrowing the wrapped do not). Note that borrowing the wrapped is most likely what you want in the first place in most situation (so the code manipulating the borrowed do not need to rely on a specific memory management scheme, which allow for versatile libraries) so copy elision is what you'll in most situation as well. Solid core constructs are much better that attribute proliferation.
Mar 18 2015
parent "Zach the Mystic" <reachzach gggmail.com> writes:
On Wednesday, 18 March 2015 at 09:28:35 UTC, deadalnix wrote:
 On Wednesday, 18 March 2015 at 06:24:38 UTC, Zach the Mystic 
 wrote:
 I'm starting to think that refcounting is precisely the 
 opposite of ownership, useful only for when its *impossible* 
 to track ownership easily. Otherwise why would you need a 
 refcount?
It is not the language's problem. If the language defines ownership, the you can define all kind of RC systems as library type by deferring the ownership of things to the RC library. The good thing about it is that it doesn't limit the library solution to be ref counting, but it can be anything else, or any refcounting strategy. Indeed, internally, the RC system have to play unsafe, but as long as it has to free, it has to play unsafe anyway. The important point is that it can provides a safe interface to the outside world. The inc/dec elision problem is simply a copy optimization problem. Framing it as a refcounting problem is the wrong way to think about it. You would like to elide copy as much as possible. The first element for this is borrowing. You can pass borrowed things around without needing to have copies.
So let's go through the steps. Question: What parts of borrowing are internally kept track of by the compiler, and what parts are made manifest in code? For what is made manifest, how do they appear -- as type qualifiers, i.e. `borrowed`, `owned` -- or built-in properties, e.g. `fun(x.borrowed)`? For things kept hidden, we need to find potential sources of ambiguity, and derive reliable algorithms to resolve them. For me, a big issue is passing variables as arguments, because the compiler can't read into the function to see what it does, and the function can only tell the caller what the attribute system allows. What if the caller takes a wrapped type and you only have an unwrapped version, or a different wrapped version, to pass to it? Should there be any way to pass it transparently (i.e for the called type to automatically receive the passed type of the argument), or does it have to be created manually? (I was thinking about this when Andrei was trying to create smart pointers, and wondered what it would take to create a`Ref!` type to entirely replace the `ref` storage class.) This may or may not be related to a fully effective ownership system.
 The general problem of the assignation comes up when something 
 is borrowed several time and assigned. const is obviously a 
 situation where we can elide when borrowing, but that is not 
 the only one.

 In that situation, only borrowing the RC wrapper require a copy 
 (borrowing the wrapped do not). Note that borrowing the wrapped 
 is most likely what you want in the first place in most 
 situation (so the code manipulating the borrowed do not need to 
 rely on a specific memory management scheme, which allow for 
 versatile libraries) so copy elision is what you'll in most 
 situation as well.
I guess this will most often be accomplished with `alias this` when passing to an argument? I guess what you're suggesting is that if a function may delete a reference, you can detect this because it accepts only a fully RC'd type rather than the unwrapped version.
 Solid core constructs are much better that attribute 
 proliferation.
I totally agree, but at this point, we must figure out precisely which constructs to ask for, and then convince everyone else of their worth. How did you become convinced of the value of built-in ownership? Is there a good article you could point me to? Secondly, what do you suggest it would look like in D? Type qualifiers, a storage class, function/parameter attributes? How much just takes place invisibly to the programmer?
Mar 18 2015
prev sibling parent "Moritz Maxeiner" <moritz ucworks.org> writes:
On Saturday, 14 March 2015 at 20:15:30 UTC, Walter Bright wrote:
 I've often thought, as do many others here, that immutability 
 should be the default for variables.

 Case (1) is what I'm talking about here. If it is made const, 
 then there are a couple ways forward in declaring a mutable 
 variable:
The following is just my point of view, so take it with a grain of salt and correct me if I state/understand something wrong: I usually abstain from participating in discussions here, because more often than not someone else will already more or less write what I would, so there is little point in my writing what has already been posted. This issue, however, I consider fairly important, as what you propose would make me classify D as "don't touch", which I really don't want considering that I've been following and using D for the better part of ten years; let me explain why: There exists an abstract amount of data that I want to store somewhere and access within my program. I shall call one instance of something I put my data into "storage entity" (SE for short). Now depending on what properties my data inherently has (or I may additionally attribute to it) I may want or need a SE to - allow any data within it to be changed ([wholly] mutable) - prohibit any data within it to be changed ([wholly] immutable) - allow some of the data within it to be changed (partially mutable) [Here and unless otherwise stated I do not use immutable in the transitive meaning that D currently applies to it, instead it is only applied to one SE] The first of the three is what is generally in computer science (CS) called a variable, the second a constant. SEs of the third type are also mostly referred to as variables, as they are usually implemented as an extension to the first. I know that from a mathematical standpoint, a variable is only a symbol with an attributed value without any associated notion about (im)mutability, so even a contant would be a variable, but this is not how the terminology is used in CS. In CS a variable must allow some kind of mutability; not necessarily wholly, but without mutability it would be a constant, not a variable. As such, should D's SEs default to being wholly immutable (which you seem to propose), it should not call them variables anymore (since they aren't), but instead clearly state that "D's SE default to being constants and if you want a variable, do [...]". With only primitives (no pointers), there can be no partial mutability, you are either allowed to assign a new (primitive) value or you are not. Partial mutability becomes a serious concern, however, once pointers/references are involved, e.g. if you want to reference an SE that is wholly immutable. Does your reference automatically also become immutable (as I understand if - and please correct me if I am wrong here - this is what D's transitive "immutable" means)? I understand that with this extremely complex issue, it may seem desirably to instead default to whole non-transtitive immutability and make people explicitly state when they want their SEs to be mutable. One might argue that it would make a lot of things simpler for everyone involved. However, D is a systems programming language and I would counter-argue that I believe the amount of partially mutable SEs to far outweight the amount of wholly immutable ones and having something like int foo = 5; [...] foo = 6; produce a compile-error because "foo" is by default non-transitive immutable [D terminology would be "const" I think] is something I can only call absurd for the following reason: It breaks with the convention systems programming languages have been using for a very long time. While I'm not generally against cutting off traditions no longer needed, I believe this would have a serious negative impact on people coming form C/C++ who are expecting new cool stuff (which D definitely has) without ground-breaking changes. The longer the list of core differences to the way you code you have to remind yourself about when switching to D, the less likely you will switch to D, I think. What I would propose is the following: Have the compiler-frontend track for all SE whether they are assigned to more than once (counting the initial assignment). Any SE that isn't can safely be marked "const" (or non-transitive immutable) the way to described in your opening post. However, is an SE assigned to at least twice is must not be marked as const. This should - in my opinion - give about the same level of safety as marking everything as "const" by default while not breaking with any long-standing conventions.
Mar 19 2015