www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Typeof woes

reply Marek Janukowicz <marek janukowicz.net> writes:
Given this short program:

class A {

  int i;
  //enum Smth { X };

  void list () {
    foreach( s; __traits(derivedMembers, typeof(this))) {
      alias type = typeof(__traits(getMember, this, s));
    }
  }
}

void main () {
  A a = new A();
  a.list();
}

if I uncomment "enum" line I get compilation error:
aa.d(8): Error: argument Smth to typeof is not an expression

So far it only happened with enums for me, but admittedly I didn't check all 
the possible types out there.

What's even more interesting it's just alias problem, the expression 
"typeof(__traits(getMember, this, s))" might be used eg. in static if even 
for enums.

Is this a bug or do I miss something (again)?

-- 
Marek Janukowicz
Aug 20 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 21, 2013 at 01:42:11AM +0200, Marek Janukowicz wrote:
 Given this short program:
 
 class A {
 
   int i;
   //enum Smth { X };
 
   void list () {
     foreach( s; __traits(derivedMembers, typeof(this))) {
       alias type = typeof(__traits(getMember, this, s));
     }
   }
 }
 
 void main () {
   A a = new A();
   a.list();
 }
 
 if I uncomment "enum" line I get compilation error:
 aa.d(8): Error: argument Smth to typeof is not an expression
 
 So far it only happened with enums for me, but admittedly I didn't
 check all the possible types out there.
The reason is that derivedMembers returns *all* members, including enum definitions, not just member variables and methods. So this includes 'Smth', and typeof(this.Smth) is invalid because this.Smth is already a type, so you can't apply typeof to it. To get around this, you will need to use static if, maybe something like this: void list() { // Warning: untested code foreach(s; __traits(derivedMembers, typeof(this))) { static if (is(typeof(__traits(getMember, this, s)) type)) { // here, 'type' is bound to the type of // the member. } } } This uses the is(X Y) form, where if X is a valid type, then it gets aliased to Y inside the static if block. (I have to admit I'm not that pleased with D's is-expressions due to their confusing syntax, but they do work and are quite a powerful tool to use once you understand them.)
 What's even more interesting it's just alias problem, the expression
 "typeof(__traits(getMember, this, s))" might be used eg. in static if
 even for enums.
 
 Is this a bug or do I miss something (again)?
[...] Are you sure about that? That doesn't sound right to me. T -- Lottery: tax on the stupid. -- Slashdotter
Aug 20 2013
parent reply Marek Janukowicz <marek janukowicz.net> writes:
H. S. Teoh wrote:

 On Wed, Aug 21, 2013 at 01:42:11AM +0200, Marek Janukowicz wrote:
 Given this short program:
 
 class A {
 
   int i;
   //enum Smth { X };
 
   void list () {
     foreach( s; __traits(derivedMembers, typeof(this))) {
       alias type = typeof(__traits(getMember, this, s));
     }
   }
 }
 
 void main () {
   A a = new A();
   a.list();
 }
 
 if I uncomment "enum" line I get compilation error:
 aa.d(8): Error: argument Smth to typeof is not an expression
 
 So far it only happened with enums for me, but admittedly I didn't
 check all the possible types out there.
The reason is that derivedMembers returns *all* members, including enum definitions, not just member variables and methods. So this includes 'Smth', and typeof(this.Smth) is invalid because this.Smth is already a type, so you can't apply typeof to it.
Yes, I do understand this, however the behavior indicated below (your last question and my code) got me really confused.
 
 To get around this, you will need to use static if, maybe something like
 this:
 
 void list() {
 // Warning: untested code
 foreach(s; __traits(derivedMembers, typeof(this))) {
 static if (is(typeof(__traits(getMember, this, s)) type)) {
 // here, 'type' is bound to the type of
 // the member.
 }
 }
 }
 
 This uses the is(X Y) form, where if X is a valid type, then it gets
 aliased to Y inside the static if block. (I have to admit I'm not that
 pleased with D's is-expressions due to their confusing syntax, but they
 do work and are quite a powerful tool to use once you understand them.)
This code works, thank you. However, if I may share my newbie impression - current traits implementation looks not complete to me. What I want to do is to process those members that are of certain type - instead of a nice one- liner to identify those, I already need two and the first (the one you provided above) looks like a hack to me, because I have to check if some expression is valid. The whole issue (which to me looks like a quite standard metaprogramming task) already took me a few hours (and also some time of you and the other helpful guys) and I can't get rid of the impression either the docs are lacking or we could use some more traits.
 What's even more interesting it's just alias problem, the expression
 "typeof(__traits(getMember, this, s))" might be used eg. in static if
 even for enums.
 
 Is this a bug or do I miss something (again)?
