www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Interested in D, spec confuses me.

reply Bambi <bmb23 hotmail.com> writes:
I have to disclaim that I am not a very good programmer, and I am 
doing this on a hobby level.

I'm reading through the spec because D seems to be a very 
interesting language that affords lower level control than say C# 
without the syntactical mess of C++. A few things confuse me a 
lot.

1. const isn't constant
  To my mind, a const is a value that cannot change from the 
moment it is defined. But D allows for modification of const 
values through non-const references and pointers, and does not 
promise to optimize const by placing it in read only memory or 
similar. Instead, it provides immutable, which does promise an 
constant value throughout the lifetime of the value. I'm not sure 
why this distinction was created, or what const is supposed to 
accomplish here. It's just very confusing to have two keywords 
that intuitively do the same thing but don't really. Applying 
these keywords to methods would seem to make the data of the 
parent object immutable to only the function. Which might be 
useful but isn't immediately obvious from the written form.

The example snippet ' immutable(int[]) bar() immutable {} ' 
brings back bad memories of redundant declarations of the style ' 
Object object = new Object(); '. And homonyms in programming 
languages seem like a bad idea in general.

2. when is an extern an extern?
  The wiki page on interfacing with C states that C globals 
require an extra extern. The extern definition in the spec 
clarifies that extern(C) alone will copy the global into the 
current module, but that extern extern(C) will read it right from 
the C code. Or maybe it means to say that using extern(C) alone 
will only specify a different calling convention for the variable 
you are declaring. It's honestly not clear. Homonym problem again.

3. typeof is an operator, sizeof is a property
  ...but not really :^). It seems like these are similar, they 
give compile time information about the object you pass them, but 
one is implemented as a function-looking operator in the classic 
C style while another is trying to mimic member access. It lacks 
consistency. And confuses as to what is a member and what isn't. 
Let the period operator be reserved for real member access and 
other operators be their own thing.

That's all for now. I don't really understand these choices. They 
seem to only confuse rather than clarify.
Feb 02 2016
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-02-02 15:36, Bambi wrote:

 1. const isn't constant
   To my mind, a const is a value that cannot change from the moment it
 is defined. But D allows for modification of const values through
 non-const references and pointers, and does not promise to optimize
 const by placing it in read only memory or similar. Instead, it provides
 immutable, which does promise an constant value throughout the lifetime
 of the value. I'm not sure why this distinction was created, or what
 const is supposed to accomplish here. It's just very confusing to have
 two keywords that intuitively do the same thing but don't really.
 Applying these keywords to methods would seem to make the data of the
 parent object immutable to only the function. Which might be useful but
 isn't immediately obvious from the written form.
"const" is like a read-only view of data. int a = 3; const(int*) b = &a; assert(*b == 3); a = 4; assert(*b == 4); *b = 5; // Error: cannot modify const expression *b "const" also acts like a super set of immutable and mutable data: const(int*) a = new int; immutable(int)* b = new int; int* c = new int; foo(a); foo(b); foo(c); void foo(const(int*) a) {} That would mean that "foo" will not change "a" regardless of if the original data is immutable, const or mutable.
 The example snippet ' immutable(int[]) bar() immutable {} ' brings back
 bad memories of redundant declarations of the style ' Object object =
 new Object(); '. And homonyms in programming languages seem like a bad
 idea in general.
