www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Const-correctness and uniqueness. Again.

reply "Dicebot" <public dicebot.lv> writes:
Doing a lot of porting from D1 to D2 recently I have realized 
that we still don't have any idiomatic way to express function 
result types that can be both mutable and immutable.

Consider this trivial snippet:

```D
import std.array : join;

void main()
{
	auto s = join([ "aaa", "bbb", "ccc" ]);
	pragma(msg, typeof(s));
}
```

It outputs "string" which stands for immutable buffer. However, 
actual allocated buffer is not yet truly immutable - it has just 
been allocated to hold the result of join algorithm and may be 
interpreted both as mutable and immutable, depending on caller 
desire.

In this specific case if caller wanted to get a mutable buffer 
instead, it would need to do cast from immutable which is very 
dangerous - especially considering implementation of `join` may 
change. And doing .dup on buffer that has just been allocated is 
quite a waste.

Right now I am leaning towards personal convention to always 
return mutable or const buffers and do assumeUnique at caller 
side where necessary. But this does, of course, suck.

What is current conventional wisdom on topic? Can we probably 
start using std.typecons.Unique for such functions?
Feb 09 2015
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Monday, 9 February 2015 at 10:56:31 UTC, Dicebot wrote:
 Consider this trivial snippet:

 ```D
 import std.array : join;

 void main()
 {
 	auto s = join([ "aaa", "bbb", "ccc" ]);
 	pragma(msg, typeof(s));
 }
 ```

 It outputs "string" which stands for immutable buffer.
The following works as well: --- void main() { import std.array : join; import std.stdio : writeln; char[] s = join(["foo", "bar"]); writeln(s); //foobar } --- std.array.join is strongly pure (with appropriate template arguments), so its return value is implicitly convertible to immutable.
Feb 09 2015
next sibling parent reply "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Err, implicitly convertible to *mutable*. It goes both ways.
Feb 09 2015
next sibling parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Monday, 9 February 2015 at 11:38:23 UTC, Jakob Ovrum wrote:
 On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Err, implicitly convertible to *mutable*. It goes both ways.
It does? Not according to my tests. And it would be bad if it did, because the returned immutable value could actually reside in read-only memory.
Feb 09 2015
parent reply "Tobias Pankrath" <tobias pankrath.net> writes:
On Monday, 9 February 2015 at 12:39:06 UTC, Marc Schütz wrote:
 On Monday, 9 February 2015 at 11:38:23 UTC, Jakob Ovrum wrote:
 On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Err, implicitly convertible to *mutable*. It goes both ways.
It does? Not according to my tests. And it would be bad if it did, because the returned immutable value could actually reside in read-only memory.
join returns a mutable array, that can be implicitly casted to immutable if join is pure. BTW, that was hard to figure out, because ElementEncodingType!(ElementType!RoR)[] join(RoR, R)(RoR ror, R sep) if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR)) && isInputRange!R && is(Unqual!(ElementType!(ElementType!RoR)) == Unqual!(ElementType!R))); ElementEncodingType!(ElementType!RoR)[] join(RoR)(RoR ror) if (isInputRange!RoR && isInputRange!(Unqual!(ElementType!RoR))); and ElementEncodingType is not linked. That would read so much better as let alias C = ElementEncodingType!(ElementType!RoR) alias ERT = Unqual!(ElementType!RoR) alias ET = ElementType!ERT in C[] join(RoR, R)(RoR ror, R sep) if( isInputRange!(RoR) && isInputRange!ERT && isInputRange!R && is(ET == ElementType!R)) C[] join(RoR)(RoR ror) if( isInputRange!RoR) && isInputRange!ERT ) Do our planned ddo[c|x] enhancement make this possible?
Feb 09 2015
parent reply "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm gmx.net> writes:
On Monday, 9 February 2015 at 13:19:24 UTC, Tobias Pankrath wrote:
 On Monday, 9 February 2015 at 12:39:06 UTC, Marc Schütz wrote:
 On Monday, 9 February 2015 at 11:38:23 UTC, Jakob Ovrum wrote:
 On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Err, implicitly convertible to *mutable*. It goes both ways.
It does? Not according to my tests. And it would be bad if it did, because the returned immutable value could actually reside in read-only memory.
join returns a mutable array, that can be implicitly casted to immutable if join is pure.
I'm talking about the general case. Jacob seems to be saying that a unique _immutable_ value is implicitly convertible to mutable.
Feb 09 2015
parent Kenji Hara via Digitalmars-d <digitalmars-d puremagic.com> writes:
2015-02-10 0:15 GMT+09:00 via Digitalmars-d <digitalmars-d puremagic.com>:

 On Monday, 9 February 2015 at 13:19:24 UTC, Tobias Pankrath wrote:

 On Monday, 9 February 2015 at 12:39:06 UTC, Marc Sch=C3=BCtz wrote:

 It does? Not according to my tests. And it would be bad if it did,
 because the returned immutable value could actually reside in read-only
 memory.
