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.