www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Idea For Attributes/Annotations

reply "Nick Sabalausky" <a a.a> writes:
I've previously voiced my desire for something similar to C#'s attributes 
(annotations in Java, IIRC), but that kind of got sidetracked into a 
discussion of command line parsers. I have another idea of a type of 
scenario where something like this would be very nice, and it also ties into 
the recent complaints of the mixin syntax being ugly.

I just learned over on digitalmars.D.learn that D apparently has no general 
way to convert the value of an enum var to the actual string name of the 
value (at least not without making the underlying type a string containing 
it's own name, but that has obvious drawbacks). To illustrate:

-------------
enum Shape { Square, Circle, Triangle }
Shape s = Shape.Triangle;

// Output is "2", but I want "Shape.Triangle" or "Triangle".
// But there doesn't seem to be a way to do that currently.
Stdout.formatln("{}", s);
-------------

First of all, I would like to propose that we have some built-in way to do 
that. But that's not the main point of this post. The general case I'm 
looking at here is that this is an example of something that can only be 
accomplished (without violating DRY) by using a common mixin pattern: 
Instead of simply declaring what we want, we create and instantiate a mixin 
that generates our intended declaration and also any related boilerplate:

-------------
// Something along these lines...
template generateEnum(char[] name, values...)
{
    const char[] generateEnum = "enum "~name~"{"~/*values*/~"}"~
        "char[] enumToString("~name~" arg) { "~/*big switch(arg) here*/~"}";
}
mixin(generateEnum!("Shape", "Square", "Circle", "Triangle"));
Shape s = Shape.Triangle;
Stdout.formatln("{}", enumToString (s));
-------------

Something along those lines would probably work (in fact, I've done similar 
things before). But, look what a mess that turns the enum's definition into! 
This is probably a good example of what people are thinking of when they 
call the mixin syntax ugly.

So here's my idea: Allow the programmer to attach the desired 
boilerplate-generator to the *actual* definition, instead of generating the 
definition as part of the boilerplate-generator:

-------------
// Actual syntax would probably differ,
// But I'm thinking something along these general lines.

[ GenerateEnumToString("some extra param")]
enum Shape { Square, Circle, Triangle }

// I really haven't throught through the syntax for this part
annotation GenerateEnumToString(T : enum, char[] extraStuff)
{
    const char[] func = "char[] enumToString("~name~" arg) { "~
        /*big switch(arg) here, using compile-time reflection on T*/
    ~"}";
    mixin(func);
}
Shape s = Shape.Triangle;
Stdout.formatln("{}", enumToString (s));
-------------

This way, we could do all the fancy mixin trickery we normally do, but in 
any case where something like this would be applicable, it wouldn't make the 
code look so terrible.
Mar 10 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Nick Sabalausky" <a a.a> wrote in message 
news:gp78hj$b26$1 digitalmars.com...
 -------------
 // Something along these lines...
 template generateEnum(char[] name, values...)
 {
    const char[] generateEnum = "enum "~name~"{"~/*values*/~"}"~
        "char[] enumToString("~name~" arg) { "~/*big switch(arg) 
 here*/~"}";
 }
 mixin(generateEnum!("Shape", "Square", "Circle", "Triangle"));
 Shape s = Shape.Triangle;
 Stdout.formatln("{}", enumToString (s));
 -------------

Ok, so apparently D2 phobos already has this in std.typecons (saw that in a reply over at ".D.learn"), but the main point of my post still remains. That point being, it looks terrible and annotations could make it (and anything similar) look not-terrible.
Mar 10 2009
prev sibling parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
For reference, Python has a concept called decorators.  Take this function:

 def trace(fn):
     def __wrap(*pargs,**kwargs):
         print 'IN  %s' % fn.__name__
         r = fn(*pargs,**kwargs)
         print 'OUT %s' % fn.__name__
         return r

     return __wrap

And use it like so:
  trace
 def foo(msg):
     print 'Bar: %s' % msg
     return 42

 print foo("baz")

It produces this output:
 IN  foo
 Bar: baz
 OUT foo
 42

And is equivalent to the following:
 def foo(msg):
     print 'Bar: %s' % msg
     return 42

 foo = trace(foo)

I believe either 2.5 or 3.0 adds this syntax to classes as well. -- Daniel
Mar 10 2009
parent Daniel Keep <daniel.keep.lists gmail.com> writes:
On the syntax for D, a few thoughts:

In Python, the decorator expression must evaluate to a function, which
is then called with the function being decorated.  We could do something
similar in D.

Let's change the syntax to this for now:

 (expr) decl

Let's assume that expr must be a template.  The template will be passed
two arguments, although the second may be omitted.

template foo(alias Decl, char[] src) { ... }

Decl is an alias to whatever is being declared.  This allows functions,
classes, structs, enums, constants, etc. to be decorated.  The second
argument is the source code for that construct.

So let's take an example:

 (foo) void bar();

If foo has a public member called 'foo', then the code is rewritten as:

private void __decorated_bar;
mixin foo!(__decorated_bar, "void bar();");
alias foo!(__decorated_bar, "void bar();").foo bar;

Otherwise, it should be rewritten as:

void bar();
mixin foo!(bar, "void bar();");

This would allow the decorator to, for example, generate a toString
method for an enum without touching the enum itself, wrap a class, or both.

A final idea, this:

 (expr) { decl_a; decl_b; }

decl_c;

 (expr):
  decl_d;
  decl_e;

Could be rewritten as:

 (expr) decl_a;
 (expr) decl_b;
decl_c;
 (expr) decl_d;
 (expr) decl_e;

  -- Daniel
Mar 10 2009