join returns a mutable array, that can be implicitly casted to immutable if join is pure.
I'm talking about the general case. Jacob seems to be saying that a uniqu=
e
 _immutable_ value is implicitly convertible to mutable.
In general, pure function can access immutable global data, and can return its address. Therefore, if a pure function returns immutable data, the returned pointer/reference to immutable a data should not be convertible to mutable. Kenji Hara
Feb 09 2015
prev sibling parent "Jakob Ovrum" <jakobovrum gmail.com> writes:
On Monday, 9 February 2015 at 11:38:23 UTC, Jakob Ovrum wrote:
 On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Err, implicitly convertible to *mutable*. It goes both ways.
Sorry, this is indeed nonsense, I take it back. It only goes one way.
Feb 09 2015
prev sibling parent "Dicebot" <public dicebot.lv> writes:
On Monday, 9 February 2015 at 11:37:23 UTC, Jakob Ovrum wrote:
 std.array.join is strongly pure (with appropriate template 
 arguments), so its return value is implicitly convertible to 
 immutable.
Ahha, I have missed that part. Thanks!
Feb 09 2015
prev sibling parent reply "deadalnix" <deadalnix gmail.com> writes:
On Monday, 9 February 2015 at 10:56:31 UTC, Dicebot wrote:
 Doing a lot of porting from D1 to D2 recently I have realized 
 that we still don't have any idiomatic way to express function 
 result types that can be both mutable and immutable.

 Consider this trivial snippet:

 ```D
 import std.array : join;

 void main()
 {
 	auto s = join([ "aaa", "bbb", "ccc" ]);
 	pragma(msg, typeof(s));
 }
 ```

 It outputs "string" which stands for immutable buffer. However, 
 actual allocated buffer is not yet truly immutable - it has 
 just been allocated to hold the result of join algorithm and 
 may be interpreted both as mutable and immutable, depending on 
 caller desire.

 In this specific case if caller wanted to get a mutable buffer 
 instead, it would need to do cast from immutable which is very 
 dangerous - especially considering implementation of `join` may 
 change. And doing .dup on buffer that has just been allocated 
 is quite a waste.

 Right now I am leaning towards personal convention to always 
 return mutable or const buffers and do assumeUnique at caller 
 side where necessary. But this does, of course, suck.

 What is current conventional wisdom on topic? Can we probably 
 start using std.typecons.Unique for such functions?
Someone is starting to see where I'm getting at when I'm pushing for owned...
Feb 09 2015
parent reply "Dicebot" <public dicebot.lv> writes:
On Monday, 9 February 2015 at 23:40:31 UTC, deadalnix wrote:
 Someone is starting to see where I'm getting at when I'm 
 pushing for owned...
I don't think this specific case is a good justification for your proposal - it is simple enough to accept a library based solution. We need more accumulated anecdotal evidence.
Feb 11 2015
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 11 February 2015 at 13:54:12 UTC, Dicebot wrote:
 On Monday, 9 February 2015 at 23:40:31 UTC, deadalnix wrote:
 Someone is starting to see where I'm getting at when I'm 
 pushing for owned...
I don't think this specific case is a good justification for your proposal - it is simple enough to accept a library based solution. We need more accumulated anecdotal evidence.
We do have. That is simply one more. On the top of my head: - Plug hole in the type system (implicit sharing). - Use the guarantee the type system is supposed to provide to get a better GC. - Construction of immutable objects. - make nogc actually useful for something else than trivias (throw, defers usage of GC to the caller). - make std.concurency and std.parallelism safe end to end. - Be able to lock tree of objects. - Generating less garbage overall. - ...
Feb 11 2015
parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 11 February 2015 at 18:37:42 UTC, deadalnix wrote:
 On Wednesday, 11 February 2015 at 13:54:12 UTC, Dicebot wrote:
 On Monday, 9 February 2015 at 23:40:31 UTC, deadalnix wrote:
 Someone is starting to see where I'm getting at when I'm 
 pushing for owned...
I don't think this specific case is a good justification for your proposal - it is simple enough to accept a library based solution. We need more accumulated anecdotal evidence.
We do have. That is simply one more. On the top of my head: - Plug hole in the type system (implicit sharing). - Use the guarantee the type system is supposed to provide to get a better GC. - Construction of immutable objects. - make nogc actually useful for something else than trivias (throw, defers usage of GC to the caller). - make std.concurency and std.parallelism safe end to end. - Be able to lock tree of objects. - Generating less garbage overall. - ...
Or we can add an easier hack for each of these. That is gonna scale well.
Feb 11 2015