Thread overview
Too many attributes?
Apr 25, 2008
Janice Caron
Apr 25, 2008
Yigal Chripun
Apr 25, 2008
Christian Kamm
Apr 25, 2008
Janice Caron
Apr 29, 2008
Bruno Medeiros
Apr 29, 2008
Janice Caron
Apr 30, 2008
Bruno Medeiros
April 25, 2008
On 25/04/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>  D2 will allow the following:
>  pure invariant invariant(int) func(invariant(int)) nothrow;
>  Am I the only one that thinks the above is too much?

You're not the only one.

Just for the record though, invariant(int) will implicitly cast to and from int, so the above could be simiplified to

    pure invariant int func(int) nothrow;

However, the following cannot be simplified

    pure invariant invariant(C) func(invariant(C) c) nothrow;

where C is a class. And it gets worse. Consider the following code:

    pure int func(invariant(C) a, invariant(C) b)
    {
        if (c == b) ...

Whoops! That won't compile, because - guess what!? - opEquals isn't pure!!!!

And it doesn't stop there - opEquals cannot be made pure just by changing its declaration, because a pure function must have invariant arguments, and opEquals has to work with or without invariant data. The upshot is that, eventually, Object.opEquals may have to be implemented twice:

    class Object
    {
        // the normal version
        const bool opEquals(const Object o) {...}

        // the pure version
        pure invariant bool opEquals(invariant Object o) {...}
    }

and, therefore, so will anything that overloads it.

And then there are delegates. I have mentioned in a previous thread (called something like "big hole in const system") that big problems exist because attributes can be applied to functions /but not to delegates/. That is, we cannot declare

        pure invariant invariant(C) delegate(invariant(C) c) nothrow dg;

If we take the address of func, where func is declared as

    pure invariant invariant(C) func(invariant(C) c) nothrow;

then &func will have type

    invariant(C) delegate(invariant(C))

That is, all the attributes will have been lost. That makes it possible to break any contract implied by the attributes without the compiler noticing, /just by taking the address of a function/.

And then we come to templates. Oh my! These new attributes are going to cause a big problem for templates. Consider:

    ReturnType!(f) foo(alias f)(ParameterTypeTuple!(f) n)
    {
        return f(n);
    }

or should that be...

    ReturnType!(f) foo(alias f)(ParameterTypeTuple!(f) n) nothrow
    {
        return f(n);
    }

? Hmm. Looks like the real answer is

    template foo(alias f)
    {
        if(is(f == nothrow))
        {
            ReturnType!(f) foo(ParameterTypeTuple!(f) n) nothrow
            {
                return f(n);
            }
        }
        else
        {
            ReturnType!(f) foo(ParameterTypeTuple!(f) n)
            {
                 return f(n);
            }
        }
    }

(and even that's assuming that we will be able to test for "if(is(f ==
nothrow))", which also is not certain). In fact, really, the template
should also test for "pure" as well, so it can attach the "pure"
attribute if it can.

And just in case anyone thinks that was a contrived example, real world uses would include root-solving by Newton's method, root-solving by the secant method, numerical integration, etc. - all of which will be pure iff f is pure, nothrow iff f is nothrow, etc.