The first immutable means that "bar" returns an immutable array of ints. The second immutable means that "bar" cannot modify the "this" reference: class Foo { int a = 3; void bar() immutable { a = 4; // Error: cannot modify immutable expression this.a } }
 2. when is an extern an extern?
   The wiki page on interfacing with C states that C globals require an
 extra extern. The extern definition in the spec clarifies that extern(C)
 alone will copy the global into the current module, but that extern
 extern(C) will read it right from the C code. Or maybe it means to say
 that using extern(C) alone will only specify a different calling
 convention for the variable you are declaring. It's honestly not clear.
 Homonym problem again.
"extern(C)" means C linkage and calling conventions. "extern" means that a symbol is defined in another object file. extern (C) int foo; Compiling that and running the "nm" command to print the symbols will list "foo": 00000000000000d0 S _foo Adding "extern" and doing the same: extern(C) extern int foo; Will result in this output: U _foo That means "foo" is undefined. That is, the linker needs to find that symbol in some other library. If you create bindings to a C library, you would use "extern(C) extern". If you create a library in D that some C code should access you would use "extern(C)"., -- /Jacob Carlborg
Feb 02 2016
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 2 February 2016 at 15:21:29 UTC, Jacob Carlborg wrote:
 [snip]
Does it make sense to add any of this to the spec?
Feb 02 2016
parent reply Daniel Kozak via Digitalmars-d <digitalmars-d puremagic.com> writes:
Everything is there already
Dne 2. 2. 2016 17:15 napsal u=C5=BEivatel "jmh530 via Digitalmars-d" <
digitalmars-d puremagic.com>:

 On Tuesday, 2 February 2016 at 15:21:29 UTC, Jacob Carlborg wrote:

 [snip]
Does it make sense to add any of this to the spec?
Feb 02 2016
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 2 February 2016 at 16:23:01 UTC, Daniel Kozak wrote:
 Everything is there already
Sure, but - as this post illustrates - there are clearly sections of the spec that could have their explanations improved.
Feb 02 2016
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2016-02-02 17:41, jmh530 wrote:

 Sure, but - as this post illustrates - there are clearly sections of the
 spec that could have their explanations improved.
I think the spec on "extern" [1][2] is pretty clear. I think the explanation of "const" and "immutable" [3] is clear as well. All of the sections have examples as well. [1] http://dlang.org/spec/declaration.html#extern [2] http://dlang.org/spec/attribute.html#linkage [3] http://dlang.org/spec/const3.html -- /Jacob Carlborg
Feb 02 2016
prev sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Tuesday, 2 February 2016 at 16:41:54 UTC, jmh530 wrote:
 Sure, but - as this post illustrates - there are clearly 
 sections of the spec that could have their explanations 
 improved.
It's sorta why i don't refer to the spec to see how the language currently works, and instead refer to the D book that's the baseline for the D2 standard; even if it's incredibly out of date. Too much of the spec from when i glanced at it felt like i was reading a lexx/yacc definition, which is a total turn-off.
Feb 02 2016
prev sibling next sibling parent reply anonymous <anonymous example.com> writes:
On 02.02.2016 15:36, Bambi wrote:
 The example snippet ' immutable(int[]) bar() immutable {} ' brings back
 bad memories of redundant declarations of the style ' Object object =
 new Object(); '. And homonyms in programming languages seem like a bad
 idea in general.
"immutable" is not a homonym here. It means the same thing ("cannot ever change"). And it's not redundant either, as the two instances apply to different targets. It's clear what the first "immutable" ties to: It qualifies the return type. The second one is less clear: It qualifies the type of the object, meaning the method can only be called on an immutable object. Leaving either of them out changes the meaning of the signature: `int[] bar() immutable {}` - Return type is mutable now. `immutable(int[]) bar() {}` - Object type is mutable now. I.e., this method can be called on a mutable object, and it cannot be called on an immutable object.
Feb 02 2016
parent reply Bambi <bmb23 hotmail.com> writes:
On Tuesday, 2 February 2016 at 15:48:02 UTC, anonymous wrote:
 "immutable" is not a homonym here. It means the same thing 
 ("cannot ever change"). And it's not redundant either, as the 
 two instances apply to different targets. It's clear what the 
 first "immutable" ties to: It qualifies the return type. The 
 second one is less clear: It qualifies the type of the object, 
 meaning the method can only be called on an immutable object.

 Leaving either of them out changes the meaning of the signature:
 `int[] bar() immutable {}` - Return type is mutable now.
 `immutable(int[]) bar() {}` - Object type is mutable now. I.e., 
 this method can be called on a mutable object, and it cannot be 
 called on an immutable object.
Making the return value immutable is a very different thing from making every value of the object immutable to the method alone. These are different meanings. It reads like a redundancy but has different meanings. This isn't good in my eyes. Also it's not so much an issue of clarification - well, the extern one is, I genuinely didn't understand what the documentation meant - but it is an issue of the design choices not making much sense to me. These just stand out to me as unnecessarily confusing and obscure in an otherwise nice and clear language.
Feb 02 2016
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/2/16 2:50 PM, Bambi wrote:
 On Tuesday, 2 February 2016 at 15:48:02 UTC, anonymous wrote:
 "immutable" is not a homonym here. It means the same thing ("cannot
 ever change"). And it's not redundant either, as the two instances
 apply to different targets. It's clear what the first "immutable" ties
 to: It qualifies the return type. The second one is less clear: It
 qualifies the type of the object, meaning the method can only be
 called on an immutable object.

 Leaving either of them out changes the meaning of the signature:
 `int[] bar() immutable {}` - Return type is mutable now.
 `immutable(int[]) bar() {}` - Object type is mutable now. I.e., this
 method can be called on a mutable object, and it cannot be called on
 an immutable object.
Making the return value immutable is a very different thing from making every value of the object immutable to the method alone. These are different meanings. It reads like a redundancy but has different meanings. This isn't good in my eyes.
Technically, it doesn't *make* it immutable, it is just an overload that accepts an immutable object. I like to think of a method like this: immutable(int)[] bar() immutable as a function that looks like this: immutable(int)[] bar(immutable(Object) this) If you tried to call this method on a mutable object, and there wasn't a mutable overload (i.e. without an attribute), the call would fail.
 Also it's not so much an issue of clarification - well, the extern one
 is, I genuinely didn't understand what the documentation meant - but it
 is an issue of the design choices not making much sense to me. These
 just stand out to me as unnecessarily confusing and obscure in an
 otherwise nice and clear language.
A lot of things in D come from C++. This is how C++ specifies const methods. The purpose is to be familiar to C++ developers, and to make porting easier. This means it can look confusing as not all C/C++ decisions were the best. However, this one I happen to like -- I can't think of a better way. -Steve
Feb 02 2016
prev sibling next sibling parent reply anonymous <anonymous example.com> writes:
On 02.02.2016 20:50, Bambi wrote:
 Making the return value immutable is a very different thing from making
 every value of the object immutable to the method alone.
Sure it's a different thing, but the meaning of "immutable" is the same. By the way, it's not that the object's fields are made immutable for the method, but the method can only be called on immutable objects.
 These are
 different meanings. It reads like a redundancy but has different
 meanings. This isn't good in my eyes.
I don't see how it reads like a redundancy. Surely, you don't expect a redundancy in this: void f(immutable int[] a, immutable int[] b); The other signature is no different. Two occurrences of "immutable", applying to two different things. I agree that it can be unclear to newbies what exactly is immutable when a method is marked immutable, but the meaning of the keyword is the same as elsewhere. Using another word there would be more confusing.
Feb 02 2016
parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Feb 02, 2016 at 09:13:41PM +0100, anonymous via Digitalmars-d wrote:
[...]
 The other signature is no different. Two occurrences of "immutable",
 applying to two different things.
 
 I agree that it can be unclear to newbies what exactly is immutable
 when a method is marked immutable, but the meaning of the keyword is
 the same as elsewhere. Using another word there would be more
 confusing.
Another way to think about it, is that the "immutable" on the function means that the implicit `this` reference to the object is immutable. T -- "How are you doing?" "Doing what?"
Feb 02 2016
parent reply IceCreamEater <bigdog icecream.com> writes:
On Tuesday, 2 February 2016 at 20:17:20 UTC, H. S. Teoh wrote:
 On Tue, Feb 02, 2016 at 09:13:41PM +0100, anonymous via 
 Digitalmars-d wrote: [...]
 The other signature is no different. Two occurrences of 
 "immutable", applying to two different things.
 
 I agree that it can be unclear to newbies what exactly is 
 immutable when a method is marked immutable, but the meaning 
 of the keyword is the same as elsewhere. Using another word 
 there would be more confusing.
Another way to think about it, is that the "immutable" on the function means that the implicit `this` reference to the object is immutable. T
I thought immutable was added to the language as a better guarantee to 'const'. Which really tells me const wasn't cutting it and wasn't a proper guarantee.
Feb 02 2016
next sibling parent Chris Wright <dhasenan gmail.com> writes:
On Tue, 02 Feb 2016 21:35:13 +0000, IceCreamEater wrote:

 On Tuesday, 2 February 2016 at 20:17:20 UTC, H. S. Teoh wrote:
 On Tue, Feb 02, 2016 at 09:13:41PM +0100, anonymous via Digitalmars-d
 wrote: [...]
 The other signature is no different. Two occurrences of "immutable",
 applying to two different things.
 
 I agree that it can be unclear to newbies what exactly is immutable
 when a method is marked immutable, but the meaning of the keyword is
 the same as elsewhere. Using another word there would be more
 confusing.
Another way to think about it, is that the "immutable" on the function means that the implicit `this` reference to the object is immutable. T
I thought immutable was added to the language as a better guarantee to 'const'. Which really tells me const wasn't cutting it and wasn't a proper guarantee.
D1 const (and up to about D2.007) was, if I recall, strictly for compile- time constants. When designing immutable, Bartosz and Walter realized that many functions don't care whether their parameters are mutable by anyone or not. For instance, the 'find' function in std.array -- it's not mutating the array, but it should be usable on mutable arrays. So we got const, where all values implicitly cast to const. Functions promise not to modify anything marked const, so it's always safe to submit immutable values. They don't get access to potential optimizations available with immutable objects, which means they're safe for mutable values.
Feb 02 2016
prev sibling parent reply "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Feb 02, 2016 at 09:35:13PM +0000, IceCreamEater via Digitalmars-d wrote:
 On Tuesday, 2 February 2016 at 20:17:20 UTC, H. S. Teoh wrote:
On Tue, Feb 02, 2016 at 09:13:41PM +0100, anonymous via Digitalmars-d
wrote: [...]
The other signature is no different. Two occurrences of "immutable",
applying to two different things.

I agree that it can be unclear to newbies what exactly is immutable
when a method is marked immutable, but the meaning of the keyword is
the same as elsewhere. Using another word there would be more
confusing.
Another way to think about it, is that the "immutable" on the function means that the implicit `this` reference to the object is immutable. T
I thought immutable was added to the language as a better guarantee to 'const'. Which really tells me const wasn't cutting it and wasn't a proper guarantee.
You're misunderstanding D's type system. Immutable is not a "better const", as though const is somehow defective. Perhaps the following diagram may help to clear things up: const / \ (mutable) immutable What this means is that both mutable and immutable are implicitly convertible to const. Or, to put it another way, const is a kind of "wildcard" that can point to underlying data that's either mutable or immutable. Mutable data is, well, mutable -- anybody who can get to it, can modify it. Immutable means *nobody* can modify it once it's initialized. Why const, then? Const is useful for when a function doesn't care whether the underlying data is mutable or not, because it doesn't need to change the data. Const provides guarantees to the caller that the function won't touch the data -- even if the data is actually mutable in the caller's context. It's a "no-mutation view" on data that's possibly mutable by a third party. Furthermore, since const provides actual guarantees that the called function isn't going to touch the data, this opens up optimization opportunities for the compiler. E.g., it can assume that any part(s) of the data that are held in registers will remain valid after the function call (provided said registers aren't touched by the function), so it doesn't need to issue another load after the function returns. As a contrived example, say you have code like this: struct Data { int x; } int func1(Data* d) { ... } int func2(const(Data)* d) { ... } ... void main() { Data d; int value1 = d.x*func1(&d) + d.x; int value2 = d.x*func2(&d) + d.x; d.x++; } When evaluating value1, the compiler may have issued a load for the first occurrence of d.x, then it calls func1. But since func1 may modify d.x, the second d.x needs another load in order to ensure the correct value is used. When evaluating value2, however, since func2 takes a const pointer to the Data, the compiler knows the value of d.x cannot possibly change across that function call, so it can safely reuse the value of d.x that was previously loaded. It may also go further and refactor the expression as d.x*(func2(&d) + 1), because the const guarantees that func2 can't mutate d.x behind our backs and invalidate the result. Such a refactoring is invalid with func1, because there is no guarantee that d.x will have the same value after func1 returns. Now, the same argument applies if immutable was used in place of const. However, the last line in main() illustrates why we need const rather than immutable in this case: we actually *want* to modify d.x in main(). We just don't want func2 to touch anything. So we can't use immutable -- since immutable means *nobody* can touch the data. So, const provides both the guarantee that func2 won't touch the data, thus allowing the aforementioned optimization, and also continues to let main() mutate the data at its leisure. As an added benefit, you can also call func2 with immutable Data: you know it's safe, because even though func2 doesn't require immutability, it also guarantees that it won't touch the data. So you don't need to write two copies of func2, one to work with mutable data and one to work with immutable data. This is why both mutable and immutable can implicitly cast to const. T -- He who does not appreciate the beauty of language is not worthy to bemoan its flaws.
Feb 02 2016
next sibling parent reply bubbasaur <bubba gmail.com> writes:
On Tuesday, 2 February 2016 at 23:41:07 UTC, H. S. Teoh wrote:
 Now, the same argument applies if immutable was used in place 
 of const. However, the last line in main() illustrates why we 
 need const rather than immutable in this case: we actually 
 *want* to modify d.x in main(). We just don't want func2 to 
 touch anything. So we can't use immutable -- since immutable 
 means *nobody* can touch the data. So, const provides both the 
 guarantee that func2 won't touch the data, thus allowing the 
 aforementioned optimization, and also continues to let main() 
 mutate the data at its leisure.
Ok, but what would differ using immutable instead of const in your example (AS ARGUMENTS)? See: import std.stdio; struct Data { int x; } auto func1(Data* d) { return d.x; } auto func2(const(Data)* d) { return d.x; } auto func3(immutable(Data)* d) { return d.x; } void main() { Data d; auto value1 = d.x*func1(&d) + d.x; auto value2 = d.x*func2(&d) + d.x; auto value3 = d.x*func3(cast(immutable)&d) + d.x; d.x++; writeln(d.x++); } Functions 2 and 3 are acting in the same way and different from what you said I can change the d.x in main. Bubba.
Feb 02 2016
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 02/02/2016 04:31 PM, bubbasaur wrote:

 Ok, but what would differ using immutable instead of const in your
 example (AS ARGUMENTS)?

 See:

 import std.stdio;

      struct Data { int x; }
      auto func1(Data* d) { return d.x; }
      auto func2(const(Data)* d) { return d.x; }
I cannot trust that a member of 'd' will not be modified later on. So, I cannot store 'd' as is. If I want to make use of its current state later, I must make a copy of it (which may have its own member-with-inderection issues). In short, the promise of "I will not change members of 'd'" is not related to what can happen to that date by other parts of the code.
      auto func3(immutable(Data)* d) { return d.x; }
There, I know that 'd' will not change state. I can store it somewhere for later use; no need to copy. I can even pass it to a thread without needing a lock.
          auto value2 = d.x*func2(&d) + d.x;
          auto value3 = d.x*func3(cast(immutable)&d) + d.x;
That's not very nice because we've just fooled func3(). :) Although it required immutable data, we've given it mutable data. The programmer is on his or her own at that point. We hope the program will work correctly. :)
 Functions 2 and 3 are acting in the same way
I like this explanation: - A const parameter is a promise by the callee to not modify - An immutable parameter is a requirement for the caller to never modify Ali
Feb 02 2016
parent bubbasaur <bubba gmail.com> writes:
On Wednesday, 3 February 2016 at 00:41:31 UTC, Ali Çehreli wrote:
 I like this explanation:

 - A const parameter is a promise by the callee to not modify

 - An immutable parameter is a requirement for the caller to 
 never modify
Well this explanation on the matter was simple, solid and comprehensive enough. Bubba.
Feb 02 2016
prev sibling parent reply Dicebot <public dicebot.lv> writes:
On 02/03/2016 02:31 AM, bubbasaur wrote:
 import std.stdio;
 
     struct Data { int x; }
     auto func1(Data* d) { return d.x; }
     auto func2(const(Data)* d) { return d.x; }
     auto func3(immutable(Data)* d) { return d.x; }   
 
     void main() {
         Data d;
         auto value1 = d.x*func1(&d) + d.x;
         auto value2 = d.x*func2(&d) + d.x;
         auto value3 = d.x*func3(cast(immutable)&d) + d.x;
         d.x++;
         writeln(d.x++);
     }
 
 Functions 2 and 3 are acting in the same way and different from what you
 said I can change the d.x in main.
Casting immutable and violating it is undefined behavior and once you do it all bets are off - compiler is even allowed to attempt formatting your hard drive when compiling this program :) The fact DMD doesn't do anything scary right now when you do it is a mere coincidence and may change in any release with no notice.
Feb 02 2016
next sibling parent reply bubbasaur <bubba gmail.com> writes:
On Wednesday, 3 February 2016 at 00:43:36 UTC, Dicebot wrote:
 Casting immutable and violating it is undefined behavior and 
 once you do it all bets are off - compiler is even allowed to 
 attempt formatting your hard drive when compiling this program 
 :)

 The fact DMD doesn't do anything scary right now when you do it 
 is a mere coincidence and may change in any release with no 
 notice.
