equivariant functions

Sergey Gromov snake.scaly at gmail.com
Wed Oct 15 03:57:39 PDT 2008


Tue, 14 Oct 2008 16:31:15 -0400,
Steven Schveighoffer wrote:
> Damn, I think there is some confusion here, because we are trying to solve 
> two related, but unequal problems.
> 
> First is const equivariance (is that the right term?).  I want to specify a 
> function that treats an argument as const, but does not affect the const 
> contract that the caller has with that argument (i.e. my original scoped 
> const proposal).
> 
> The second is type equivariance.  I want to specify that I will treat an 
> argument as a base type, but I will not change the derived type that the 
> caller is using.
> 
> These are similar, if you consider that const is a 'base type' of its 
> mutable or invariant version.  But there is one caveat that is hard to 
> explain using this terminology.  const is a type modifier, not a type.  So 
> it's not really a base type of a type, and it can be applied to any type in 
> existance.  Not the same as specifying a base type.

This is a very good point.  I think these cases are different, too.  And 
I think there is another confusion which leads to obscure syntaxes.

Any function argument has two types: the call-site and the function-site, 
the latter is made from the former by an implicit cast.  You must specify 
function-site type for the function body to compile properly, and you 
want to know a call-site type for equi-trickery.

But that's not all.  An equi-function should have two return types: one 
function-site for the body to compile properly, and one call-site for the 
type system to work there.

Let's experiment with an explicit syntax.  I'll define a calltype() 
built-in function similar to typeof() but yielding a call-site type in a 
function definition context.  And I'll use cast() to specify the call-
site return type of a function.

cast(calltype(s)) const(char)[] stripl(const(char)[] s);

Here cast() explicitly defines this function as a variant function.

class A { cast(calltype(this)) A clone() { return new A; } }

Now a verbose one:

cast(PassQual!(calltype(a), calltype(b))(char)[])
  const(char)[] choose(const(char)[] a, const(char)[] b, bool which);

Now that I look at this, all the magic is concentrated within the cast() 
block and is actually quite close to Bill Baxter's typedef {} proposal.  
And it seems like the call-site types are only needed there, and they're 
the only types needed.  So typeof() can be used there with modified 
semantics.  I even think that inout() can be used instead of cast() with 
pretty much the same effect on readability.  So a simple covariant 
function would look like this:

inout(typeof(s)) const(char)[] foo(const(char)[] s)
{
  return s[1..$];
}

which is translated into:

T foo(T)(T s)
{
  return cast(T) _foo_impl(s);
}
const(char)[] _foo_impl(const(char)[] s)
{
  return s[1..$];
}

Though I'd prefer cast() or var() or covar() as a covariant modifier.



More information about the Digitalmars-d mailing list