November 14, 2012
On 2012-11-14 12:18, Tove wrote:
> On Wednesday, 14 November 2012 at 11:08:04 UTC, Leandro Lucarella wrote:
>>
>> Can you provide one concrete case where it makes sense NOT to restrict
>> UDAs to
>> types and it's different from restricting exception to classes derived
>> from
>> Exception?
>>
>> Thank you.
>
> There was the example with Thrift...
>
>    struct UserProfile {
>          1: i32 uid,
>          2: string name,
>          3: string blurb
>        }
>        service UserStorage {
>          void store(1: UserProfile user),
>          UserProfile retrieve(1: i32 uid)
>        }
>
> You could use a user defined type for the struct... but for the members
> it would make sense to use the native type directly... and if you
> traverse the annotation in sequence rather than as standalone entities..
> it's perfectly safe to use 1,2,3 as annotation...
>
> i.e. first scan for the Thrift symbol, then scan for native typed int:s...

I assume you mean something like:

struct UserProfile {
    [1] i32 uid;
    [2] string name;
    [3] string blurb;
}

In that case I would much rather prefer this:

struct UserProfile {
    @Id(1) i32 uid;
    @Id(2) string name;
    @Id(3) string blurb;
}

Where Id is "thrift.attributes.Id" or something similar.

-- 
/Jacob Carlborg
November 14, 2012
On Wednesday, 14 November 2012 at 12:33:58 UTC, Jacob Carlborg wrote:
>
> I assume you mean something like:
>
> struct UserProfile {
>     [1] i32 uid;
>     [2] string name;
>     [3] string blurb;
> }
>
> In that case I would much rather prefer this:
>
> struct UserProfile {
>     @Id(1) i32 uid;
>     @Id(2) string name;
>     @Id(3) string blurb;
> }
>
> Where Id is "thrift.attributes.Id" or something similar.

well, similar... but beginning with a symbol...

[thrift.attributes.Definition]
struct UserProfile
{
  [1] i32 uid;
  [2] string name;
  [3] string blurb;
}

November 14, 2012
On Wednesday, 14 November 2012 at 11:18:28 UTC, Tove wrote:
> There was the example with Thrift...
>
>   struct UserProfile {
>         1: i32 uid,
>         2: string name,
>         3: string blurb
>       }
>       service UserStorage {
>         void store(1: UserProfile user),
>         UserProfile retrieve(1: i32 uid)
>       }
>
> You could use a user defined type for the struct... but for the members it would make sense to use the native type directly... and if you traverse the annotation in sequence rather than as standalone entities.. it's perfectly safe to use 1,2,3 as annotation...

But what if you want to use that struct with another library as well, for which you might also want to tack some ids on the fields? I'm the author of the current D implementation in Thrift, and if/when user defined attributes become stable and I'll amend it to take advantage of UDAs, I'll definitely not go for raw literals…

David
November 14, 2012
On Wednesday, 14 November 2012 at 13:03:18 UTC, David Nadlinger wrote:
> On Wednesday, 14 November 2012 at 11:18:28 UTC, Tove wrote:
>> There was the example with Thrift...
>>
>>  struct UserProfile {
>>        1: i32 uid,
>>        2: string name,
>>        3: string blurb
>>      }
>>      service UserStorage {
>>        void store(1: UserProfile user),
>>        UserProfile retrieve(1: i32 uid)
>>      }
>>
>> You could use a user defined type for the struct... but for the members it would make sense to use the native type directly... and if you traverse the annotation in sequence rather than as standalone entities.. it's perfectly safe to use 1,2,3 as annotation...
>
> But what if you want to use that struct with another library as well, for which you might also want to tack some ids on the fields? I'm the author of the current D implementation in Thrift, and if/when user defined attributes become stable and I'll amend it to take advantage of UDAs, I'll definitely not go for raw literals…
>
> David

// in this nested scope, all uints are interpreted as belonging to the thrift module.
[std.attributes(uint, thrift)]
struct UserProfile
...

// error detected at compile-time
[std.attributes(uint, thrift), std.attributes(uint, thrift2)]
struct UserProfile
...

November 14, 2012
On 2012-11-14 14:28, Tove wrote:

> // in this nested scope, all uints are interpreted as belonging to the
> thrift module.
> [std.attributes(uint, thrift)]
> struct UserProfile
> ...
>
> // error detected at compile-time
> [std.attributes(uint, thrift), std.attributes(uint, thrift2)]
> struct UserProfile
> ...

That doesn't sound like a very good idea. I think it's better to not use raw ints.

