March 16, 2012
On Fri, 16 Mar 2012 09:35:54 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:

> On the ride over here today, I had a thought that
> I think neatly solves the user defined attribute
> question.
>
> enum Serializable { yes, no }
>
> @note(Serializable.yes) int a;

I thought @<symbol> was supposed to be a user-defined annotation.  Otherwise, why did we introduce @syntax?

I'd rather see something like this:

@annotation serializable(bool x = true) {return x ? Serializable.yes : Serializable.no;}  // implicit auto

@serializable int a;
@serializable(false) int b;

Where annotations have to be CTFE-able (because they are constructed at compile-time)

-Steve
March 16, 2012
On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer wrote:
> I thought @<symbol> was supposed to be a user-defined annotation.  Otherwise, why did we introduce @syntax?

idk, to "reduce" the number of keywords or somethiny.

This is why I call it a mistake or missed opportunity
right now though: @property, @safe, @disable, @system,
and @trusted have already made a claim on the @syntax.

Now, we have to work around that, which is why I'm
thinking @note(expression) rather than @<something>.

> I'd rather see something like this:

I could live with that too, but I think it'd be harder
to make happen due to potential clashes with the current
thing @ is used for.
March 16, 2012
On Fri, 16 Mar 2012 13:36:42 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:

> On Friday, 16 March 2012 at 16:57:26 UTC, Steven Schveighoffer wrote:
>> I thought @<symbol> was supposed to be a user-defined annotation.  Otherwise, why did we introduce @syntax?
>
> idk, to "reduce" the number of keywords or somethiny.

Quote from TDPL (section 5.9.1 on page 156):

"Attributes, always introduced with @, are simple adornments specifying certain features for the symbol being defined.  Some attributes are recognized by the compiler; some are defined and used by the programmer alone."

I assumed this was true, and thought so even before TDPL came out.  See http://prowiki.org/wiki4d/wiki.cgi?LanguageDevel/DIPs/DIP6

> This is why I call it a mistake or missed opportunity
> right now though: @property, @safe, @disable, @system,
> and @trusted have already made a claim on the @syntax.

I think those are just compiler-defined attributes.  They aren't keywords in the sense that the parser doesn't treat them as special.  They are just another type of symbol.

> I could live with that too, but I think it'd be harder
> to make happen due to potential clashes with the current
> thing @ is used for.

meh, just don't use @safe, @property, @disable, @system, and @trusted.  I don't see a huge number of compiler-defined attributes springing up.  Nor do I see a huge number of library or user-defined ones springing up.

-Steve
March 16, 2012
On 3/16/12, Adam D. Ruppe <destructionator@gmail.com> wrote:
> The current language solution isn't really *bad* with enough library help, but it isn't particularly *good* either and I don't think it can be. I've tried a few things, and I still see the lack of user annotations as D's biggest miss right now.

Yeah, but I would say if we had even better compile-time introspection we could have the freedom to implement any number of annotation implementations in library code. When you put something into the language you have to depend on C++ hackers to implement and then inevitably fix the upcoming bugs in the front-end (ICEs are an annoying blocker), and there's always that issue where the devs are against adding new features to an existing language feature. (say annotations were implemented, soon enough someone is going to complain it doesn't have enough functionality and that it needs to be extended).

Personally I'd love it if we had more __traits and compile-time introspection abilities.
March 16, 2012
On Friday, 16 March 2012 at 18:07:18 UTC, Steven Schveighoffer wrote:
> Quote from TDPL (section 5.9.1 on page 156):

Huh. There's nothing I can see in the compiler that even
hints at this. The @ thing is just another storage class
as far as it's concerned right now.

> Nor do I see a huge number of library or user-defined ones springing up.

Yeah.

I still really like my expression idea though, but
thinking about your strategy, we could say:

When you see @, if it is a compiler word after it,
parse it that way. Otherwise, try to get a function
call out of it.