[...] Are you sure about that? That doesn't sound right to me.
Yes, I'm sure, here goes modified code: import std.stdio; class A { int a; enum Smth { A }; void list () { foreach( s; __traits(derivedMembers, typeof(this))) { //alias type = typeof(__traits(getMember, this, s)); static if (is (typeof(__traits(getMember, this, s)) == int)) { writefln ("%s is int", s ); } } } } void main () { A a = new A(); a.list(); } which outputs: a is int -- Marek Janukowicz
Aug 20 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 21, 2013 at 08:32:09AM +0200, Marek Janukowicz wrote:
 H. S. Teoh wrote:
[...]
 To get around this, you will need to use static if, maybe something
 like this:
 
 void list() {
 // Warning: untested code
 foreach(s; __traits(derivedMembers, typeof(this))) {
 static if (is(typeof(__traits(getMember, this, s)) type)) {
 // here, 'type' is bound to the type of
 // the member.
 }
 }
 }
 
 This uses the is(X Y) form, where if X is a valid type, then it gets
 aliased to Y inside the static if block. (I have to admit I'm not
 that pleased with D's is-expressions due to their confusing syntax,
 but they do work and are quite a powerful tool to use once you
 understand them.)
This code works, thank you. However, if I may share my newbie impression - current traits implementation looks not complete to me. What I want to do is to process those members that are of certain type - instead of a nice one- liner to identify those, I already need two and the first (the one you provided above) looks like a hack to me, because I have to check if some expression is valid. The whole issue (which to me looks like a quite standard metaprogramming task) already took me a few hours (and also some time of you and the other helpful guys) and I can't get rid of the impression either the docs are lacking or we could use some more traits.
From what I can tell, the traits are designed to be minimal, just enough
to cover what needs to be done, so sometimes they may not have the nicest or most concise interfaces. You can always add some syntactic sugar to it using templates, of course. That, and the docs probably could use a facelift. :) This isn't the first time somebody's found the docs too obscure or unhelpful. In any case, you're not the first one who found the __traits syntax cumbersome. I know I did when I first started using it. It's probably too late to change __traits syntax, but I can see adding some nice wrapper templates to std.traits to make these things less annoying. Some stuff is already there, but a few more would make introspection easier.
 What's even more interesting it's just alias problem, the
 expression "typeof(__traits(getMember, this, s))" might be used eg.
 in static if even for enums.
 
 Is this a bug or do I miss something (again)?
[...] Are you sure about that? That doesn't sound right to me.
Yes, I'm sure, here goes modified code: import std.stdio; class A { int a; enum Smth { A }; void list () { foreach( s; __traits(derivedMembers, typeof(this))) { //alias type = typeof(__traits(getMember, this, s)); static if (is (typeof(__traits(getMember, this, s)) == int)) { writefln ("%s is int", s );
[...] Ah, you're putting it inside an is(...) expression, that's why it "works". :) The whole point of is(...) is to check if a particular expression makes sense (aka "has a type", the idea being that if the expression is invalid, like if you try to perform arithmetic on types or if use a type as a value). So naturally, it suppresses type-related errors inside and just returns false instead. This is one of the dark corners in D where things are more obscure than they need to be. The whole syntax/semantics of is(...), while quite complete and functional, leaves a lot to be desired in terms of readability / usability. A lot of newcomers have trouble with it, including myself when I first encountered it. In any case, if you're just looking for members of a particular known type, then using is(typeof(__traits(getMember, ...)...)) should be good enough. T -- Political correctness: socially-sanctioned hypocrisy.
Aug 21 2013
prev sibling next sibling parent reply "Dicebot" <public dicebot.lv> writes:
typeof only accepts expressions, not types.

Workaround:

template myTypeOf(T...)
    if (T.length == 1)
{
     static if (is(T))
         alias myTypeOf = T;
     else
         alias myTypeOf = typeof(T);
}
Aug 20 2013
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 21, 2013 at 02:15:06AM +0200, Dicebot wrote:
 typeof only accepts expressions, not types.
 
 Workaround:
 
 template myTypeOf(T...)
    if (T.length == 1)
 {
     static if (is(T))
         alias myTypeOf = T;
     else
         alias myTypeOf = typeof(T);
 }
Clever. I may have to add this to my personal code library. :) T -- Everybody talks about it, but nobody does anything about it! -- Mark Twain
Aug 20 2013
parent "Dicebot" <public dicebot.lv> writes:
On Wednesday, 21 August 2013 at 00:24:24 UTC, H. S. Teoh wrote:
 On Wed, Aug 21, 2013 at 02:15:06AM +0200, Dicebot wrote:
 typeof only accepts expressions, not types.
 
 Workaround:
 
 template myTypeOf(T...)
    if (T.length == 1)
 {
     static if (is(T))
         alias myTypeOf = T;
     else
         alias myTypeOf = typeof(T);
 }
Clever. I may have to add this to my personal code library. :) T
It should have been T[0], not plain T, of course.
Aug 20 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 08/21/13 02:15, Dicebot wrote:
 typeof only accepts expressions, not types.
 
 Workaround:
 
 template myTypeOf(T...)
    if (T.length == 1)
 {
     static if (is(T))
         alias myTypeOf = T;
     else
         alias myTypeOf = typeof(T);
 }
