equivariant functions

Steven Schveighoffer schveiguy at yahoo.com
Tue Oct 14 08:08:23 PDT 2008


"Andrei Alexandrescu" wrote
> I'm glad there's interest in equivariant functions. Let me share below how 
> I think they can be properly typechecked.
>
> The general signature of an equivariant function is:
>
> typeof(expression_involving(pk)) fun(T1 p1, ..., Tk pk, ..., Tn pn);
>
> The actual notation does not matter for the purpose of typechecking. What 
> the notation means is that fun will accept a subtype of Tk in position pk, 
> call it Uk, and will return type Uk. Of course, for that magic to happen 
> there are a few restrictions that must be met.
>
> The simplest way to typecheck fun is by substituting Tk with a typedef for 
> it:
>
> typedef Tk __Surrogate;
> typeof(expression_involving(__Surrogate.init))
> fun(T1 p1, ..., __Surrogate pk, ..., Tn pn)
> {
>    ... typecheck me ...
> }
>
> The typedef was Walter's idea; my idea involved a synthetic subtype. I 
> think the typedef has a lot of appeal. This is because the typedef of Tk 
> is almost like a subtype of Tk: it has Tk's layout trivially prefix it, 
> converts to Tk implicitly, yet it is a distinct type.
>
> Let's see this typechecking at work:
>
> 1. Accessing a field:
>
> typeof(s.ptr) at(const char[] s, uint i) { return s.ptr + i; }
>
> Typecheck with a typedef for const char[], yielding:
>
> typedef const(char[]) __Surrogate;
> typeof(__Surrogate.init.ptr) at(__Surrogate s, uint i)
> { return s.ptr + i; }
>
> Pass.
>
> 2. Returning (part of) an argument:
>
> typeof(s) stripl(const(char)[] s)
> {
>     uint i;
>     ...
>     return s[i .. $];
> }
>
> Typecheck like this:
>
> typedef const(char)[] __Surrogate;
> __Surrogate stripl(__Surrogate s)
> {
>     uint i;
>     ...
>     return s[i .. $];
> }
>
> Fail. I think that reveals a bug in the compiler. A slice of a typedef'd 
> array should return the same type as the array itself. I'll file a bug.
>
> 3. Walter's example:
>
> class Base {}
> class Derived : Base {}
>
> typeof(a) foo(Base a) { return new Base; }
>
> Typecheck like this:
>
> typedef Base __Surrogate;
> __Surrogate foo(__Surrogate a)
> {
>     return new Base;
> }
>
> Fail, as it should.
>
>
> Come at it with all you've got.
>

I think there is a problem with a simple typedef.  The problem is that all 
the fields and functions do not have an equivalent typedef, only the outer 
type has a typedef.  What you need is a 'deep' typedef, or one that typedefs 
all the contained types for all the fields/functions to ensure that a piece 
of another argument that is not protected by the typeof() construct is not 
returned.  So what we really need is a type modifier, like const.

Here is an example:

typedef const(char[]) __Surrogate;
typeof(__Surrogate.init.ptr) foo(__Surrogate s, uint i, const(char)[] s2)
{
    return s2.ptr + i;
}

In your proposed notation, this would look like:

typeof(s.ptr) foo(const(char)[] s, uint i, const(char)[] s2)

This should fail to compile, but it doesn't (tested with 2.019).  You can 
cause problems like this:

char[] test = "test".dup;
char *testptr = foo(test, 0, "TEST");

Now testptr points to the beginning of the invariant string "TEST".

But your usage of typedef is very good!  I never thought of it that way when 
designing the scoped const proposal.

BTW, I didn't say it in my other reply, but I would definitely use something 
like this.

-Steve 





More information about the Digitalmars-d mailing list