Well if anything bad happen it will mess with dpaste. :) By the way, now you and Ali are saying this isn't correct, so I took a look on this page: http://dlang.org/spec/const3.html And looking on the bottom: "Implicit Conversions" - I wonder, is this page missing something or what? Look what I'm seeing right now: http://i.imgur.com/L4a6bx6.png It shouldn't be some circles or crosses showing the conversion on that table? Bubba.
Feb 02 2016
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 02/02/2016 04:56 PM, bubbasaur wrote:
 I took a
 look on this page: http://dlang.org/spec/const3.html
That table has a bunch of check marks, which happens to be a Unicode character: ✔. It looks like a font issue. Perhaps we should update that page and use a more available character like 'x'. :p Ali
Feb 02 2016
prev sibling parent reply Jack Stouffer <jack jackstouffer.com> writes:
On Wednesday, 3 February 2016 at 00:56:41 UTC, bubbasaur wrote:
 http://i.imgur.com/L4a6bx6.png

 It shouldn't be some circles or crosses showing the conversion 
 on that table?

 Bubba.
Works for me. Something isn't loading properly for you. If you have any plugins, try disabling them to see if that helps. If that isn't it, hit alt-super-j if you're on chrome and paste here what it says there.
Feb 02 2016
parent reply bubbasaur <bubba gmail.com> writes:
On Wednesday, 3 February 2016 at 02:00:09 UTC, Jack Stouffer 
wrote:
 Works for me. Something isn't loading properly for you. If you 
 have any plugins, try disabling them to see if that helps. If 
 that isn't it, hit alt-super-j if you're on chrome and paste 
 here what it says there.