Keep in mind that 'typeof' is special - it won't evaluate the argument. Which could be any expression (not just the subset allowed as template parms). artur
Aug 22 2013
prev sibling next sibling parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and typeof(this.Smth) is invalid because this.Smth is already a
 type, so you can't apply typeof to it.
I am beginning to wonder if typeof(typeof(expr)) is worthy of an error. Would we end up having any bugs if we were to allow typeof() to work on types (IOW it would just alias itself to the type)? I guess the only way to find out is to try it out in a compiler branch, but I'm genuinely curious.
Aug 20 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-08-21 02:24, Andrej Mitrovic wrote:

 I am beginning to wonder if typeof(typeof(expr)) is worthy of an
 error. Would we end up having any bugs if we were to allow typeof() to
 work on types (IOW it would just alias itself to the type)?

 I guess the only way to find out is to try it out in a compiler
 branch, but I'm genuinely curious.
As we have seen, not allowing typeof to accept types makes some generic code a bit more cumbersome to write. -- /Jacob Carlborg
Aug 20 2013
prev sibling parent reply Marek Janukowicz <marek janukowicz.net> writes:
Andrej Mitrovic wrote:

 On 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and typeof(this.Smth) is invalid because this.Smth is already a
 type, so you can't apply typeof to it.
I am beginning to wonder if typeof(typeof(expr)) is worthy of an error. Would we end up having any bugs if we were to allow typeof() to work on types (IOW it would just alias itself to the type)?
It's not always considered an error, this code compiles fine (my guess is the "is" expression does some magic with its arguments): import std.stdio; class A { int a; enum Smth { A }; void list () { foreach( s; __traits(derivedMembers, typeof(this))) { static if (is (typeof(typeof(typeof(__traits(getMember, this, s)))) == int)) { writefln ("%s is int", s ); } } } } void main () { A a = new A(); a.list(); } -- Marek Janukowicz
Aug 20 2013
parent "Dicebot" <public dicebot.lv> writes:
Yes, "is" expression suppresses errors and simply returns "false" 
(as error is not valid type). It is used in a common D idiom - 
"is(typeof(<expression>))" to check if certain expression is 
valid and compiles in current scope.
Aug 21 2013
prev sibling next sibling parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Aug 21, 2013 at 02:24:18AM +0200, Andrej Mitrovic wrote:
 On 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and typeof(this.Smth) is invalid because this.Smth is already a
 type, so you can't apply typeof to it.
I am beginning to wonder if typeof(typeof(expr)) is worthy of an error. Would we end up having any bugs if we were to allow typeof() to work on types (IOW it would just alias itself to the type)? I guess the only way to find out is to try it out in a compiler branch, but I'm genuinely curious.
Hmm. If typeof were idempotent on types, then it would provide nice closure properties (as in mathematical closure, not delegates) if we ever implement the proposed typetuple/std.typecons.Tuple unification from the other thread: typeof(tup(1,"a")) == tup(int,string) typeof(tup(int,"a")) == tup(int,string) typeof(tup(int,string)) == tup(int,string) This alleviates one of the nagging problems with mixed tuples that contain both types and expressions. Then the only left to do is to specify that a tuple can be used as a type only if it contains only types (IOW, if is(typeof(tup)==tup), a rather nice way to express it), and a tuple can be used as a value (assigned to variables, etc.) only if it contains only values. "Mixed" tuples can still be used as template arguments, and you can always apply typeof to them. T -- It's bad luck to be superstitious. -- YHL
Aug 20 2013
prev sibling parent Artur Skawina <art.08.09 gmail.com> writes:
On 08/21/13 02:43, H. S. Teoh wrote:
 On Wed, Aug 21, 2013 at 02:24:18AM +0200, Andrej Mitrovic wrote:
 On 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote:
 and typeof(this.Smth) is invalid because this.Smth is already a
 type, so you can't apply typeof to it.
I am beginning to wonder if typeof(typeof(expr)) is worthy of an error. Would we end up having any bugs if we were to allow typeof() to work on types (IOW it would just alias itself to the type)? I guess the only way to find out is to try it out in a compiler branch, but I'm genuinely curious.
Hmm. If typeof were idempotent on types, then it would provide nice closure properties (as in mathematical closure, not delegates) if we ever implement the proposed typetuple/std.typecons.Tuple unification from the other thread: typeof(tup(1,"a")) == tup(int,string) typeof(tup(int,"a")) == tup(int,string) typeof(tup(int,string)) == tup(int,string) This alleviates one of the nagging problems with mixed tuples that contain both types and expressions. Then the only left to do is to specify that a tuple can be used as a type only if it contains only types (IOW, if is(typeof(tup)==tup), a rather nice way to express it), and a tuple can be used as a value (assigned to variables, etc.) only if it contains only values. "Mixed" tuples can still be used as template arguments, and you can always apply typeof to them.
You do realize that this is the way the built-in tuples work already? With the exception of that last case, which of course is an error right now. (I'm not sure that allowing typeof(Type) would be a good idea) artur
Aug 22 2013