equivariant functions
Andrei Alexandrescu
SeeWebsiteForEmail at erdani.org
Wed Oct 15 06:57:12 PDT 2008
Benji Smith wrote:
> Andrei Alexandrescu wrote:
>> Benji Smith wrote:
>>> I really don't see what all the fuss is about with the equivariance
>>> stuff, except that it introduces a lot of confusing rules to fix the
>>> holes in the (already complex and confusing) const system.
>>
>> Please stop perpetuating misunderstanding and apprehension about the
>> const system through statements strong on opinion and vague on detail.
>> If you're willing to mention holes, complexity, and confusion about
>> the const system, please explain where the holes are, where the
>> complexities lie, and what aspects you are confused about. Suggestions
>> for fixing said issues would also be welcome.
>
> Fair enough. Here are the reasons I describe the const system as "complex":
I see where you're coming from. If your opinion is that the const system
does not bring enough benefits to justify its learning, you are of
course entitled to it. But talking about complexity implies that there
would be simpler ways to achieve its charter that we missed, and talking
about holes that we made mistakes of principle that go beyond bugs in
the compiler or incompleteness of implementation.
> * Comparable code without const qualifiers contains quantatitively fewer
> semantic constructs than code that uses those qualifiers. Smaller
> function prototypes are easier to read and comprehend:
>
> char[] join(char[] str2, char[] str2) { ... }
>
> const(char)[] join(const(char)[] str1, const(char)[] str1) { ... }
Complexity should be judged in proportion to the goal achieved. Of
course an AM radio will be less complex than an AM/FM radio; that does
not reveal an intrinsic problem with the latter. And the more complex
signature tells you that join will not change its arguments. As Gide
mentioned, one source of bugs in a D program is that attention must be
paid to duplicating aliased slices properly. That wouldn't be bad if
such mistakes were easily detected. Unfortunately, forgetting to call
.dup causes long-distance coupling that make changing one piece of data
in one place influence the change in an unrelated part, just because
they share some history.
> * Nestable const type constructors require me to keep a more detailed
> mental model of the type system. Am I using a "const(char)[]" or a
> "const(char[])"?
You don't need to keep a more detailed mental model of the type system -
whatever that is :o). On the contrary, const allows you to free your
mind of many details about the data flow in your application. If you
make a mistake regarding const(char)[] vs. const(char[]), the compiler
will tell you about it, not one night of debugging.
> * Remembering the difference between "const" and "invariant" requires
> more mental energy than using a system (D1) where such distinctions
> don't exist.
I think this reflects a misunderstanding of what constness is supposed
to do for you. It's exactly the contrary. Invariant reduces the amount
of mental energy you need to expend. I think you owe it to yourself to
use D2. You will see that using invariant strings is a huge improvement
and it alone is worth introducing constness. Literally all bugs caused
by undue aliasing disappear if you use string. You will see that the net
result is that your programs are easier to write and understand, not harder.
> * The idea that "const" is a supertype of "mutable" and "invariant" is
> utterly bizarre to me, since it violates the "is a" rule of type
> hierarchies. I understand that, for the sake of overloading, both
> "mutable" and "invariant" can be implicitly cast to "const", but the
> other implications of that relationship are beyond my grasp at the moment.
You may want to reframe this as an opportunity to improve your
understanding of subtyping. I recommend a classic paper by Cardelli and
Wegner "On understanding Types, Data Abstraction, and Polymorphism"
(http://lucacardelli.name/Papers/OnUnderstanding.A4.pdf). It's a
classic. Reading through section 1.5 does not require much background
and is very rewarding.
That's not to say using const and invariant requires formal background.
Most people would just use them for their benefits and have them just
work. But if you want to lift the hood and see exactly why they work,
you have to get your hands greasy a bit.
> * The transitivity of const qualifiers requires me to consider not just
> the object I'm passing around, but the references to other objects
> contained within the const-qualified object.
This is backwards, too. If const didn't exist or weren't transitive,
you'd be required to keep in mind for mutation and aliasing bugs not
just the object you're passing around, but the references to other objects.
> * Both "const" and "invariant" can apply to value-type objects. But
> "const" doesn't make any sense in that context, so in my mind I have to
> use the "invariant" concept instead whenever applying the concept to a
> primitive value.
Of course it does make sense. The address of a const int has a different
type than the address of a mutable int. Again the pattern is that you
have less, not more to keep in mind. The type "keeps it in its mind" for
you.
> * The equivariance proposal, at least in some of its forms, requires me
> to imagine the existence of implicit typedefs at the function-call site,
> and to know that the declared type in my method signature might not be
> the actual type of the arguments received by the function.
Not at all. On this group many people have high competency and willing
to discuss the most minute implementation details of a language feature,
and my understanding is that you also engage in such discussions. On
occasion, therefore, there will be discussion on exactly how sausages
are being made. Do not confuse those discussions with things that
complicate the experience of eating sausages.
Much of the problems caused by const equivariance is just that Walter is
leery of implementing aliases bound to qualifiers. If he would, there
would be no problem to talk about. The issues with const equivariance,
in that case, would be exactly the same as with supporting equivariance
for any types in general.
> The "holes" I described in the const system are the symptoms being
> addressed by the equivariance proposal.
You did not describe any hole above. In fact, on first reading, at this
point I was expecting you'd start describing the actual holes that I
know about - e.g. the impossibility of creating invariant objects
without a cast. (That will be fixed.)
> Without equivariance, the const
> system sometimes requires verbatim duplication of code within function
> bodies whose only difference is their signature. Without the const
> system, the equivariance proposal wouldn't be necessary.
No. Without equivariance, you'd be required to duplicate bodies of
functions that need it, such as clone. It is true that const makes that
need more frequent, but that does not reveal a problem intrinsic to it
or a mistake in defining it.
> And the reason I describe the const system as "confusing"?
>
> Because it confuses me.
This is a fair point. One problem is that right now no good description
of const exists. Worse, trying to infer it from discussions on this
group is bound to scare one away because here mostly problems are
discussed.
> I understand some of the allure of having a robust, provably correct
> const system. Automatic memoization would be cool. Automatic parallel
> execution would be nice. And, in theory, it'd be nice to avoid errors
> that occur when a piece of code modifies a piece of data that it shouldn't.
>
> But in my experience (using languages that lack any sort of const
> system, except for enum-style declarations), it just doesn't matter
> much. When I need memoization or parallel execution, I write the code
> explicitly. And when I need an immutable object, I design its interface
> to make external mutation impossible (see also: java.lang.String).
There's been a pretty incredible resurgence in interest in functional
programming languages. At ACCU 2008 (www.accu.org), previously a
"no-quarters" hardcore C++ conference, who do you think gave keynotes
and talks? Simon Peyton-Jones, the inventor of Haskell, and Joe
Armstrong, inventor of Erlang. To better understand the size of that
change, you must browse through previous years' conference programs.
Only 2-3 years ago that very concept would have been unbelievable. ACCU
stands for Association of C and C++ Users! Use of Haskell and Erlang is
increasing exponentially. Even mainstream companies started using them.
Others (big, big companies that shall not be named) are designing their
own languages. And guess what - they _all_ have a notion of immutability.
What do functional languages have? Do you think people like them for the
monads? It's the immutability. They've been embracing and dealing with
it for years. And that gives them a ready answer to today's 2000-lbs
pink elephant in the room - the manycores. Having made a resolute step
towards classic, deadlock-oriented programming, Java is now in the
unpleasant position of having invested it money in the wrong stock, just
like it did with UTF-16. So when people think manycores, they look away
from Java.
> It's indisputable that the D2 const system is measurably more complex
> than the D1 const system.
>
> For me, the cost of that complexity significantly outweighs the benefit.
It is fair to hold such a belief. I think it would be helpful to
reevaluate it in wake of the above.
Andrei
More information about the Digitalmars-d
mailing list