D safety! New Feature?
Mark J Twain via Digitalmars-d
digitalmars-d at puremagic.com
Fri Aug 5 12:39:50 PDT 2016
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
represents different types(e.g., ImmutableQueue vs Queue means
nothing unless ImmutableQueue is truly an immutable type of
Queue, which can only be designed properly designed with intent).
What I would say to you is, either try it on some examples, or
don't. 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.
More information about the Digitalmars-d
mailing list