equivariant functions

Bill Baxter wbaxter at gmail.com
Tue Oct 14 19:08:53 PDT 2008


On Wed, Oct 15, 2008 at 10:13 AM, Steven Schveighoffer
<schveiguy at yahoo.com> wrote:
> "Bill Baxter" wrote
>> I'm looking at all these funky syntaxes with their many unusual
>> implicit assumptions that they require a user to keep in mind, and
>> thinking it would all be a lot simpler if you could just declare some
>> template-like meta-variables in a preamble to the function.
>>
>> I also don't like the looks of using a symbol in the return type that
>> doesn't get declared till you see the parameter list.
>>
>> So how about introducing a "typedef block" where you can introduce
>> metatypes and give them constraints.
>>
>> For constness:
>>
>> typedef {  Const : const }  // in this case means any kind of const
>> Const(int) someFunc(Const(int) z) { ... }
>>
>> For Objects:
>>
>> class BaseClass {}
>> class DerivedClass : BaseClass {}
>>
>> typedef { DType : BaseClass } // DType is anything passing  is(DType :
>> BaseClass)
>> DType someFunc(DType input)
>>
>> For both:
>>
>> typedef {
>>  DType : BaseClass;
>>  Const : const
>> }
>> Const(DType) someFunc(Const(DType) input)
>>
>> And with something like this you can also come up with some solution
>> to the min problem
>> typedef{
>>   ConstA : const;
>>   ConstB : const;
>>   ConstC = maxConst!(ConstA,ConstB)
>> }
>> ConstC(Type) max(ConstA(Type), ConstB(Type))
>>
>> maxConst would be some template-like thing func to take the "more
>> const" of the two qualifiers.  Not sure how you'd implement that.
>>
>> Lots o hole's there, but the basic idea I'm suggesting is to declare
>> the types and qualifiers that can vary in some sort of a preamble
>> block.  To me this looks like it will be much more readable and easier
>> to maintain than any of these funky syntaxes with lots of implicit
>> rules to remember.
>
> 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.

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.

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.

> 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.

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);


---

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?:

  const(X) getValue(const_iterator iter);
  X getValue(iterator iter);

I guess I can answer that myself.  If you wrote an apropriate
IterType!(T) template then maybe with your syntax you could say:

   inout(X) getValue(IterType!(inout(X)) iter);

and with my suggestion it would be something like

  typedef {Const : const }
  Const(X) getValue(IterType!(Const(X)) iter);

Ok, so I guess that's handled OK.

--bb



More information about the Digitalmars-d mailing list