April 11, 2022
On 4/11/22 08:02, Paul Backus wrote:

> any pointers or references

To add, Salih and I were in an earlier discussion where that concept appeared as "indirections."

Ali

April 11, 2022
On 4/11/22 05:57, wjoe wrote:

> To my understanding immutable data should reside in a separate data
> segment which itself could reside in ROM.

We are getting into implementation details which a programming language acts as not to care (but has to do especially when it's a system programming language like D. :) ).

> So when the variable, or 'pointer' to the data, goes out of scope just
> the 'pointer' is gone, the actual data unreachable, but still there.

I think it translates to the destructor never being executed. Otherwise, nobody would even know whether the memory location is reused for other pursposes later on.

> Due to its immutable nature immutable data can't be changed and this, to
> my understanding, includes deallocation.

D one language where object lifetime is deliberately separate from memory allocation.

> And because the data could be
> in ROM any modification is an error.

Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.)

> immutable data on the fly doesn't make sense to me - that should be
> const's domain.

Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++):

1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that:

  void foo (const(int)[] arr);

2) This variable is const:

  const i = 42;

Well, there is the confusion: There is no "reference" in the second case at all!

I don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly.

Perhaps you are saying the same thing but enters D's immutable: The data is immutable.

immutable data on the fly can be very useful because e.g. it makes multithreaded programming trivial in some cases by removing the need for locking.

> Strings, I know. But the way things are, I hardly see a
> difference between immutable and const.

Imagine a File struct that holds on to a file name. In C++, we would have to make a copy of the constructor argument for two reasons:

1) Lifetime of the object might be short. This is not a problem in D because of the GC.

2) The data might change after I start holding on to it through a reference. This is not a problem *only if* data were immutable because my const parameter cannot preclude the producer from mutating it further. Example:

import std.stdio;
import std.format;

struct S {
  const(char)[] fileName;

  this(const(char)[] fileName) {
    this.fileName = fileName;
    report();
  }

  ~this() {
    report();
  }

  void report(string func = __FUNCTION__) {
    writefln!"%s working with %s."(func, fileName);
  }
}

void main() {
  char[] fileName = "foo.txt".dup;
  auto s = S(fileName);
  fileName[0..3] = "bar";
}

The output shows that the file name changed between construction and destruction:

deneme.S.this working with foo.txt.
deneme.S.~this working with bar.txt.

If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name.

Ali

April 12, 2022
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:
> 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that:
>
>   void foo (const(int)[] arr);
>
> 2) This variable is const:
>
>   const i = 42;
>
> Well, there is the confusion: There is no "reference" in the second case at all!
I think this second case should not be allowed. Use

   immutable i = 42;

instead. The meaning is identical, but we could remove the burden of two different meanings from const if it is not allowed. const should only be allowed in function declarations. A variable must be declared either mutable or immutable. It's only functions that may guarantee not to modify a parameter or the objects they belong to, and so are allowed to work on both mutable and immutable objects.

April 12, 2022
On 4/12/22 03:28, Dom DiSc wrote:
> On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:
>> 1) I will not mutate data through this reference. For example, a
>> parameter that is pointer to const achieves that:
>>
>>   void foo (const(int)[] arr);
>>
>> 2) This variable is const:
>>
>>   const i = 42;
>>
>> Well, there is the confusion: There is no "reference" in the second
>> case at all!
> I think this second case should not be allowed. Use
>
>     immutable i = 42;
>
> instead. The meaning is identical,

Only if there are no indirections as in 'int'. If I use a struct

struct S {
  const(char[]) arr;
}

void foo(const(char[]) arr) {
  immutable s = immutable(S)(arr);  // <-- ERROR:
  // Error: cannot implicitly convert expression `arr` of type `const(char[])` to `immutable(string)`
}

void main() {
  int[] arr;
  foo(arr);
}

The reason is a property of immutable that I like to describe as "selective" (or "demanding"). The struct variable cannot be immutable because it would be demanding that its constructor argument be immutable, which cannot be because foo's 'const' parameter hides that information even when it were immutable e.g. in main().

> but we could remove the burden of two
> different meanings from const if it is not allowed. const should only be
> allowed in function declarations. A variable must be declared either
> mutable or immutable. It's only functions that may guarantee not to
> modify a parameter or the objects they belong to, and so are allowed to
> work on both mutable and immutable objects.

I understand but the guarantee of immutable seems to leave no other choice.

Ali