Treat the function call as an enum and add it to the
list. Thus, it is CTFE, namespaced, etc. like normal.

The return value is appended to the declaration's
attribute expression list (so the end result is the same
as I was thinking.)


I'm pretty sure that'd work..
March 16, 2012
On Fri, 16 Mar 2012 14:33:37 -0400, Adam D. Ruppe <destructionator@gmail.com> wrote:

> On Friday, 16 March 2012 at 18:07:18 UTC, Steven Schveighoffer wrote:
>> Quote from TDPL (section 5.9.1 on page 156):
>
> Huh. There's nothing I can see in the compiler that even
> hints at this. The @ thing is just another storage class
> as far as it's concerned right now.

Right, the user-defined portion is not implemented.

>
>> Nor do I see a huge number of library or user-defined ones springing up.
>
> Yeah.
>
> I still really like my expression idea though, but
> thinking about your strategy, we could say:
>
> When you see @, if it is a compiler word after it,
> parse it that way. Otherwise, try to get a function
> call out of it.

The way I'd like to see it work (and not being a compiler writer, I have no idea if/how this would work) is:

When you see @, call a CTFE function with that name.

The compiler defines intrinsic functions @property, @disable, etc.  which return intrinsic data types that get appended to the attribute list.  Then the compiler recognizes those data types in the attribute list when deciding how things should be compiled.

This might not be feasible given the current implementation, or how compilers are designed, I have no idea.

> The return value is appended to the declaration's
> attribute expression list (so the end result is the same
> as I was thinking.)

Yes.  And I'd like to see the other intrinsic attributes appended (and queryable) as well.