I just have Ublock Origin here, I tried dev-tools on chrome and there are NO errors or anything, them I changed the Font family from "ROBOT SLAB" to "SAN SERIF" and this is what I got: http://i.imgur.com/zuaLXx0.png Bubba.
Feb 02 2016
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 02/02/2016 06:14 PM, bubbasaur wrote:
 On Wednesday, 3 February 2016 at 02:00:09 UTC, Jack Stouffer wrote:
 Works for me. Something isn't loading properly for you. If you have
 any plugins, try disabling them to see if that helps. If that isn't
 it, hit alt-super-j if you're on chrome and paste here what it says
 there.
I just have Ublock Origin here, I tried dev-tools on chrome and there are NO errors or anything, them I changed the Font family from "ROBOT SLAB" to "SAN SERIF" and this is what I got: http://i.imgur.com/zuaLXx0.png Bubba.
Yay! Now assume that those empty boxes are check marks. :) Ali
Feb 02 2016
prev sibling parent Kagamin <spam here.lot> writes:
On Wednesday, 3 February 2016 at 00:43:36 UTC, Dicebot wrote:
 The fact DMD doesn't do anything scary right now when you do it 
 is a mere coincidence and may change in any release with no 
 notice.
DMD performs some immutable optimizations and people noticed it.
Feb 03 2016
prev sibling next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Tue, 02 Feb 2016 15:41:07 -0800, H. S. Teoh via Digitalmars-d wrote:

 Furthermore, since const provides actual guarantees that the called
 function isn't going to touch the data, this opens up optimization
 opportunities for the compiler.
