Re: D safety! New Feature?

2016-08-06 Thread Chris Wright via Digitalmars-d
On Sat, 06 Aug 2016 07:56:29 +0200, ag0aep6g wrote:

> On 08/06/2016 03:38 AM, Chris Wright wrote:
>> Some reflection stuff is a bit inconvenient:
>>
>> class A {
>>   int foo() { return 1; }
>> }
>>
>> void main() {
>>   auto a = new immutable(A);
>>   // This passes:
>>   static assert(is(typeof(a.foo)));
>>   // This doesn't:
>>   static assert(__traits(compiles, () { a.foo; }));
>> }
>>
>> __traits(compiles) is mostly an evil hack, but things like this require
>> its use.
> 
> The two are not equivalent, though. The first one checks the type of the
> method.

Which is a bit awkward because in no other context is it possible to 
mention a raw method. You invoke it or you get a delegate to it.

And if you try to get a delegate explicitly to avoid this, you run into 
https://issues.dlang.org/show_bug.cgi?id=1983 .

It's also a bit awkward for a method to be reported to exist with no way 
to call it. While you can do the same with private methods, you get a 
warning:

a.d(7): Deprecation: b.B.priv is not visible from module a

Which implies that this will become illegal at some point and fail to 
compile.


Re: D safety! New Feature?

2016-08-06 Thread Timon Gehr via Digitalmars-d

On 06.08.2016 07:56, ag0aep6g wrote:


Add parentheses to the typeof one and it fails as expected:

static assert(is(typeof(a.foo(; /* fails */

Can also do the function literal thing you did in the __traits one:

static assert(is(typeof(() { a.foo; }))); /* fails */


You can, but don't.

class A {
int foo() { return 1; }
}

void main() {
auto a = new A;
static void gotcha(){
// This passes:
static assert(is(typeof((){a.foo;})));
// This doesn't:
static assert(__traits(compiles,(){a.foo;}));
}
}



Re: D safety! New Feature?

2016-08-05 Thread ag0aep6g via Digitalmars-d

On 08/06/2016 03:57 AM, Mark J Twain wrote:

On Friday, 5 August 2016 at 21:12:06 UTC, ag0aep6g wrote:

[...]


struct MutableSomething
{
int value;
void mutate(int newValue) { value = newValue; }
}

struct ImmutableSomething
{
int value;
/* no mutate method here */
}

void main()
{
auto i = ImmutableSomething(1);
(cast(MutableSomething) i).mutate(2);
import std.stdio;
writeln(i.value); /* prints "2" */
}



wow, that seems like a huge design issues.

Change one value to double though and it won't work. It's not general
and seems to be a design flaw. It is not proof of anything in this case
though. Why? Because an Queue might not have the same size as an
ImmutableQueue and your "proof" only works when they are the same size.


You can cast between structs of different sizes. Just need to add some 
magical symbols:



struct MutableSomething
{
float value;
void mutate(int newValue) { value = newValue; }
float somethingElse;
}

struct ImmutableSomething
{
int value;
/* no mutate method here */
}

void main()
{
auto i = ImmutableSomething(1);
(* cast(MutableSomething*) &i).mutate(2);
import std.stdio;
writeln(i.value); /* prints "2" */
}


You can cast anything to anything this way. It's absolutely unsafe, of 
course, just like casting away immutable and then mutating.


Also, MutableFoo and ImmutableFoo would usually have the same fields, 
wouldn't they?


[...]

What kind of example? I have already given examples and proved that
there is a functional difference.


The first example you gave was ImmutableArray. You later said it was a 
bad example, and I should disregard it. You also said something about 
caching allocations. I'm still not sure how that's supposed to work 
differently than with an ordinary array/object/whatever. If you uphold 
that point, I would appreciate an example in code that shows how it works.


Then you said that ImmutableQueue would have better errors than 
immutable Queue. That's wrong. Again, I'd appreciate a code example that 
shows the better errors, if you think you were correct there.


The next point was that one can cast immutable away, but they can't cast 
ImmutableFoo to MutableFoo. That's wrong, too. Casting immutable away 
and then mutating has undefined behavior, i.e. it's not allowed. And you 
can cast between unrelated types just fine.


One thing that's true is that an uncallable method is slightly different 
from one that's not there at all. Chris Wright gave an example where 
this would make it a tiny bit harder to mess up. But I think that's a 
rather minor point. And it has to be weighed against the downsides of 
ImmutableFoo, like the added complexity and it being more of an eyesore 
than immutable Foo. If there's more to this, a code example would do 
wonders.


Those are the points/examples that I'm aware of. I don't see much in 
favor of ImmutableFoo. If I missed anything, please point it out. I'm 
repeating myself, but code examples would be great then.



I will not continue this conversation
because you refuse accept that. First you haven't given your criteria
for me to prove anything to you to satisfy whatever it is you want.


I'm not asking for (strict mathematical) proof. Just some example that 
highlights the pros of ImmutableFoo over immutable Foo. Can be 
subjective, like "See? This looks much nicer with my version". So far I 
just don't see any reason to use it over the immutable keyword.



Second, I'm not here to waste my time trying to prove the merits of the
tool. Again, either use it if you feel like it does something or don't.
The method is not flawed in and of itself. It works(if it didn't,
someone would have probably already shown it not to work).

All you can do is assert a negative, which you can't prove yourself.


I'm not disputing that it works. (Neither am I saying that it does work 
correctly.) Assuming that it works just fine, it apparently does almost 
exactly what immutable does, just in a more cumbersome way.



So we end up chasing each other tails, which I refuse to do.


I don't think the discussion has been that way so far. You've made some 
points where you were mistaken about how immutable and casting work. So 
they fell flat. You can defend them (point out errors in my refusals), 
or you can bring up other points. You don't have to, of course, but I 
don't think we have to agree to disagree just yet.


I don't know why you refuse to give code examples. If you're using this 
thing, you have something lying around, no? Some code that shows the 
benefits of your approach would shut me up. I'd try for sure to show 
that it works similarly well with plain immutable, though ;)



If you want more proof then it is up to you, not me. I have my proof and
it is good enough for me.


Sure, go ahead. But it seems to me that you're basing this on wrong 
impressions of immutable and casts.


Re: D safety! New Feature?

2016-08-05 Thread ag0aep6g via Digitalmars-d

On 08/06/2016 03:38 AM, Chris Wright wrote:

Some reflection stuff is a bit inconvenient:

class A {
  int foo() { return 1; }
}

void main() {
  auto a = new immutable(A);
  // This passes:
  static assert(is(typeof(a.foo)));
  // This doesn't:
  static assert(__traits(compiles, () { a.foo; }));
}

__traits(compiles) is mostly an evil hack, but things like this require
its use.


The two are not equivalent, though. The first one checks the type of the 
method.


__traits(compiles, ...) also passes when used that way:

static assert(__traits(compiles, a.foo)); /* passes */

Add parentheses to the typeof one and it fails as expected:

static assert(is(typeof(a.foo(; /* fails */

Can also do the function literal thing you did in the __traits one:

static assert(is(typeof(() { a.foo; }))); /* fails */

If the method wasn't there at all, they would all fail, of course. So 
the programmer couldn't mess this up as easily. I don't think this 
outweighs the inconvenience of bending over backwards to avoid 
immutable, though.


Re: D safety! New Feature?

2016-08-05 Thread Chris Wright via Digitalmars-d
On Sat, 06 Aug 2016 01:57:50 +, Mark "J" Twain wrote:
> wow, that seems like a huge design issues.

In practice, it's not a problem. In a language supporting low-level 
operations, you do sometimes need a reinterpret_cast. And since there's 
no other useful cast you can do with a D struct...

The casting rules *are* moderately complex, though, in order to do the 
most useful thing at all times.

> Change one value to double though and it won't work.

With your own example of MutableArray and ImmutableArray, the two structs 
need the same fields. You even started out with a mixin to define common 
fields.

>> [...]
>>> What I would say to you is, either try it on some examples, or don't.
>>
>> I'm asking you for an example, because I don't see the point of it.
> 
> What kind of example?

An example demonstrating clearly the difference between this design 
pattern immutability and the already defined language construct. This 
example should demonstrate that you know how immutability works in D 
today.

You haven't demonstrated much knowledge of D so far in this thread. 
Specifically:

* You think a function can trivially reuse memory across invocations, but 
only if manually released, and releasing memory allows it to be stack-
allocated. This ignores garbage collection, recursion, memory safety, how 
the stack works, and how to make this feature practical to implement.
* You use a 'global' keyword.
* You are using a nonstandard code style.
* You misspelled `scope(exit)`.
* You believed that you can call on an immutable object methods that are 
not marked immutable, and that the compiler would produce an error at the 
site of mutation, which would be quite impossible. Consider the following 
code:

extern(D) void foo(MyObject o);
void main() {
  foo(new immutable(MyObject));
}

With your idea of how immutability is enforced, the compiler wouldn't 
know whether the code is valid or not.

> I have my proof and it is good enough for me.

But you posted here to suggest that people use this pattern. You went so 
far as to hint "compiler support would make this nice". And then when 
people pointed out that there's already compiler support for this and 
it's better than what you suggested, you argued with them!

If you had said that you had looked at the builtin const/immutable and 
still preferred your model, well, it's your code. But instead you tried 
to sell us on your idea.


Re: D safety! New Feature?

2016-08-05 Thread Mark J Twain via Digitalmars-d

On Friday, 5 August 2016 at 21:12:06 UTC, ag0aep6g wrote:

On 08/05/2016 09:39 PM, Mark J Twain wrote:

In the case of ImmutableQueue, There is no Enqueue!

See, there is a difference between "not callable" and "does 
not exists".


Ok, but what cool stuff is enabled by "does not exist" that 
doesn't work (as nicely) with "not callable"? As far as I can 
tell, there is no difference in practice.


[...]
`immutability` only logically makes something immutable, as it 
can
easily be proved. One can cast out immutable and mutate very 
easily(take

address, change values).


Casting immutable away and then mutating is not allowed by the 
language. It has undefined behavior. After doing it, the 
program may see the old value, it may see the new value, it may 
crash, or it may behave completely crazy.


Some example code that shows different outcomes of mutating 
immutable data:



void main()
{
import std.stdio;

immutable int i = 1;
* cast(int*) &i = 2;
writeln(i); /* prints "1" */
writeln(*&i); /* prints "2" */

immutable int* p = new int(3);
*(cast(int*) p) = 4;
writeln(*p); /* prints "4" */

immutable(char)[] s = "a";
(cast(char[]) s)[0] = 'b'; /* segfault on Linux */
writeln(s); /* prints "b" on Windows */
}


That's not a valid D program at all, of course. All of the 
mutations are invalid.



`Immutable` cannot be cast because there is no
type relationship.


You can cast between completely unrelated types no problem:


struct MutableSomething
{
int value;
void mutate(int newValue) { value = newValue; }
}

struct ImmutableSomething
{
int value;
/* no mutate method here */
}

void main()
{
auto i = ImmutableSomething(1);
(cast(MutableSomething) i).mutate(2);
import std.stdio;
writeln(i.value); /* prints "2" */
}



wow, that seems like a huge design issues.

Change one value to double though and it won't work. It's not 
general and seems to be a design flaw. It is not proof of 
anything in this case though. Why? Because an Queue might not 
have the same size as an ImmutableQueue and your "proof" only 
works when they are the same size.




[...]
What I would say to you is, either try it on some examples, or 
don't.


I'm asking you for an example, because I don't see the point of 
it.


What kind of example? I have already given examples and proved 
that there is a functional difference. I will not continue this 
conversation because you refuse accept that. First you haven't 
given your criteria for me to prove anything to you to satisfy 
whatever it is you want. Second, I'm not here to waste my time 
trying to prove the merits of the tool. Again, either use it if 
you feel like it does something or don't. The method is not 
flawed in and of itself. It works(if it didn't, someone would 
have probably already shown it not to work).


All you can do is assert a negative, which you can't prove 
yourself. So we end up chasing each other tails, which I refuse 
to do. If you want more proof then it is up to you, not me. I 
have my proof and it is good enough for me.








Re: D safety! New Feature?

2016-08-05 Thread Chris Wright via Digitalmars-d
On Fri, 05 Aug 2016 23:12:06 +0200, ag0aep6g wrote:

> On 08/05/2016 09:39 PM, Mark J Twain wrote:
>> In the case of ImmutableQueue, There is no Enqueue!
>>
>> See, there is a difference between "not callable" and "does not
>> exists".
> 
> Ok, but what cool stuff is enabled by "does not exist" that doesn't work
> (as nicely) with "not callable"? As far as I can tell, there is no
> difference in practice.

Some reflection stuff is a bit inconvenient:

class A {
  int foo() { return 1; }
}

void main() {
  auto a = new immutable(A);
  // This passes:
  static assert(is(typeof(a.foo)));
  // This doesn't:
  static assert(__traits(compiles, () { a.foo; }));
}

__traits(compiles) is mostly an evil hack, but things like this require 
its use.


Re: D safety! New Feature?

2016-08-05 Thread ag0aep6g via Digitalmars-d

On 08/05/2016 09:39 PM, Mark J Twain wrote:

In the case of ImmutableQueue, There is no Enqueue!

See, there is a difference between "not callable" and "does not exists".


Ok, but what cool stuff is enabled by "does not exist" that doesn't work 
(as nicely) with "not callable"? As far as I can tell, there is no 
difference in practice.


[...]

`immutability` only logically makes something immutable, as it can
easily be proved. One can cast out immutable and mutate very easily(take
address, change values).


Casting immutable away and then mutating is not allowed by the language. 
It has undefined behavior. After doing it, the program may see the old 
value, it may see the new value, it may crash, or it may behave 
completely crazy.


Some example code that shows different outcomes of mutating immutable data:


void main()
{
import std.stdio;

immutable int i = 1;
* cast(int*) &i = 2;
writeln(i); /* prints "1" */
writeln(*&i); /* prints "2" */

immutable int* p = new int(3);
*(cast(int*) p) = 4;
writeln(*p); /* prints "4" */

immutable(char)[] s = "a";
(cast(char[]) s)[0] = 'b'; /* segfault on Linux */
writeln(s); /* prints "b" on Windows */
}


That's not a valid D program at all, of course. All of the mutations are 
invalid.



`Immutable` cannot be cast because there is no
type relationship.


You can cast between completely unrelated types no problem:


struct MutableSomething
{
int value;
void mutate(int newValue) { value = newValue; }
}

struct ImmutableSomething
{
int value;
/* no mutate method here */
}

void main()
{
auto i = ImmutableSomething(1);
(cast(MutableSomething) i).mutate(2);
import std.stdio;
writeln(i.value); /* prints "2" */
}


[...]

What I would say to you is, either try it on some examples, or don't.


I'm asking you for an example, because I don't see the point of it.


I'm not trying to take away your favorite blanket... just present
another tool for solving some problems. If you don't like it, that's
fine, don't use it. Don't get upset, it won't affect your world if you
don't want it to. For those that see some use out of it, use it, else
don't. Simple as that.


I cannot see any benefit in using your tool rather than plain immutable, 
which is why I'm asking for an example that shows the benefit. By now, I 
think there just isn't any.


I think you have (or had) some misconceptions about immutable and 
casting in D, so you came up with something that's supposed to be 
stronger. But D's immutable already seems to be as strong as your construct.


D already rejects calling a mutating method on an immutable object, and 
it also disallows casting immutable away and then mutating. Breaking 
things with a cast is always possible, even with your unrelated types.


Please don't take this personally. I don't mean to put you down. I just 
think your ImmutableFoo thingy doesn't buy you anything over immutable Foo.


Re: D safety! New Feature?

2016-08-05 Thread Mark J Twain via Digitalmars-d

On Thursday, 4 August 2016 at 18:58:18 UTC, ag0aep6g wrote:

On 08/04/2016 08:22 PM, Mark J Twain wrote:
The problem is that you have fixated on the *array* and not 
the general

principle. The Array was an example.


I'm having trouble understanding what you're getting at, so I'm 
trying to get it from the example you gave. If there's merit in 
your idea, then surely you can give a example where it provides 
benefit over the immutable keyword.



Get Array out of your mind and
think of them as general structures. It could be a queue.


Ok.


D has no built
in queue, then what?


You can still have an immutable queue, or a queue of immutable 
elements. Just like with arrays.



What if it is a widget, then what? Immutable Widget
vs Mutable Widget.

Marking a widget immutable is not the same as having an 
ImmutableWidget.

Can you see the difference?


No.


I assure you there is.


Please show.


The immutable keyword
only prevents data manipulation, it does not change the 
interface.


I'm still not sure what that means. An immutable object does 
not have mutating operations in its interface. A mutable object 
does. So the interfaces are different.



For
simple primitives, there is not much difference, but for 
larger complex

types, the immutable keyword doesn't cut it.

immutable Queue!int q1;
ImmutableQueue!int q2;

q1.Enqueue(x);  // Compile time error if no tricks, but the 
error is
further up the line inside Enqueue, when it actually modifies 
the data.


Not true. Since Enqueue isn't marked const or immutable, it 
can't be called on an immutable object. The compiler rejects 
the call itself. It doesn't reject the mutation that happens 
inside Enqueue, because that's perfectly fine in a non-const, 
non-immutable method.


In code:


struct Queue
{
void Enqeue(int dummy) {}
}

void main()
{
Queue m;
m.Enqeue(1);
immutable Queue i;
i.Enqeue(2); /* Error: mutable method test.Queue.Enqeue is 
not callable using a immutable object */

}


We can cast away immutability and end up defeating the purpose 
and end

up with run-time problems.


You can break everything with casts, yes.


q2.Enqueue(x);  // Compile time error, Enqueue doesn't exist in
ImmutableQueue.


It doesn't exist for an immutable Queue, either.


cannot cast away immutable.


You can still cast from ImmutableQueue to MutableQueue.


At most we can convert q2 to
a mutable class, which is a copy, then replace q2 with the 
copy.


There are difference and the second case is better. The error 
reporting
is more accurate and no casting can be done to bypass 
immutability.


We
essentially get all this stuff for free if we simply use 
templates to

build the hierarchy and separate the template in to different
parts(immutable, mutable, etc).

Now, an ImmutableQueue might not be hugely useful if we have 
no way to
access the data, but it could provide [] access. Again, don't 
get bogged
down in the specifics, I'm talking about general application 
here. The
more complex the type and hierarchy the more useful such a 
method is and

the less useful immutable keyword is.

The immutable keyword is a blind, it only does one thing. 
Building
immutability in to the type system itself allows the 
programmer to make

immutable smarter and control exactly what it does.


Sorry, but I still don't see what ImmutableWhatever does that 
`immutable Whatever` can't do. As far as I see, your example 
about having better error locations is wrong.


Ok, Simple:

immutable does not remove the interface! Regardless of how you 
are thinking about it, your exmaple, i still have Enqueue. Only 
the compiler has stopped compiling.



struct Queue
{
void Enqeue(int dummy) {}
}

void main()
{
Queue m;
m.Enqeue(1);
immutable Queue i;
i.Enqeue(2); /* Error: mutable method test.Queue.Enqeue is 
not callable using a immutable object */

}


In the case of ImmutableQueue, There is no Enqueue!

See, there is a difference between "not callable" and "does not 
exists". It seems maybe minor, and maybe it is, but immutability 
and "Immutability" are not exactly the same. I actually think 
they would work well together, and of course, a lot of overlap 
exist.


`immutability` only logically makes something immutable, as it 
can easily be proved. One can cast out immutable and mutate very 
easily(take address, change values). `Immutable` cannot be cast 
because there is no type relationship. Any time a change has to 
be made to an Immutable object, a copy is created. Of course, one 
could call this a "long winded cast", but it's more safe and 
requires more verbosity, hence less ambiguity and therefore less 
problems.


Again, there is a lot of overlap, I'm not claiming this replaces 
`immutable`. But it does things that immutable doesn't. I'm not 
even claiming it is perfect in and of itself. After all, it is 
somewhat arbitrary. One has to design the templates to be 
immutable, and if they are not then it means nothing and just 
repre

Re: D safety! New Feature?

2016-08-05 Thread H.Loom via Digitalmars-d

On Thursday, 4 August 2016 at 18:22:52 UTC, Mark "J" Twain wrote:
Building immutability in to the type system itself allows the 
programmer to make immutable smarter and control exactly what 
it does.


once again it is in the type system:

°°
struct Queue{}
alias ImmutQueue = immutable(Queue);

unittest
{
static assert(!is(Queue == ImmutQueue));
}
°°





Re: D safety! New Feature?

2016-08-05 Thread H.Loom via Digitalmars-d

On Thursday, 4 August 2016 at 18:22:52 UTC, Mark "J" Twain wrote:
Marking a widget immutable is not the same as having an 
ImmutableWidget. Can you see the difference?


Yes, you're thinking here of logical vs physical immutability.

I assure you there is. The immutable keyword only prevents data 
manipulation, it does not change the interface. For simple 
primitives, there is not much difference, but for larger 
complex types, the immutable keyword doesn't cut it.


immutable Queue!int q1;
ImmutableQueue!int q2;


I don't get you well because D has actually the ability to use 
immutable as part of the type. This feature is called the type 
constructors.


immutable Queue : attribute or storage class
immutable(Queue): part of type





Re: D safety! New Feature?

2016-08-04 Thread ag0aep6g via Digitalmars-d

On 08/04/2016 08:22 PM, Mark J Twain wrote:

The problem is that you have fixated on the *array* and not the general
principle. The Array was an example.


I'm having trouble understanding what you're getting at, so I'm trying 
to get it from the example you gave. If there's merit in your idea, then 
surely you can give a example where it provides benefit over the 
immutable keyword.



Get Array out of your mind and
think of them as general structures. It could be a queue.


Ok.


D has no built
in queue, then what?


You can still have an immutable queue, or a queue of immutable elements. 
Just like with arrays.



What if it is a widget, then what? Immutable Widget
vs Mutable Widget.

Marking a widget immutable is not the same as having an ImmutableWidget.
Can you see the difference?


No.


I assure you there is.


Please show.


The immutable keyword
only prevents data manipulation, it does not change the interface.


I'm still not sure what that means. An immutable object does not have 
mutating operations in its interface. A mutable object does. So the 
interfaces are different.



For
simple primitives, there is not much difference, but for larger complex
types, the immutable keyword doesn't cut it.

immutable Queue!int q1;
ImmutableQueue!int q2;

q1.Enqueue(x);  // Compile time error if no tricks, but the error is
further up the line inside Enqueue, when it actually modifies the data.


Not true. Since Enqueue isn't marked const or immutable, it can't be 
called on an immutable object. The compiler rejects the call itself. It 
doesn't reject the mutation that happens inside Enqueue, because that's 
perfectly fine in a non-const, non-immutable method.


In code:


struct Queue
{
void Enqeue(int dummy) {}
}

void main()
{
Queue m;
m.Enqeue(1);
immutable Queue i;
i.Enqeue(2); /* Error: mutable method test.Queue.Enqeue is not 
callable using a immutable object */

}



We can cast away immutability and end up defeating the purpose and end
up with run-time problems.


You can break everything with casts, yes.


q2.Enqueue(x);  // Compile time error, Enqueue doesn't exist in
ImmutableQueue.


It doesn't exist for an immutable Queue, either.


cannot cast away immutable.


You can still cast from ImmutableQueue to MutableQueue.


At most we can convert q2 to
a mutable class, which is a copy, then replace q2 with the copy.

There are difference and the second case is better. The error reporting
is more accurate and no casting can be done to bypass immutability.

We
essentially get all this stuff for free if we simply use templates to
build the hierarchy and separate the template in to different
parts(immutable, mutable, etc).

Now, an ImmutableQueue might not be hugely useful if we have no way to
access the data, but it could provide [] access. Again, don't get bogged
down in the specifics, I'm talking about general application here. The
more complex the type and hierarchy the more useful such a method is and
the less useful immutable keyword is.

The immutable keyword is a blind, it only does one thing. Building
immutability in to the type system itself allows the programmer to make
immutable smarter and control exactly what it does.


Sorry, but I still don't see what ImmutableWhatever does that `immutable 
Whatever` can't do. As far as I see, your example about having better 
error locations is wrong.


Re: D safety! New Feature?

2016-08-04 Thread Mark J Twain via Digitalmars-d

On Thursday, 4 August 2016 at 13:08:11 UTC, ag0aep6g wrote:

On 08/03/2016 09:33 PM, Mark J Twain wrote:
The built in array is mutable and exposes the same interface 
for the
immutable copy. The only difference is that the immutable copy 
is marked

immutable.


I don't understand. An immutable array does not let you 
overwrite elements. A mutable array does. So they have 
different interfaces, don't they?


My method changes the interface. An "immutable" type has an 
immutable

interface while a mutable type has a mutable interface.

For simple types, and value types, there is obviously no 
advantage...

their interfaces are essentially empty/DNE.


Can you show some example code where there is an advantage over 
built-in arrays?


[...]
As far as I see, "marking for reuse" is practically the same 
as
freeing here. If Data were static, there could be a 
difference.


For built-in arrays, you can mark an array for reuse by 
setting the

length to 0 and calling assumeSafeAppend, like so:


No again. Data in the example above is a local variable that 
allocates
memory that would generally be free'ed at the end of the 
function call.
Hence every call to foo results in a malloc/free pair for 
Data.  By

reusing the memory, if possible, one can potentially skip the
malloc/free.  Therefore instead of potentially thrashing the 
memory
pool, after a few runs of foo, Data would generally not 
allocate.



[...]
Your assumeSafeAppend works while the object is in existence. 
This thing

works between the existence of the object.


The allocator (e.g. the GC) is free to reuse memory it obtained 
from the operating system. So repeatedly allocating and freeing 
the same amount of memory can already be faster than one might 
think.


Of course, the compiler is also free to reuse stuff, if it's 
guaranteed that no reference escapes the function. I don't 
expect dmd to do stuff like this. ldc or gdc might.


What do Mutable/ImmutableArray enable beyond this? What do they 
do that built-in arrays don't? Again, example code would help 
doofuses like me make sense of this.


The problem is that you have fixated on the *array* and not the 
general principle. The Array was an example. Get Array out of 
your mind and think of them as general structures. It could be a 
queue. D has no built in queue, then what? What if it is a 
widget, then what? Immutable Widget vs Mutable Widget.


Marking a widget immutable is not the same as having an 
ImmutableWidget. Can you see the difference? I assure you there 
is. The immutable keyword only prevents data manipulation, it 
does not change the interface. For simple primitives, there is 
not much difference, but for larger complex types, the immutable 
keyword doesn't cut it.


immutable Queue!int q1;
ImmutableQueue!int q2;

q1.Enqueue(x);  // Compile time error if no tricks, but the error 
is further up the line inside Enqueue, when it actually modifies 
the data. We can cast away immutability and end up defeating the 
purpose and end up with run-time problems.


q2.Enqueue(x);  // Compile time error, Enqueue doesn't exist in 
ImmutableQueue. cannot cast away immutable. At most we can 
convert q2 to a mutable class, which is a copy, then replace q2 
with the copy.


There are difference and the second case is better. The error 
reporting is more accurate and no casting can be done to bypass 
immutability. We essentially get all this stuff for free if we 
simply use templates to build the hierarchy and separate the 
template in to different parts(immutable, mutable, etc).


Now, an ImmutableQueue might not be hugely useful if we have no 
way to access the data, but it could provide [] access. Again, 
don't get bogged down in the specifics, I'm talking about general 
application here. The more complex the type and hierarchy the 
more useful such a method is and the less useful immutable 
keyword is.


The immutable keyword is a blind, it only does one thing. 
Building immutability in to the type system itself allows the 
programmer to make immutable smarter and control exactly what it 
does.












Re: D safety! New Feature?

2016-08-04 Thread ag0aep6g via Digitalmars-d

On 08/03/2016 09:33 PM, Mark J Twain wrote:

The built in array is mutable and exposes the same interface for the
immutable copy. The only difference is that the immutable copy is marked
immutable.


I don't understand. An immutable array does not let you overwrite 
elements. A mutable array does. So they have different interfaces, don't 
they?



My method changes the interface. An "immutable" type has an immutable
interface while a mutable type has a mutable interface.

For simple types, and value types, there is obviously no advantage...
their interfaces are essentially empty/DNE.


Can you show some example code where there is an advantage over built-in 
arrays?


[...]

As far as I see, "marking for reuse" is practically the same as
freeing here. If Data were static, there could be a difference.

For built-in arrays, you can mark an array for reuse by setting the
length to 0 and calling assumeSafeAppend, like so:


No again. Data in the example above is a local variable that allocates
memory that would generally be free'ed at the end of the function call.
Hence every call to foo results in a malloc/free pair for Data.  By
reusing the memory, if possible, one can potentially skip the
malloc/free.  Therefore instead of potentially thrashing the memory
pool, after a few runs of foo, Data would generally not allocate.


[...]

Your assumeSafeAppend works while the object is in existence. This thing
works between the existence of the object.


The allocator (e.g. the GC) is free to reuse memory it obtained from the 
operating system. So repeatedly allocating and freeing the same amount 
of memory can already be faster than one might think.


Of course, the compiler is also free to reuse stuff, if it's guaranteed 
that no reference escapes the function. I don't expect dmd to do stuff 
like this. ldc or gdc might.


What do Mutable/ImmutableArray enable beyond this? What do they do that 
built-in arrays don't? Again, example code would help doofuses like me 
make sense of this.


Re: D safety! New Feature?

2016-08-03 Thread Mark J Twain via Digitalmars-d

On Wednesday, 3 August 2016 at 08:09:41 UTC, qznc wrote:

On Tuesday, 2 August 2016 at 21:48:58 UTC, Mark Twain wrote:

global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable 
copy of Data.

... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


I see the problem that you cannot compose this. Expand the 
example to two global arrays:


global ImmutableArray!int Data1;
global ImmutableArray!int Data2;

MutableArray!int DataCopy1 = Data1.Copy;
MutableArray!int DataCopy2 = Data2.Copy;
 ... Do work with DataCopy1 and DataCopy2 ...
Data1.Replace(DataCopy1);
// in between state is inconsistent => unsafe
Data2.Replace(DataCopy2);


I don't see this at all.  What in between state are you talking 
about?  Each object has it's own copy so no inconsistency is 
possible. If you are saying that Data1 and Data2 are suppose to 
be consistent as a pair, then your doing it wrong. You should 
combine them in to a single structure. The same problem would 
exist with other techniques. You have acted like the order of 
declaration matters but your example, given the information, can 
be written as



global ImmutableArray!int Data1;
MutableArray!int DataCopy1 = Data1.Copy;

//

Data1.Replace(DataCopy1);




global ImmutableArray!int Data2;
MutableArray!int DataCopy2 = Data2.Copy;

//

Data2.Replace(DataCopy2);








Re: D safety! New Feature?

2016-08-03 Thread Mark J Twain via Digitalmars-d

On Wednesday, 3 August 2016 at 05:44:42 UTC, ag0aep6g wrote:

On 08/02/2016 11:48 PM, Mark Twain wrote:

global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable 
copy of Data.

... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


What benefit do ImmutableArray and MutableArray have over 
built-in arrays? The thing above can be done with built-in 
arrays:



immutable(int)[] Data;
int[] DataCopy = Data.dup; // Creates a mutable copy of Data.
... Do work with DataCopy ...
Data = DataCopy.idup; // Makes a copy of DataCopy.




The built in array is mutable and exposes the same interface for 
the immutable copy. The only difference is that the immutable 
copy is marked immutable.


My method changes the interface. An "immutable" type has an 
immutable interface while a mutable type has a mutable interface.


For simple types, and value types, there is obviously no 
advantage... their interfaces are essentially empty/DNE.



[...]

void foo()
{
MutableArray!int Data;
scope(Exit) Data.Reuse();
}

Reuse can simply mark the memory reusable rather then freeing 
it. This
memory can then be reused the next time foo is called(or 
possibly use

the stack for memory).


As far as I see, "marking for reuse" is practically the same as 
freeing here. If Data were static, there could be a difference.


For built-in arrays, you can mark an array for reuse by setting 
the length to 0 and calling assumeSafeAppend, like so:


No again. Data in the example above is a local variable that 
allocates memory that would generally be free'ed at the end of 
the function call.  Hence every call to foo results in a 
malloc/free pair for Data.  By reusing the memory, if possible, 
one can potentially skip the malloc/free.  Therefore instead of 
potentially thrashing the memory pool, after a few runs of foo, 
Data would generally not allocate.


When one has hundreds of functions allocating and deallocating 
memory, this is a big deal. This is effectively caching or 
similar memory pooling.


Your assumeSafeAppend works while the object is in existence. 
This thing works between the existence of the object.





Re: D safety! New Feature?

2016-08-03 Thread qznc via Digitalmars-d

On Tuesday, 2 August 2016 at 21:48:58 UTC, Mark Twain wrote:

global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable 
copy of Data.

... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


I see the problem that you cannot compose this. Expand the 
example to two global arrays:


global ImmutableArray!int Data1;
global ImmutableArray!int Data2;

MutableArray!int DataCopy1 = Data1.Copy;
MutableArray!int DataCopy2 = Data2.Copy;
 ... Do work with DataCopy1 and DataCopy2 ...
Data1.Replace(DataCopy1);
// in between state is inconsistent => unsafe
Data2.Replace(DataCopy2);


Re: D safety! New Feature?

2016-08-02 Thread ag0aep6g via Digitalmars-d

On 08/02/2016 11:48 PM, Mark Twain wrote:

global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable copy of Data.
... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


What benefit do ImmutableArray and MutableArray have over built-in 
arrays? The thing above can be done with built-in arrays:



immutable(int)[] Data;
int[] DataCopy = Data.dup; // Creates a mutable copy of Data.
... Do work with DataCopy ...
Data = DataCopy.idup; // Makes a copy of DataCopy.


[...]

void foo()
{
MutableArray!int Data;
scope(Exit) Data.Reuse();
}

Reuse can simply mark the memory reusable rather then freeing it. This
memory can then be reused the next time foo is called(or possibly use
the stack for memory).


As far as I see, "marking for reuse" is practically the same as freeing 
here. If Data were static, there could be a difference.


For built-in arrays, you can mark an array for reuse by setting the 
length to 0 and calling assumeSafeAppend, like so:



void foo(int x)
{
static int[] Data;
scope(exit)
{
Data.length = 0;
Data.assumeSafeAppend();
}

Data ~= x;
import std.stdio: writeln;
writeln(Data, " ", Data.ptr);
}

void main()
{
foo(1);
foo(2);
}




Re: D safety! New Feature?

2016-08-02 Thread Mark J Twain via Digitalmars-d

On Tuesday, 2 August 2016 at 21:48:58 UTC, Mark Twain wrote:

[...]


Another useful feature I forgot to mention is that we can cast 
between Immutable and Muttable types.


class ImmutableTest
{
private MutableArray!int data;
public ImmutableArray!int Data;

this()
{
// Creates a view to data, but Data cannot modify data.
Data.View(data);
}

public Add(int x)
{
data.Add(x);
}
}

Data can then be exposed without worrying about it getting 
trampled on. Sense a MutableArray contains all the functionality 
of an ImmutableArray, there should be no problems with the 
interpretation.


D safety! New Feature?

2016-08-02 Thread Mark Twain via Digitalmars-d
One of the biggest problems with memory is that it's inherently 
unsafe. We know the solution to that is immutability. Immutable 
memory is inherently safe. The problem is that Immutable memory 
is useless because it cannot be changed. To make it useful and 
safe we have to cheat by creating mutable things then turning 
them in to immutable things, which is safe, but generally not the 
other way around, which is unsafe.


So, Immutable memory sort of as a direction, that if we follow 
it, we are safe, and when we go in the opposite direction, we are 
unsafe.


Now, to real world stuff. I was building some container classes 
using mixin templates like:


// An Immutable Array Template
template _ImmutableArray(T, A)
{
// Has functionality to create itself from a indexable object 
and supply accessor methods for indexing, etc. The typical array 
stuff.


}

template _MutableArray(T,A)
{
mixin _ImmutableArray!(T,A);

// Adds stuff so we can mutate the array(change it's length, 
remove elements, etc...

}

struct ImmutableArray(T)
{
mixin _ImmutableArray!(T,A);
}


struct MutableArray(T)
{
mixin _MutableArray!(T,A)
}


With such a pattern we can build up a hierarchy of templates that 
have corresponding run-time types. We can use introspection by 
having things like IsImmutable, etc. Very oop like, but with 
static constructs. On top of these we can build stacks(just a 
MutableArray), a queue(a MutableArray with Enqueue and Dequeue), 
a circular queue(a fixed array(length can't change)), and their 
immutable variants.


Now the cool thing about this, I think, is that we can do stuff 
like:


global ImmutableArray!int Data;


MutableArray!int DataCopy = Data.Copy; // Creates a mutable copy 
of Data.

... Do work with DataCopy ...
Data.Replace(DataCopy); // Makes a copy of DataCopy.


Note that Data is immutable and doesn't even have functionality 
that mutate the Data. So we know that we never mutate Data(this 
is checked at compile time). Immutable data always has it's own 
copy. The difference is that this is a weaker form of standard 
immutability. It allows us to logically separate code that 
mutates something with the code that doesn't. We can reduce 
overhead of allocating relatively simply:


void foo()
{
MutableArray!int Data;
scope(Exit) Data.Reuse();
}

Reuse can simply mark the memory reusable rather then freeing it. 
This memory can then be reused the next time foo is called(or 
possibly use the stack for memory). Utilities could find optimal 
uses for typical program behavior. e.g., foo uses on average 500 
bytes for Data. Hence allocate the capacity for Data to 500 bytes.


While compiler support would make this shine, I think the main 
benefit is that one doesn't keeps mutable functionality with 
mutable data and immutable functionality with immutable data. The 
more a program works with immutable the safer it gets. It's 
leaving the little dropping of mutable data around that create 
the hard to find bugs. It makes it easier to reason about 
code(compiler or tool support can make it even better).  
Basically all the benefits of immutability as that is all it is 
but with a bit more type logic than the keyword.


e.g., If you use intelligent and mark something immutable, You'll 
still have "access" visually to all the mutable functionality. If 
you don't use intelligent, you'll get a compile time error higher 
up the stream that is more meaningful(basically trying to access 
a member that doesn't exist in the type).