-- 
/Jacob Carlborg
November 14, 2012
Tove, el 14 de November a las 13:55 me escribiste:
> >struct UserProfile {
> >    @Id(1) i32 uid;
> >    @Id(2) string name;
> >    @Id(3) string blurb;
> >}
> >
> >Where Id is "thrift.attributes.Id" or something similar.
> 
> well, similar... but beginning with a symbol...
> 
> [thrift.attributes.Definition]
> struct UserProfile
> {
>   [1] i32 uid;
>   [2] string name;
>   [3] string blurb;
> }

OK, that's just a good example of convenience of allowing native types, but I think it is still potentially harmful. What if you pass that struct to another library that have another meaning attached to int annotations? How could you tell that library that this particular int annotations is not for it?

I mean, it convenient to be able to throw numbers or strings too:

void f() { throw 1; }

void g() { throw "error"; }

int main()
{
	try {
		g();
	} catch (char[] e) {
		writefln(e);
	}
	try {
		f();
	} catch (int i) {
		return i;
	}
	return 0;
}

-- 
November 14, 2012
On 11/14/2012 03:31 PM, Leandro Lucarella wrote:
> Tove, el 14 de November a las 13:55 me escribiste:
>>> struct UserProfile {
>>>     @Id(1) i32 uid;
>>>     @Id(2) string name;
>>>     @Id(3) string blurb;
>>> }
>>>
>>> Where Id is "thrift.attributes.Id" or something similar.
>>
>> well, similar... but beginning with a symbol...
>>
>> [thrift.attributes.Definition]
>> struct UserProfile
>> {
>>    [1] i32 uid;
>>    [2] string name;
>>    [3] string blurb;
>> }
>
> OK, that's just a good example of convenience of allowing native types, but I
> think it is still potentially harmful. What if you pass that struct to another
> library that have another meaning attached to int annotations? How could you
> tell that library that this particular int annotations is not for it?
> ...

By not telling it that they are for it. Note that the [thrift.attributes.Definition] annotation is significant.

> I mean, it convenient to be able to throw numbers or strings too:
>
> void f() { throw 1; }
>
> void g() { throw "error"; }
>
> int main()
> {
> 	try {
> 		g();
> 	} catch (char[] e) {
> 		writefln(e);
> 	}
> 	try {
> 		f();
> 	} catch (int i) {
> 		return i;
> 	}
> 	return 0;
> }
>

What he does is more like (but not exactly like):

class ThriftException{
    int x;
    this(int x){ this.x = x; }
}



November 14, 2012
On 11/14/2012 2:53 AM, Jacob Carlborg wrote:
> If "std.mytypes.mystring" is a variable of the type "string" then the fully
> qualified name is lost if it's used as an attribute. Something like this:

I am having a REALLY hard time making my point here.

struct MyString
{
     string s;
}

Now use MyString as an attribute. No, the name is not lost. Yes, two different modules can use MyString as an attribute, and impute completely different meanings into it.

Just because it is not a builtin type does not change anything.
November 14, 2012
On 11/14/12 1:13 PM, Walter Bright wrote:
> On 11/14/2012 2:53 AM, Jacob Carlborg wrote:
>> If "std.mytypes.mystring" is a variable of the type "string" then the
>> fully
>> qualified name is lost if it's used as an attribute. Something like this:
>
> I am having a REALLY hard time making my point here.
>
> struct MyString
> {
> string s;
> }
>
> Now use MyString as an attribute. No, the name is not lost. Yes, two
> different modules can use MyString as an attribute, and impute
> completely different meanings into it.
>
> Just because it is not a builtin type does not change anything.

I think a simple way to put this is that attribute name lookup works the same as ordinary symbol lookup. Assuming we did a good job at the latter, the former doesn't introduce any specific issues.

Andrei
November 14, 2012
On Wednesday, 14 November 2012 at 13:28:05 UTC, Tove wrote:
> // in this nested scope, all uints are interpreted as belonging to the thrift module.
> [std.attributes(uint, thrift)]
> struct UserProfile
> ...
>
> // error detected at compile-time
> [std.attributes(uint, thrift), std.attributes(uint, thrift2)]
> struct UserProfile
> ...

»interpreted as belonging to the thrift module« – what do you even mean by that? As of now, UDAs are just a way to tuck arbitrary symbols onto declarations. There is no concept of »belonging« to a module, and I don't think there should be.

Also, your solution is more complex than simply using types, yet less flexible: What if you want to use "uint" attributes from two libraries on the same type?

David