Dicebot on leaving D: It is anarchy driven development in all its glory.
H. S. Teoh
hsteoh at quickfur.ath.cx
Tue Aug 28 16:49:30 UTC 2018
On Mon, Aug 27, 2018 at 06:11:14PM -0700, Walter Bright via Digitalmars-d wrote:
> On 8/27/2018 10:08 AM, H. S. Teoh wrote:
> > Const in D makes sense as-is. Though, granted, its infectiousness means
> > its scope is actually very narrow, and as a result, we ironically
> > can't use it in very many places, and so its touted benefits only
> > rarely apply. :-( Which also means that it's taking up a lot of
> > language design real estate with not many benefits to show for it.
>
> D const is of great utility if you're interested in functional
> programming. Using it has forced me to rethink how I separate tasks
> into functions, and the result is for the better.
>
> I agree that D const has little utility if you try to program in C++
> style.
I am very interested in functional programming, yet ironically, one of
D's top functional programming selling points, ranged-based programming,
interacts badly with const. Just ask Jonathan about using ranges with
const, and you'll see what I mean. :-)
The very design of ranges in D requires that the range be mutable.
However, because const is infectious, this makes it a royal pain to use
in practice. Take, for example, a user-defined container type, let's
call it L. For argument's sake, let's say it's a linked list. And let's
say the list elements are reference-counted -- we'll write that as
RefCounted!Elem even though this argument isn't specific to the current
Phobos implementation of RefCounted.
As experience has shown in the past, it's usually a good idea to
separate the container from the range that iterates over it, so an
obvious API choice would be to define, say, an .opSlice method for L
that returns a range over its elements.
Now, logically speaking, iterating over L shouldn't modify it, so it
would make sense that .opSlice should be const. So we have:
struct L {
private RefCounted!Elem head, tail;
auto opSlice() const {
...
}
}
The returned range, however, must be mutable, since otherwise you
couldn't use .popFront to iterate over it (and correspondingly, Phobos
isInputRange would evaluate to false). But here's the problem: because
opSlice is declared const, that means `this` is also const, which means
this.head and this.tail are also const. But since this.head is const,
that means you couldn't do this:
auto opSlice() const {
struct Result {
RefCounted!(const(Elem)) current;
... // rest of range API
void popFront() {
current = current.next;
// Error: cannot assign const(RefCounted!Elem) to RefCounted!(const(Elem))
}
}
return Result(head); // <-- Error: cannot assign const(RefCounted!Elem) to RefCounted!(const(Elem))
}
This would have worked had we used pointers instead, because the
compiler knows that it's OK to assign const(Elem*) to const(Elem)*.
However, in this case, the compiler has no way of knowing that it is
safe to assign const(RefCounted!Elem) to RefCounted!(const(Elem)).
Indeed, they are different types, and the language currently has no way
of declaring the head-mutable construct required here.
This is only the tip of the iceberg, of course. If you then try to add
a method to RefCounted to make it convert const(RefCounted!T) to
RefCounted!(const(T)), then you'll be led down a rabbit hole of further
problems with const (e.g., how to implement ref-counting with const
objects in a way that doesn't violate the type system) until you reach
the point where it's impossible to proceed without casting away const
somehow. Unfortunately, the spec says that's Undefined Behaviour. So
you're on your own.
This is just one example among many, that const is hard to use in the
general case. It works fairly well for a narrow number of cases, such
as for built-in types, but once you start generalizing your code, you'll
find brick walls in undesired places, the workarounds for which require
so much effort as to offset any benefits that const may have brought.
TL;DR: const is beautiful in theory, but hard to use in practice. So
hard that it's often not worth the trouble, despite the benefits that it
undoubtedly does provide.
P.S. If D had the concept of head-mutable, a lot of this pain (though
not all) would have been alleviated.
T
--
"I'm running Windows '98." "Yes." "My computer isn't working now." "Yes, you already said that." -- User-Friendly
More information about the Digitalmars-d
mailing list