const opens up optimizations at the call site, so it's useful. immutable is useful on top of const because it allows optimizations within the function. Want to memoize a function? If it takes const(char[]), you have to copy your input and later check the entire array to see if the parameters match a previous call. If it takes an immutable(char[]), you can compare pointers. Do you need to acquire a lock to read this data? If it's immutable, no, you don't.
Feb 02 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 2/2/16 8:42 PM, Chris Wright wrote:
 On Tue, 02 Feb 2016 15:41:07 -0800, H. S. Teoh via Digitalmars-d wrote:

 Furthermore, since const provides actual guarantees that the called
 function isn't going to touch the data, this opens up optimization
 opportunities for the compiler.
const opens up optimizations at the call site, so it's useful. immutable is useful on top of const because it allows optimizations within the function. Want to memoize a function? If it takes const(char[]), you have to copy your input and later check the entire array to see if the parameters match a previous call. If it takes an immutable(char[]), you can compare pointers.
This isn't exactly right. If I call a function with "hello", I'd want it to memoize if the function is called with "hello" that resides elsewhere. What *is* true is that you can safely save the array (pointer + len) of "hello" and be sure it won't change when you check against it later. With const(char[]), you'd need to allocate a new block to make sure it doesn't change. -Steve
Feb 03 2016
parent Chris Wright <dhasenan gmail.com> writes:
On Wed, 03 Feb 2016 09:29:30 -0500, Steven Schveighoffer wrote:

 On 2/2/16 8:42 PM, Chris Wright wrote:
 On Tue, 02 Feb 2016 15:41:07 -0800, H. S. Teoh via Digitalmars-d wrote:

 Furthermore, since const provides actual guarantees that the called
 function isn't going to touch the data, this opens up optimization
 opportunities for the compiler.
