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