digitalmars.D.learn - Typeof woes
- Marek Janukowicz (24/24) Aug 20 2013 Given this short program:
- H. S. Teoh (25/54) Aug 20 2013 The reason is that derivedMembers returns *all* members, including enum
- Marek Janukowicz (34/90) Aug 20 2013 Yes, I do understand this, however the behavior indicated below (your la...
- H. S. Teoh (31/84) Aug 21 2013 to cover what needs to be done, so sometimes they may not have the
- Dicebot (10/10) Aug 20 2013 typeof only accepts expressions, not types.
- H. S. Teoh (5/17) Aug 20 2013 Clever. I may have to add this to my personal code library. :)
- Dicebot (2/17) Aug 20 2013 It should have been T[0], not plain T, of course.
- Artur Skawina (5/17) Aug 22 2013 Keep in mind that 'typeof' is special - it won't evaluate the
- Andrej Mitrovic (6/8) Aug 20 2013 I am beginning to wonder if typeof(typeof(expr)) is worthy of an
- Jacob Carlborg (5/10) Aug 20 2013 As we have seen, not allowing typeof to accept types makes some generic
- Marek Janukowicz (22/29) Aug 20 2013 It's not always considered an error, this code compiles fine (my guess i...
- Dicebot (4/4) Aug 21 2013 Yes, "is" expression suppresses errors and simply returns "false"
- H. S. Teoh (20/30) Aug 20 2013 Hmm.
- Artur Skawina (5/37) Aug 22 2013 You do realize that this is the way the built-in tuples work already?
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
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
H. S. Teoh wrote:On Wed, Aug 21, 2013 at 01:42:11AM +0200, Marek Janukowicz wrote:Yes, I do understand this, however the behavior indicated below (your last question and my code) got me really confused.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.)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.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 JanukowiczWhat'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.
Aug 20 2013
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 enoughto 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.[...] 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.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 );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.
Aug 21 2013
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
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
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:It should have been T[0], not plain T, of course.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
Aug 20 2013
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
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
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
Andrej Mitrovic wrote:On 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote: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 Janukowiczand 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)?
Aug 20 2013
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
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: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. -- YHLand 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
On 08/21/13 02:43, H. S. Teoh wrote:On Wed, Aug 21, 2013 at 02:24:18AM +0200, Andrej Mitrovic wrote: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) arturOn 8/21/13, H. S. Teoh <hsteoh quickfur.ath.cx> wrote: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.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 22 2013