While I understand the reasons for "pure", etc. (- and don't misunderstand me, they are sound reasons!), there are implications for syntax which will come back and bite us if we're not careful. At the very least, I would suggest

(1) attributes for delegates
(2) attribute tuples for templates

(e.g.

    AttributeTuple!(f) ReturnType!(f) foo(alias f)(ParameterTypeTuple!(f) n)
    {
        return f(n);
    }

)
April 25, 2008
Janice Caron wrote:
> While I understand the reasons for "pure", etc. (- and don't misunderstand me, they are sound reasons!), there are implications for syntax which will come back and bite us if we're not careful. At the very least, I would suggest
> 
> (1) attributes for delegates
> (2) attribute tuples for templates
> 
> (e.g.
> 
>     AttributeTuple!(f) ReturnType!(f) foo(alias f)(ParameterTypeTuple!(f) n)
>     {
>         return f(n);
>     }
> 
> )

My example was a contrived one, but you provided good examples that illustrates the syntactic issues that arise from all those additions to D's syntax.

One note regarding your template example: I don't see how that
AttributeTuple!(f) helps.
this should be solved via introspection at runtime or via traits at
compile type. either way, it should be possible to get f's attributes in
 a tuple/array.
something like: f.attributesof which would return a tuple of attributes.
since I'm advocating for introduction of user defined attributes to D,
I'd suggest that the compiler should treat "const"/"pure" and such as
built-in attributes that are provided by the language, so that if you
define:
[myAttr1, myAttr2]
pure int func(invariant C c);

then func.attributesof would contain the tuple:
(pure, myAttr1, myAttr2).
just like a type tuple can contain both built-in types and user defined
ones.

--Yigal
April 25, 2008
> And then there are delegates. I have mentioned in a previous thread (called something like "big hole in const system") that big problems exist because attributes can be applied to functions /but not to delegates/. That is, we cannot declare
> 
>         pure invariant invariant(C) delegate(invariant(C) c) nothrow dg;

For what it's worth, the D spec 2.013 allows delegates to have the pure and nothrow attributes. (Declarations: BasicType2 and Expressions: FunctionLiteral)

Christian
April 25, 2008
On 25/04/2008, Christian Kamm <kamm.incasoftware@shift-at-left-and-remove-this.de> > > For what it's worth, the D spec 2.013 allows delegates to have the pure and
> nothrow attributes. (Declarations: BasicType2 and Expressions:
> FunctionLiteral)

Ooh nice! Thanks for pointing that out.

But they need to be able to take const and invariant attributes too.

(And come to think of it, if you allowed delegates to be declared static then we could lose the "function" keyword altogether, as "delegate static" could take over that role. Maybe that's pushing things too far though).
April 29, 2008
Janice Caron wrote:
> On 25/04/2008, Yigal Chripun <yigal100@gmail.com> wrote:
>>  D2 will allow the following:
>>  pure invariant invariant(int) func(invariant(int)) nothrow;
>>  Am I the only one that thinks the above is too much?
> 
> You're not the only one.
> 

I second that.

> 
>     pure int func(invariant(C) a, invariant(C) b)
>     {
>         if (c == b) ...
> 
> Whoops! That won't compile, because - guess what!? - opEquals isn't pure!!!!
> 
> And it doesn't stop there - opEquals cannot be made pure just by
> changing its declaration, because a pure function must have invariant
> arguments, and opEquals has to work with or without invariant data.
> The upshot is that, eventually, Object.opEquals may have to be
> implemented twice:
> 
>     class Object
>     {
>         // the normal version
>         const bool opEquals(const Object o) {...}
> 
>         // the pure version
>         pure invariant bool opEquals(invariant Object o) {...}
>     }
> 
> and, therefore, so will anything that overloads it.
> 

Another example why "scoped const", despite "only" being a syntactical convenience, might be so important.


-- 
Bruno Medeiros - Software Developer, MSc. in CS/E graduate
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
April 29, 2008
On 29/04/2008, Bruno Medeiros <brunodomedeiros+spam@com.gmail> wrote:
> Another example why "scoped const", despite "only" being a syntactical convenience, might be so important.

That wouldn't help in this case. The reason you need the second declaration is so that one declaration can be pure, the other not. Steven's proposal solves a different problem.
April 30, 2008
Janice Caron wrote:
> On 29/04/2008, Bruno Medeiros <brunodomedeiros+spam@com.gmail> wrote:
>> Another example why "scoped const", despite "only" being a syntactical
>> convenience, might be so important.
> 
> That wouldn't help in this case. The reason you need the second
> declaration is so that one declaration can be pure, the other not.
> Steven's proposal solves a different problem.

Not if you combine "scoped const" with the partially pure function rules (see new thread). Example:

  //(using 'anyconst' as the scoped const keyword)

  class Object
  {
    pure anyconst bool opEquals(anyconst(Object) obj) {...}


The 3 versions of opEquals would be created. 2 of them would partially pure, the other (the invariant one) would be fully pure. The mutable version would be redudant though.

This would work, but since there is redundancy, I'm thinking there could be a better way to do it.


-- 
Bruno Medeiros - Software Developer, MSc. in CS/E graduate
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D