equivariant functions

Steven Schveighoffer schveiguy at yahoo.com
Wed Oct 15 08:46:51 PDT 2008


"Bill Baxter"wrote
> On Wed, Oct 15, 2008 at 10:13 AM, Steven Schveighoffer
>> I'm not a fan of complexity where it is not required.
>>
>> You have outlined pretty much exactly the only use cases for this 
>> feature.
>> So why do you want to continually write large typedefs that are identical 
>> in
>> front of all your functions, when you can do so with a succinct syntax?
>
> Think of it this way: right now to create variations on const you have
> to declare the method three times.  This would reduce that to one
> short preamble + one declaration.

An example class with 10 accessors (forget about the implementations):

Your way:

class Owner
{
   typedef {  Const : const }
   Const(A) a() Const {...}

   typedef {  Const : const }
   Const(B) b() Const {...}

   typedef {  Const : const }
   Const(C) c() Const {...}

   typedef {  Const : const }
   Const(D) d() Const {...}

   typedef {  Const : const }
   Const(E) e() Const {...}

   typedef {  Const : const }
   Const(F) f() Const {...}

   typedef {  Const : const }
   Const(G) g() Const {...}

   typedef {  Const : const }
   Const(H) h() Const {...}

   typedef {  Const : const }
   Const(I) i() Const {...}

   // written by another author who prefers a different identifier for Const
   typedef {  Konst : const }
   Konst(J) j() Konst {...}
}

My way:

class Owner
{
   inout(A) a() inout {...}

   inout(B) b() inout{...}

   inout(C) c() inout{...}

   inout(D) d() inout{...}

   inout(E) e() inout{...}

   inout(F) f() inout{...}

   inout(G) g() inout{...}

   inout(H) h() inout{...}

   inout(I) i() inout{...}

   inout(J) j() inout{...}
}

It might be just me, but my way seems cleaner and easier to implement.

>
> And note that C++ gets along fine without any such assistance.  It is
> far from "all your functions" that require such handling, that's why
> it doesn't kill C++ not to have it.

But C++ doesn't have transitive const.  It is my experience that most C++ 
classes are not implemented as const-aware, or are but implemented const 
aware incorrectly.  Including classes I wrote.  I think Walter is right when 
he says that C++ const is a mess.

> For returning the input type, it has been commented "do we even really
> need this?" since most other popular languages don't have such a
> thing, and it is generally not a "buzz feature" you hear about much
> (unlike closures or lambdas, etc).  So I don't think its use will be
> that common, thus a little more verbose syntax will not be an undue
> burden.  Indeed, precisely because it will not be used frequently, it
> *should* have a more verbose, more explicit syntax.  So that on those
> occasions when it is used it will A) not sneak past the casual
> observer's notice and B) have at least some chance that the meaning of
> the code can be guessed without having to look it up.

It is not something I really am pushing.  My main concern is the const 
variance.  Something like this makes const so much more appealing (remember, 
no other languages have tried transitive const).  However, with an 
additional feature that requires all derived classes to re-implement a 
function, this can be more useful than just 'return the input value'.  Think 
clone().

As far as requiring a clumsy syntax, especially where changing style is 
possible but has no real affect on the result (i.e. using Konst instead of 
Const), I don't see that as a benefit.

>> To put the use cases in more english descriptions:
>>
>> 1. I want the constancy of my return to be dependent on the constancy of 
>> the
>> argument at the call site.
>> 2. I want the constancy of my return to be the greatest common constancy 
>> of
>> multiple arguments at the call site.
>> 3. The return type will be exactly the same value that I passed in to the
>> function, so I can use it for call chaining.  So it's OK to auto cast to 
>> the
>> most derived type.
>> 4. I want 1 and 3
>>
>> Where greatest common constancy is defined as const if any arguments 
>> differ
>> in constancy.  If they don't differ it is defined as the constancy of the
>> arguments.
>>
>> My biggest concern is 1 and 2, since otherwise for 1, we are left with 3
>> different versions of the same function, and for 2, we are left with 3^n
>> different versions.
>>
>> 3 (and 4) are of mild concern, because you are only forced to redefine 
>> once
>> per class, and it's not as common a problem, since not all classes 
>> require
>> handling of call chaining, whereas all classes should be const-aware.
>>
>> I'll reiterate my proposal:
>>
>> inout(X) f(inout(Y) y, inout(Z) z)
>>
>> where inout means 'the greatest common constancy of all variables that 
>> are
>> declared inout'.  Again, not in love with inout, but inout kind of reads
>> 'you get out what you put in', and it is a 'free' keyword.
>>
>> and for 3:
>>
>> X f(return X x);
>>
>> where return means 'this function will return x'.  The return statements 
>> in
>> this function are all required to return the value of x.  (x cannot be
>> rebindable inside the function).
>
> That does sound reasonable for the use cases you've outlined. I hadn't
> realized you were making "inout" mean "maximal const".   I can't think
> of any use case for non-maximal const of the top of my head so aside
> from the "inout" being not a great word, I think I could live with
> this ... unless someone discovers another use case.

I'm not super fond of inout, but it allows not adding a new keyword.  Some 
view that as an essential part of this.

>
> If inout is useless now then we could retire it and add a new
> "anyconst" keyword.  Or "vconst" for "variable/various/varying/virtual
> const"
>
>  vconst(X) f(vconst(Y) y, vconst(Z) z)
>
> Or stick some syntax on it.  Like "const~", the squiggle indicating
> "similar to".  Or "const?".
>
>  const?(X) f(const?(Y) y, const?(Z) z);

All these sound like valid alternatives, I'd be fine with any of them.

> Here's something that comes up -- iterators in C++ usually end up
> needing to come in const and non-const flavors.  The "head" of both is
> mutable, but the "tail" is const on the const flavor.  How do you
> write this pair of functions as one?:

You can't cast an iterator to 'head const' (another nagging problem, but a 
separate one), so the only viable option is templates.

-Steve 





More information about the Digitalmars-d mailing list