const opens up optimizations at the call site, so it's useful. immutable is useful on top of const because it allows optimizations within the function. Want to memoize a function? If it takes const(char[]), you have to copy your input and later check the entire array to see if the parameters match a previous call. If it takes an immutable(char[]), you can compare pointers.
This isn't exactly right. If I call a function with "hello", I'd want it to memoize if the function is called with "hello" that resides elsewhere.
In the general case, yes. It's much faster to look up a pointer/length pair in a hashtable than to look up a very long string, so if you expect long strings and many calls with the same addresses, you might have a fast cache by pointer. You might also have values that are automatically generated, and you're relatively certain that it will be rare to have duplicate values. Or you might have a complex data structure that's immutable and has an id and a revision. If you know for certain that id+revision is unique, you can memoize based on that, and immutable gives you some extra protection.
Feb 03 2016
prev sibling parent reply Bambi <bmb23 hotmail.com> writes:
On Tuesday, 2 February 2016 at 23:41:07 UTC, H. S. Teoh wrote:
 You're misunderstanding D's type system.  Immutable is not a 
 "better const", as though const is somehow defective.  Perhaps 
 the following diagram may help to clear things up:

 	        const
 	       /     \
 	(mutable)   immutable

 What this means is that both mutable and immutable are 
 implicitly convertible to const. Or, to put it another way, 
 const is a kind of "wildcard" that can point to underlying data 
 that's either mutable or immutable.

 Mutable data is, well, mutable -- anybody who can get to it, 
 can modify it. Immutable means *nobody* can modify it once it's 
 initialized.

 Why const, then?  Const is useful for when a function doesn't 
 care whether the underlying data is mutable or not, because it 
 doesn't need to change the data. Const provides guarantees to 
 the caller that the function won't touch the data -- even if 
 the data is actually mutable in the caller's context.  It's a 
 "no-mutation view" on data that's possibly mutable by a third 
 party.

 Furthermore, since const provides actual guarantees that the 
 called function isn't going to touch the data, this opens up 
 optimization opportunities for the compiler. E.g., it can 
 assume that any part(s) of the data that are held in registers 
 will remain valid after the function call (provided said 
 registers aren't touched by the function), so it doesn't need 
 to issue another load after the function returns. As a 
 contrived example, say you have code like this:

 	struct Data { int x; }
 	int func1(Data* d) { ... }
 	int func2(const(Data)* d) { ... }
 	...
 	void main() {
 		Data d;
 		int value1 = d.x*func1(&d) + d.x;
 		int value2 = d.x*func2(&d) + d.x;
 		d.x++;
 	}

 When evaluating value1, the compiler may have issued a load for 
 the first occurrence of d.x, then it calls func1. But since 
 func1 may modify d.x, the second d.x needs another load in 
 order to ensure the correct value is used.

 When evaluating value2, however, since func2 takes a const 
 pointer to the Data, the compiler knows the value of d.x cannot 
 possibly change across that function call, so it can safely 
 reuse the value of d.x that was previously loaded.  It may also 
 go further and refactor the expression as d.x*(func2(&d) + 1), 
 because the const guarantees that func2 can't mutate d.x behind 
 our backs and invalidate the result. Such a refactoring is 
 invalid with func1, because there is no guarantee that d.x will 
 have the same value after func1 returns.

 Now, the same argument applies if immutable was used in place 
 of const. However, the last line in main() illustrates why we 
 need const rather than immutable in this case: we actually 
 *want* to modify d.x in main(). We just don't want func2 to 
 touch anything. So we can't use immutable -- since immutable 
 means *nobody* can touch the data. So, const provides both the 
 guarantee that func2 won't touch the data, thus allowing the 
 aforementioned optimization, and also continues to let main() 
 mutate the data at its leisure.


 T
In C90+, const can apply to both values and pointers to those values. And since pointers are themselves values of a memory address that is consistent usage. That seems to be the only meaningful distinction here. Pointing to a const value makes the pointer mutable but the value immutable. Pointing a const to a value makes the pointer immutable but the value mutable. etc. Immutable accomplishes nothing distinct here that I can see, other than making the use of const confusing. To make a function not change a value you declare a const input. Because it is the value declared in the function definition that is a const, not the value you pass it. "Passing as const" doesn't make any logical sense. To be honest, it smells like the kind of opaque cleverness D is ostensibly supposed to obviate.
Feb 03 2016
next sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 3 February 2016 at 11:38:06 UTC, Bambi wrote:
 Immutable accomplishes nothing distinct here that I can see, 
 other than making the use of const confusing. To make a 
 function not change a value you declare a const input.
Immutablity provides stronger guarantee that allows more optimizations, e.g. reading the same immutable value is known to result in the same value so such repeated reading can be optimized out, in C such optimization is illegal, because const data can change over time.
Feb 03 2016
parent reply Bambi <bmb23 hotmail.com> writes:
On Wednesday, 3 February 2016 at 12:12:03 UTC, Kagamin wrote:
 Immutability provides stronger guarantee that allows more 
 optimizations, e.g. reading the same immutable value is known 
 to result in the same value so such repeated reading can be 
 optimized out, in C such optimization is illegal, because const 
 data can change over time.
In C, you can only make pointers to const data a const pointer. The guarantee is built in there. Sure you can cast the address of a const to a regular pointer but then you are kind of going out of your way to break the rules and deliberately shoot yourself in the foot. I'm pretty sure casting away a const pointer to a const value is undefined behaviour.
Feb 03 2016
parent reply Kagamin <spam here.lot> writes:
On Wednesday, 3 February 2016 at 20:30:01 UTC, Bambi wrote:
 I'm pretty sure casting away a const pointer to a const value 
 is undefined behaviour.
const data in C can be immutable, but also can be mutable and change over time, that's why immutable optimizations are illegal on it: you can't tell if it's immutable, the const qualifier tells nothing about that, it only helps the callee to not modify the data.
Feb 03 2016
parent Era Scarecrow <rtcvb32 yahoo.com> writes:
On Thursday, 4 February 2016 at 06:34:15 UTC, Kagamin wrote:
 On Wednesday, 3 February 2016 at 20:30:01 UTC, Bambi wrote:
 I'm pretty sure casting away a const pointer to a const value 
 is undefined behaviour.
const data in C can be immutable, but also can be mutable and change over time, that's why immutable optimizations are illegal on it: you can't tell if it's immutable, the const qualifier tells nothing about that, it only helps the callee to not modify the data.
I tend to take away the meanings in D: Immutable: This data Cannot/Will not change Const: I promise not to change your data on you (but the owner might make changes to it at some point) So converting a const pointer to a const value... it still remains const (and it's value doesn't/won't change if const is honored). You could throw away the constness; This assumes that it doesn't have referenced data/pointers and you're editing only a local copy (or duplicated it first); But that's it's own thing to discuss.
Feb 04 2016
prev sibling parent Kagamin <spam here.lot> writes:
Another nice property of immutable data is that it can be safely 
shared between threads: const data that is a readonly view into 
changing mutable data may require locking to get consistent view 
of data; immutable data doesn't require locking, since it doesn't 
change.
Feb 03 2016
prev sibling parent "H. S. Teoh via Digitalmars-d" <digitalmars-d puremagic.com> writes:
On Tue, Feb 02, 2016 at 07:50:57PM +0000, Bambi via Digitalmars-d wrote:
 On Tuesday, 2 February 2016 at 15:48:02 UTC, anonymous wrote:
"immutable" is not a homonym here. It means the same thing ("cannot
ever change"). And it's not redundant either, as the two instances
apply to different targets. It's clear what the first "immutable"
ties to: It qualifies the return type. The second one is less clear:
It qualifies the type of the object, meaning the method can only be
called on an immutable object.

Leaving either of them out changes the meaning of the signature:
`int[] bar() immutable {}` - Return type is mutable now.
`immutable(int[]) bar() {}` - Object type is mutable now. I.e., this
method can be called on a mutable object, and it cannot be called on
an immutable object.
Making the return value immutable is a very different thing from making every value of the object immutable to the method alone. These are different meanings. It reads like a redundancy but has different meanings. This isn't good in my eyes.
[...] It's an unfortunate historical accident that function attributes can easily be conflated with return type attributes. Different people have pushed for prohibiting (apparently) ambiguous cases, but so far Walter has been resistant to the idea. Because of this, my own recommendation is to always use parenthesis when writing type qualifiers, and always write function attributes on the right rather than on the left: struct S { // Do write: immutable(int) func() { ... } int func() immutable { ... } immutable(int) func() immutable { ... } // Don't write: immutable int func() { ... } immutable immutable int func() { ... } // may not compile } I'd even recommend using parenthesis in variable declarations, just for consistency's sake: // Do write: immutable(int) x; // Don't write: immutable int x; Even though the two are identical, I think it's better to always write parentheses so that you get into the habit of thinking of the type in terms of parentheses, and don't get confused when you encounter ambiguous cases (or at least you'll be on the alert when parentheses are absent, and be sure read the function signature more carefully). T -- Debian GNU/Linux: Cray on your desktop.
Feb 02 2016
prev sibling parent Kagamin <spam here.lot> writes:
On Tuesday, 2 February 2016 at 14:36:05 UTC, Bambi wrote:
 1. const isn't constant
The idea was to ease porting C code to D, so many things work the same in C and D except for maybe integer types that were borrowed from java.
 The example snippet ' immutable(int[]) bar() immutable {} ' 
 brings back bad memories of redundant declarations of the style 
 ' Object object = new Object(); '.
Redundancy is not all that bad, but if you want less of it, you can: `auto bar() immutable {}` - the compiler will infer the return type.
 2. when is an extern an extern?
  The wiki page on interfacing with C states that C globals 
 require an extra extern.
extern attribute is optimized for binding extern functions, which is what you need most of the time. In C you would need two attributes `extern cdecl` - one for extern and one for calling convention, in D it's done with one attribute, though it only specifies calling convention, because everything is implicitly extern anyway. It works well for functions, but not for variables, when you can't differentiate between declaration and definition.
 3. typeof is an operator, sizeof is a property
sizeof is a property because it can work that way, and making it a whole special syntactical construct for this single purpose would be overkill. Now it can be implemented as a template, but it wasn't always this way.
Feb 03 2016