What we also may need in the @annotation declaration is an alias of the thing being compiled (again, don't know how feasible this is)

for example:

@annotation property(alias sym)() { static assert(is(sym == function)); return INTRINSIC.isProperty;}

Maybe that's going too far :)

-Steve
March 16, 2012
Am Fri, 16 Mar 2012 16:21:41 +0100
schrieb Piotr Szturmaj <bncrbme@jadamspam.pl>:

> > @note(Serializable.yes) int a;
> 
> This is just enum. If you use struct or class attributes (like in C#) then direct @Struct(constructor args..) might be better.
> 
> 
+1

> > The expression inside is appended to a list on
> > the declaration.
> >
> > This would be valid on variables, functions,
> > function parameters, struct members, etc.
> >
> > 2) __traits(getNotes, decl). This returns a tuple
> > of all the note expressions.
> >
> > foreach(i, exp; __traits(getNotes, a)) {
> > static assert(is(typeof(exp) == Serializable);
> > static assert(exp == Serializable.yes);
> > }
> 
> This is nice. Also it's base for library functions like hasNotes or getNotes!(Symbol, MyAttribute).
+1

> > 1) how do you define what is and is not a valid attribute?
> >
> 
> It should be any user-defined type that may be used at compile-time. For example @UUID("...") should be available out of the box.

Or we could add a @attribute attribute:

@attribute struct Test
{
    this(int a) {}...
}

@Test(99)
string someTestVal;

> > 2) OK, how do you namespace things so different libraries don't get different results?
> >
> > That's where the beauty of the expression type comes in: D already has namespaces. foo.Serializable != bar.Serializable, so there's no conflict.
> >
> > We simply declare types. The enum X {yes, no} pattern is already common in Phobos, and is also the best way to express a bool condition here. (Technically, @note(true) would compile, but it'd be much less useful than declaring a name for the note too, with an enum.)
> >
> > Name conflicts is a problem already solved by the language.
> 
> Yes, it should be possible to write @std.UUID("").
> 
And all normal import rules should work for user defined attributes as well (renamed imports, static imports, ...).


March 17, 2012
On Friday, 16 March 2012 at 16:09:55 UTC, Andrei Alexandrescu wrote:
> So we have:
>
> class A {
>     @note(Serializable.yes) int a;
>     ...
> }
>
> vs. a hypothetical in-language solution:
>
> class A {
>     int a;
>     mixin(note("a", Serializable.yes));
>     ...
> }
>
> I wonder to what extent the in-language solution can be made to work.
>
>
> Andrei

This gets to an unreasonable amount of noise and complexity. First, you have the issue of using mixins. Using mixins is quite ugly. It's very tool unfriendly. It's not easily readable. It can mess up line numbers. It may or may not be limited in situations such as with parameters.

In language solutions should be preferred for many things, but ultimately, not everything is a nail to hammer.

Ultimately, I'd like to see being able to use any CTFE capable struct/class for an attribute with a constructor, ideally with access to the symbol it's defined on. This allows things like (if my unchecked code was correct):

@attribute struct WebForm {
    string FormName;
    this(string FormName) {
        this.FormName = FormName;
        static assert(is(typeof(Symbol) : struct));
        static assert(__traits(getAllMembers, Symbol).length > 0);
        // TODO: Make sure at least one member set to Browsable(true).
    }
}

@attribute struct Validate {
    string Pattern;
    this(string Pattern) {
        this.Pattern = Pattern;
        static assert(isValidRegex(Pattern));
    }
}

@WebForm("Account");
@PostTo("Services/CreateAccount")
@SecureOnly(true)
struct CreateAccountForm {

    @Description("The name of your account. Must be between 3 and 12 letters.");
    @Validate(r"\w{3,12}")
    string Username;

    @Validate(r"\w+@\w+.\w+");
    @Description("The email address to associate with this account.");
    string Email;

}

While it's a bit of boiler plate to create the library and attributes, you only need to do it once. Then you can make a very nice form that automatically validates fields both clientside (where JS and/or CSS3 is supported) and serverside (when posted to Services/CreateAccount). You can verify a bunch of things at compile-time, including that the service exists or that the validation contains valid regex. And best of all, it's actually readable unlike if you went with a mixin approach. You would end up having 7 random mixins in that above form, and have to manually check each one to see if it's creating code or just creating an attribute. This way, you can see the @ and know it's an attribute immediately.

Another thing that doesn't come up yet but may in the future: reflection. It seems worrying to have random mixins adding reflection info, as that's a job for the compiler. It knows about when reflection is enabled, it knows how much it needs to generate, if things exist after optimization, etc. There's no point leaving that to a mixin and having to keep it in sync with the compiler. This way though, there's no need. The reflection data would just have an annotations property that can be accessed.
March 17, 2012
On 3/17/12, Kapps <opantm2+spam@gmail.com> wrote:
> @WebForm("Account");
> @PostTo("Services/CreateAccount")
> @SecureOnly(true)
> struct CreateAccountForm {

That kind of turns D from a structural to a declarative language. :p
March 17, 2012
On Saturday, 17 March 2012 at 00:54:02 UTC, Andrej Mitrovic wrote:
> On 3/17/12, Kapps <opantm2+spam@gmail.com> wrote:
>> @WebForm("Account");
>> @PostTo("Services/CreateAccount")
>> @SecureOnly(true)
>> struct CreateAccountForm {
>
> That kind of turns D from a structural to a declarative language. :p

Web design is quite a declarative thing. :) The code is already written for you, why bother writing it again instead of just writing it once and passing along essentially parameters to it.

Anywho, something like that is a somewhat extreme case. Basic things include @serializable or @notserialized (or @serialized(false)). With the topic of dynamic/scripting languages, other things can include @scripted to indicate a method or class can be accessed from the script, or @scripted(Security.low) to indicate user scripts can access it and not just game scripts. This prevents having to make annoying bindings and communicate what's passed in to the script compiler. Instead, you can find out everything that can be passed in at compile-time, and upon script compilation you can verify the script has access to all of these methods, then statically call them without any of the performance hit you would normally have by using scripting, yet with the safety.