April 12, 2022
On Monday, 11 April 2022 at 22:10:07 UTC, Ali Çehreli wrote:
> On 4/11/22 05:57, wjoe wrote:
>
> > And because the data could be
> > in ROM any modification is an error.
>
> Fully agreed. However, how could I initialize such an object then? (You may have meant a read-only memory page instead of ROM.)
>

I was thinking during compile time. By initializing a variable with immutable data or a pointer that points to an address e.G. an EEPROM.

> Even 'const' cause confusions because it's used in at least two different ways (even e.g. in C++):
>
> 1) I will not mutate data through this reference. For example, a parameter that is pointer to const achieves that:
>
>   void foo (const(int)[] arr);
>
> 2) This variable is const:
>
>   const i = 42;
>
> Well, there is the confusion: There is no "reference" in the second case at all!

In general, I guess, it's a bad idea to reuse the same word for 2 or more distinctly different ideas. Maybe the const keyword in 1) should have a better name. Especially since it's only a promise and the compiler accepts this:

void foo (const(char)[] arr)
{
  cast(char[])arr[0..3] = "baz";
}
string bar = "123";
foo(bar);
assert(bar=="baz");

But I could cast away const and modify the string bar.

So with that said
> I don't agree with you when you say immutability should be const's domain because const covers item 1 above as well, where there is no immutability of data whatsoever. The data may be perfectly mutable or immutable, where my access will be readonly.

When I said immutability should be the domain of const I am referring only to 2).

I.e. immutable is constant data which is created at compile time - like laws of physics,
const as in 2) is constant data which is created at run time - like man made laws,
and 1) should get a better name - maybe 'in' and get rid of const.

And to be able to use immutable anywhere other than where immutable is explicitly specified, a copy is necessary.

I know it's not as simple as that. But one can dream, right? :)

> producer from mutating it further. Example:
>
> import std.stdio;
> import std.format;
>
> struct S {
>   const(char)[] fileName;
>
>   this(const(char)[] fileName) {
>     this.fileName = fileName;
>     report();
>   }
>
>   ~this() {
>     report();
>   }
>
>   void report(string func = __FUNCTION__) {
>     writefln!"%s working with %s."(func, fileName);
>   }
> }
>
> void main() {
>   char[] fileName = "foo.txt".dup;
>   auto s = S(fileName);
>   fileName[0..3] = "bar";
> }
>
>
> If fileName were immutable, then the owner would not be able to mutate anyway, so the struct could get away without copying the file name.
>
> Ali

I presume you refer to fileName in main() ? And if yes, if it were const, it couldn't be mutated either, so isn't immutable and const sort of synonymous in that case or am I missing your point?
April 12, 2022
On Tuesday, 12 April 2022 at 19:54:13 UTC, wjoe wrote:
> Especially since it's only a promise and the compiler accepts this:
>
> void foo (const(char)[] arr)
> {
>   cast(char[])arr[0..3] = "baz";
> }
> string bar = "123";
> foo(bar);
> assert(bar=="baz");
>
> But I could cast away const and modify the string bar.

No, you could not. You're relying on undefined behavior there. Just because the compiler accepts something, doesn't mean it's ok.

If you want to be guarded against wandering into undefined territory, that's what @safe does. With @safe, the cast doesn't compile.
April 12, 2022
On 4/12/22 12:54, wjoe wrote:

> I.e. immutable is constant data which is created at compile time - like
> laws of physics,

For completeness, we already have 'enum' and 'static const' for that.

> should get a better name - maybe 'in' and get rid of const.

Yes! 'in' for parameters! :D

>> import std.stdio;
>> import std.format;
>>
>> struct S {
>>   const(char)[] fileName;
>>
>>   this(const(char)[] fileName) {
>>     this.fileName = fileName;
>>     report();
>>   }
>>
>>   ~this() {
>>     report();
>>   }
>>
>>   void report(string func = __FUNCTION__) {
>>     writefln!"%s working with %s."(func, fileName);
>>   }
>> }
>>
>> void main() {
>>   char[] fileName = "foo.txt".dup;
>>   auto s = S(fileName);
>>   fileName[0..3] = "bar";
>> }
>>
>>
>> If fileName were immutable, then the owner would not be able to mutate
>> anyway, so the struct could get away without copying the file name.
>>
>> Ali
>
> I presume you refer to fileName in main() ?

No, I meant the member 'fileName'. If the member is 'const' (as in C++ and as in the example above), you have to take a copy because the caller may mutate. (The example is demonstrating the case where the struct assumes data won't changed and gets surprised by the mutation.)

> And if yes, if it were
> const, it couldn't be mutated either,

You started as assuming 'fileName' in main, so, I will continue with that. If the struct had 'const fileName' and pleaded that the caller also define a 'const' variable, that would be wishful thinking. The struct cannot trust that all callers will obey that plea.

That kind of trust (more like guarantee) comes with 'immutable' and this is a good example of how immutable is different from const. (immutable does not exist in some languages like C++.)

> so isn't immutable and const sort
> of synonymous in that case or am I missing your point?

If on the other hand, the member were immutable, the caller would have to provide immutable data and the struct could rely on the fact that the data was immutable forever. No need for a copy...

Ali

April 13, 2022

On Monday, 11 April 2022 at 21:48:56 UTC, Ali Çehreli wrote:

>

On 4/11/22 08:02, Paul Backus wrote:

>

any pointers or references

To add, Salih and I were in an earlier discussion where that concept appeared as "indirections."

Ali

I tried the following and I didn't understand one thing: Why is there no need to use dup when slicing?

struct S(T) {
 T fileName;

  this(T fileName) {
    this.fileName = fileName;
    report();
  }

  ~this() { report(); }

  void report(string func = __FUNCTION__) {
    import std.stdio;
    writefln!"%s\nworking with %s"(func, fileName);
  }
}
alias T1 = const(char)[];
alias T2 = const char[];
alias T3 = const(char[]); // T3 == T2
alias T4 = immutable char[]; // Not compiling!


void main() {
  auto fileName = "foo.txt".dup;
  auto s = S!T1(fileName);
  fileName[0..3] = "bar";
}/* Results:
  *
  *T1:
  *
  source.S!(const(char)[]).S.this
  working with foo.txt.
  source.S!(const(char)[]).S.~this
  working with bar.txt.
  *
  *T2:
  *
  source.S!(const(char[])).S.this
  working with foo.txt
  source.S!(const(char[])).S.~this
  working with bar.txt
  *
  *T3:
  *
  source.S!(const(char[])).S.this
  working with foo.txt
  source.S!(const(char[])).S.~this
  working with bar.txt
  *
  *T4
  *
  Error: slice `fileName[0..3]` is not mutable
  */

SDB@79

April 13, 2022
On 4/12/22 21:34, Salih Dincer wrote:

> I tried the following and I didn't understand one thing: Why is there no
> need to use dup when slicing?

I don't think I understand you fully.

> ```d
> struct S(T) {
>   T fileName;
>
>    this(T fileName) {
>      this.fileName = fileName;
>      report();
>    }
>
>    ~this() { report(); }
>
>    void report(string func = __FUNCTION__) {
>      import std.stdio;
>      writefln!"%s\nworking with %s"(func, fileName);
>    }
> }
> alias T1 = const(char)[];
> alias T2 = const char[];
> alias T3 = const(char[]); // T3 == T2
> alias T4 = immutable char[]; // Not compiling!
>
>
> void main() {
>    auto fileName = "foo.txt".dup;

In every test case that 'fileName' is char[].

>    auto s = S!T1(fileName);
>    fileName[0..3] = "bar";

For that reason, that expression will always succeed. And that's the point I tried to make: Without immutable, struct S *must* make a copy in order to guarantee that its member will remain the same.

> }/* Results:

[...]

>    *
>    *T4
>    *
>    Error: slice `fileName[0..3]` is not mutable

Your code was different because I get an error that I expect: The following line fails.

  auto s = S!T4(fileName);

cannot pass argument `fileName` of type `char[]` to parameter `immutable(string) fileName`

Exactly! struct S wants immutable (to guarantee that nobody will mutate it) but 'fileName' in main is not immutable. You can make it pass in the T4 case

a) by making an immutable copy with .idup:

  auto s = S!T4(fileName.idup);

b) by converting 'fileName' to immutable without a copy:

  import std.exception;
  auto s = S!T4(fileName.assumeUnique);
  // But then, the following will fail. Awesome!
  // fileName[0..3] = "bar";

I am amazed everyday how great D is; such a fun engineering tool!

Ali

April 13, 2022
On Wed, Apr 13, 2022 at 08:39:17AM -0700, Ali Çehreli via Digitalmars-d-learn wrote:
> On 4/12/22 21:34, Salih Dincer wrote:
> 
> > I tried the following and I didn't understand one thing: Why is there no need to use dup when slicing?
[...]

Because of two things: (1) there is a GC, and (2) characters in a string
are immutable.

Without (1), you will end up with either a memory leak or a dangling
pointer; without (2), your slice may randomly mutate when you don't
expect it to.


T

-- 
May you live all the days of your life. -- Jonathan Swift