Bug 9065 – Please consider adding these std.traits

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
phobos
Product
D
Version
D2
Platform
All
OS
All
Creation time
2012-11-23T06:06:00Z
Last change time
2017-07-05T13:39:51Z
Keywords
pull
Assigned to
nobody
Creator
turkeyman

Comments

Comment #0 by turkeyman — 2012-11-23T06:06:00Z
I'm constantly writing static logic code where I have no real idea what I'm really doing, and when I finally get it working, it's unreadable. This usually involves lots of careful is()-ing and __traits-ing. I'd really appreciate these templates added to std.traits: Tell me if T is a variable; ie, has a value, has an address (not an enum), ... template isVariable(alias T) { ... } Tell me if T is a function definition, not just if I can call it. I can easily find if something is a function pointer/delegate, or a method, or virtual, or abstract, or various other function related details, but surprisingly, not if T is just a function definition or not. template isFunctionDefinition(alias T) { ... } I want to know if I is an instance of some template T. template isInstanceOf(alias T, I) { ... } Perhaps like this: http://dpaste.dzfl.pl/fedac944
Comment #1 by andrej.mitrovich — 2012-11-24T09:59:20Z
(In reply to comment #0) > template isInstanceOf(alias T, I) { ... } That one is now in Phobos: http://d.puremagic.com/issues/show_bug.cgi?id=9064
Comment #2 by turkeyman — 2012-11-24T10:59:39Z
(In reply to comment #1) > (In reply to comment #0) > > template isInstanceOf(alias T, I) { ... } > > That one is now in Phobos: http://d.puremagic.com/issues/show_bug.cgi?id=9064 Sweet. Any chance of the other 2? I'd really like to delete all my unreadable fail code asap.
Comment #3 by code — 2012-11-24T11:01:39Z
For the second point, what about is(T == function)? The is() syntax is not very popular (rightfully so), but I am not sure if we have a decision to replicate even its most basic use cases in std.traits yet.
Comment #4 by turkeyman — 2012-11-24T11:48:53Z
(In reply to comment #3) > For the second point, what about is(T == function)? The is() syntax is not very > popular (rightfully so), but I am not sure if we have a decision to replicate > even its most basic use cases in std.traits yet. Does that not test if T is some sort of function pointer? ...actually, that's some sort of special case overloaded meaning of the function keyword in conjunction with 'is' isn't it? I think I remember running into that like, a year ago. I recall being very confused at the time. Will that only pass in function definitions? Will exclude function pointers? The first point is by far the most important, it's the one I really have no idea how to do properly myself. I have templates that kinda work, but seem to break in some cases, and I use it constantly.
Comment #5 by code — 2012-11-24T11:51:44Z
(In reply to comment #4) > Does that not test if T is some sort of function pointer? > > ...actually, that's some sort of special case overloaded meaning of the > function keyword in conjunction with 'is' isn't it? I think I remember running > into that like, a year ago. I recall being very confused at the time. > Will that only pass in function definitions? Will exclude function pointers? Yes, this is one of the ugliest parts of the is() syntax. You are probably right, the fact alone that "function" has a different meaning here than in the rest of the language probably warrants inclusion of an isFunction() trait…
Comment #6 by issues.dlang — 2012-11-24T12:13:34Z
is(T == function) actually tests whether T is the type of a function, not whether it's a function pointer. This stackoverflow question discusses the differences between is(T == function), isFunctionPointer!T, isDelegate!T, and isSomeFunction!T: http://stackoverflow.com/questions/11067972/functions-versus-function-pointers-whats-the-difference A related issue is issue# 4427 We may want to add more of these to std.traits, but we should probably think carefully about exactly what we should add, especially because is expressions do most (all?) of it already, and often fairly cleanly. It's just that they're complicated enough in general and not understood well enough, that many people don't have a clue what they can do. It still may be worth adding stuff like isVariable to std.traits though.
Comment #7 by turkeyman — 2012-11-24T14:35:53Z
(In reply to comment #6) > is(T == function) actually tests whether T is the type of a function, not > whether it's a function pointer. This stackoverflow question discusses the > differences between is(T == function), isFunctionPointer!T, isDelegate!T, and > isSomeFunction!T: > > http://stackoverflow.com/questions/11067972/functions-versus-function-pointers-whats-the-difference Looking at that link, what's with all the typeof(bar) stuff? Do those tempaltes not work by alias? Surely I should be able to say isSomeFunction!bar, not requiring isSomeFunction!(typeof(bar)) ? Using typeof() like that makes generic code harder, because 'bar' could be anything, and if it doesn't have a typeof, then it is a different compile error and I have to produce even more logic prior to eliminate that case. Surely that could be encapsulated? > A related issue is issue# 4427 > > We may want to add more of these to std.traits, but we should probably think > carefully about exactly what we should add, especially because is expressions > do most (all?) of it already, and often fairly cleanly. It's just that they're > complicated enough in general and not understood well enough, that many people > don't have a clue what they can do. It still may be worth adding stuff like > isVariable to std.traits though. In my experience, code to the effect of isVariable is actually rather non-trivial. It would be very useful. Ideally, written by someone who knows what they're doing ;) (I clearly don't)
Comment #8 by turkeyman — 2012-11-24T14:41:05Z
> A related issue is issue# 4427 It's interesting to note that in your very first reply you were confused by is(T == function) too. I really think isFunction! should be added. It seems there is significant evidence to support it.
Comment #9 by issues.dlang — 2012-11-24T20:35:26Z
> Looking at that link, what's with all the typeof(bar) stuff? Do those > tempaltes not work by alias? Surely I should be able to say > isSomeFunction!bar, not requiring isSomeFunction!(typeof(bar)) ? std.traits operates on types, so I would not expect isSomeFunction!bar to ever work. You need to pass it a type. That's how everything in that module works.
Comment #10 by code — 2012-11-25T01:19:38Z
(In reply to comment #9) > std.traits operates on types, so I would not expect isSomeFunction!bar to ever > work. You need to pass it a type. That's how everything in that module works. This is definitely not true. Just have a look at isSomeFunction/isCallable resp. their unit tests.
Comment #11 by turkeyman — 2012-11-26T06:14:46Z
Okay, I've worked my templates a little more, consider these: // test if something is a type template isType( alias T ) { enum isType = is( T ); } template isType( T ) { enum isType = is( T ); } // test if something is a function // wrap this up, 'function' is an overloaded term, confusing for newbies template isFunction( alias T ) { enum isFunction = is( typeof( T ) == function ); } template isFunction( T ) { enum isFunction = is( T == function ); } // test if something is an enum (used by isVariable) template isEnum( alias T ) { template knownAtCompileTime( alias T ) { // enum knownAtCompileTime = is( typeof( { enum e = T; } ) ); // immutable breaks this check enum knownAtCompileTime = !__traits(compiles, ( ref typeof( T ) x ) {}( T ) ); // hack to see if we can pass it by ref } enum isEnum = is( typeof( T ) == enum ) || knownAtCompileTime!T; } template isEnum( T ) { enum isEnum = is( T == enum ); } // test if something is a variable; has a value, has an address template isVariable( alias T ) { enum isVariable = !is( T ) && is( typeof( T ) ) // if it is not a type, and does have a type, it starts to look like a variable && !isFunction!T // reject function definitions, they can't be assigned to && !isEnum!T // reject enum's && !is( typeof( T ) == void ); // reject modules, which appear as a variable of type 'void' } template isVariable( T ) { enum isVariable = false; // types aren't variables } This seems to be working for me now (caught a few more edge cases that would fail on occasion). I'd really like to see all of these in std.traits. It would be ideal if is() was a rare occurrence, it produces bracket spam, and most I work with barely understand it past expressions like: is(T == int)
Comment #12 by k.hara.pg — 2012-11-26T06:47:09Z
(In reply to comment #11) > Okay, I've worked my templates a little more, consider these: [snip] 1. I would never agree with isFunction. They introduce huge confusion but has no benefit. Blending traits about type and symbol is not a purpose of std.traits. 2. isEnum should be separated to isEnumType and isManifestConstant (with better name). Blending traits is very poor design. And, there is no use case. if you propose an enhancement, you should show one or more use cases in order to claim its usefulness. (I always doubt the reason like "for the newbie". Increase of language newbies is not worth than an increase of the design confusion in the standard library.)
Comment #13 by turkeyman — 2012-11-26T07:45:34Z
Turns out this is still incomplete: isEnum fails in some situations. Using 'compiles' like that is not reliable with various levels of aliases/templates. Also, that implementation of isVariable marks getter-properties as if they are variables, which they are not. Back to the drawing board...
Comment #14 by turkeyman — 2012-11-26T08:13:04Z
(In reply to comment #12) > (In reply to comment #11) > > Okay, I've worked my templates a little more, consider these: > [snip] > > 1. I would never agree with isFunction. They introduce huge confusion but has > no benefit. Blending traits about type and symbol is not a purpose of > std.traits. You would never agree with a std.traits to wrap is(x == function)? Why? It confuses everyone. Nobody expects that overload of meaning. > 2. isEnum should be separated to isEnumType and isManifestConstant (with better > name). Blending traits is very poor design. I've never heard this term 'manifest constant' before today. Here's a line of code: enum i = 10; I (and I presume any sane person) would say "that's an enum!". I appreciate there is a distinction between an enum type and an enum value, but they are both called enums, and I doubt any non-D-compiler-developer would presume otherwise. I don't care if they are separated, just that the terminology should make sense. > And, there is no use case. if you propose an enhancement, you should show one > or more use cases in order to claim its usefulness. I work in a studio on highly sensitive proprietary code. Isolating examples out of context is often difficult, annoying, and very time consuming. I don't have the time to convince you in this case. However, if there wasn't a use case, rest assured I wouldn't be posting here, and I wouldn't be wasting consecutive days of my time on it. I need to know these things, D doesn't provide this information; it should. Perhaps this will help: foreach(m; __traits(allMembers, something)) { // What on earth is m? // I think think you can argue this is unconventional code. // I need to know all sorts of things about m in this context, and I have absolutely no prior information. // 'is' traits shouldn't make me jump through bunches of hoops to gather information. } > (I always doubt the reason like "for the newbie". Increase of language newbies > is not worth than an increase of the design confusion in the standard library.) Confusion? Surely you mean simplification? Having 'function' mean 2 to different things in 2 contexts is confusing. Having enum mean 2 different things in almost precisely the same context is confusing. std.traits is meant to simplify my code and save me time. In my experience to date, it's severely lacking.
Comment #15 by turkeyman — 2012-11-26T08:24:02Z
(In reply to comment #13) > Turns out this is still incomplete: > > isEnum fails in some situations. Using 'compiles' like that is not reliable > with various levels of aliases/templates. > Also, that implementation of isVariable marks getter-properties as if they are > variables, which they are not. > > Back to the drawing board... I've become convinced I also need to add template isProperty(T) { ... } I can't find any sensible way to do this, and I'm also blocked without it.
Comment #16 by doob — 2012-11-26T09:59:57Z
(In reply to comment #14) > Perhaps this will help: > > foreach(m; __traits(allMembers, something)) > { > // What on earth is m? > // I think think you can argue this is unconventional code. > // I need to know all sorts of things about m in this context, and I have > absolutely no prior information. > // 'is' traits shouldn't make me jump through bunches of hoops to gather > information. > } I agree with you in general but what's wrong with that example? Sure, the double underscore prefix doesn't look that nice but except from that. What would the alternative be? Something like this: foreach (m; allMembers!(somethnig)) { // What on earth is m? } What's better with that code? The "allMembers" trait is clearly documented here: http://dlang.org/traits.html#allMembers "m" would be a string, since __traits(allMembers, something) returns a tuple of strings.
Comment #17 by turkeyman — 2012-11-26T10:18:05Z
(In reply to comment #16) > (In reply to comment #14) > > > Perhaps this will help: > > > > foreach(m; __traits(allMembers, something)) > > { > > // What on earth is m? > > // I think think you can argue this is unconventional code. > > // I need to know all sorts of things about m in this context, and I have > > absolutely no prior information. > > // 'is' traits shouldn't make me jump through bunches of hoops to gather > > information. > > } > > I agree with you in general but what's wrong with that example? Sure, the > double underscore prefix doesn't look that nice but except from that. What > would the alternative be? Something like this: > > foreach (m; allMembers!(somethnig)) > { > // What on earth is m? > } > > What's better with that code? The "allMembers" trait is clearly documented > here: > > http://dlang.org/traits.html#allMembers > > "m" would be a string, since __traits(allMembers, something) returns a tuple of > strings. *sigh* Sorry, I take no issue with allMembers. My point has nothing to do with allMembers except that it's a common source of symbols that you have no idea what they are. ** "what on earth is mixin(m)?" If you don't know what something is you need to query details about it, and with all the traits as britle and scarce as they are, chances are that leads to many compile errors and consequently rubbish code scattered to protect it against such errors, when rather, such 'is' traits should just produce 'false' instead of compile errors.
Comment #18 by code — 2012-11-26T11:11:13Z
(In reply to comment #14) > Here's a line of code: > enum i = 10; > > I (and I presume any sane person) would say "that's an enum!". > I appreciate there is a distinction between an enum type and an enum value, but > they are both called enums, and I doubt any non-D-compiler-developer would > presume otherwise. > > I don't care if they are separated, just that the terminology should make > sense. Yes, but the correct answer to this is to not overload the word "enum" with two different meanings. Unfortunately, this ship has long sailed, but this still doesn't mean that we should repeat this mistake in the standard library, further adding confusion. If you don't like the term manifest constant, maybe isEnumConstant/isEnumType?
Comment #19 by turkeyman — 2012-11-26T11:47:52Z
(In reply to comment #18) > (In reply to comment #14) > > Here's a line of code: > > enum i = 10; > > > > I (and I presume any sane person) would say "that's an enum!". > > I appreciate there is a distinction between an enum type and an enum value, but > > they are both called enums, and I doubt any non-D-compiler-developer would > > presume otherwise. > > > > I don't care if they are separated, just that the terminology should make > > sense. > > Yes, but the correct answer to this is to not overload the word "enum" with two > different meanings. Unfortunately, this ship has long sailed, but this still > doesn't mean that we should repeat this mistake in the standard library, > further adding confusion. If you don't like the term manifest constant, maybe > isEnumConstant/isEnumType? Sure, just as long as something useful is added to std.traits.
Comment #20 by k.hara.pg — 2012-11-26T20:14:55Z
(In reply to comment #12) > (In reply to comment #11) OK. I understood that Manu says. And my yesterday comment was completely wrong. Sorry for my misunderstanding. Certainly, isFunction is an important template which lacks in std.traits. Existing other function traits, isSomeFunction, isCallble, FunctionTypeOf, return true even if given function pointer or delegate. I can agree with the behavior is sometimes obstacle. > Blending traits about type and symbol is not a purpose of std.traits. And more, mixing symbol and type is *a design* of std.traits. I had misunderstood at the point. isSomeFunction is an example. /** Detect whether >>symbol or type<< $(D T) is a function, a function pointer or a delegate. */ template isSomeFunction(T...) { ... } I challenged implementing proposed template isVariable. Is this what you want? https://gist.github.com/4152297
Comment #21 by turkeyman — 2012-11-26T23:24:17Z
(In reply to comment #20) > (In reply to comment #12) > > (In reply to comment #11) > > OK. I understood that Manu says. And my yesterday comment was completely wrong. > Sorry for my misunderstanding. > > Certainly, isFunction is an important template which lacks in std.traits. > Existing other function traits, isSomeFunction, isCallble, FunctionTypeOf, > return true even if given function pointer or delegate. I can agree with the > behavior is sometimes obstacle. > > > Blending traits about type and symbol is not a purpose of std.traits. > > And more, mixing symbol and type is *a design* of std.traits. I had > misunderstood at the point. isSomeFunction is an example. > > /** > Detect whether >>symbol or type<< $(D T) is a function, a function pointer > or a delegate. > */ > template isSomeFunction(T...) { ... } > > I challenged implementing proposed template isVariable. Is this what you want? > > https://gist.github.com/4152297 It looks good, although I can't test it now. Does isVariable deal with properties (which I would say are NOT variables). And how about an isProperty? That's really hard to detect... I'm not sold on the term isManifestConstant, I'm sure no non-compiler developer would have ever heard that term before. I've never seen it in any docs before, and they're declared with 'enum', that's what most people will call it.
Comment #22 by issues.dlang — 2012-11-26T23:36:52Z
> I'm not sold on the term isManifestConstant, I'm sure no non-compiler > developer would have ever heard that term before. I've never seen it in any > docs before, and they're declared with 'enum', that's what most people will > call it. I don't know if isManifestConstant is the best name, but it's the official term, and I'm sure that a large portion of the regular newsgroup goers know what it is, not just the compiler devs. I don't know how many outside that group would know it however. Still, given that it's the official name, and that we don't really _have_ another name, I think that I'd still be in favor of isManifestConstant. And if we have isManifestConstant and isEnumType and no isEnum, then we avoid all of the confusion surrounding what exactly an enum is. And it's not like it will be hard for the docs to explain the terms.
Comment #23 by k.hara.pg — 2012-11-26T23:46:48Z
(In reply to comment #21) > (In reply to comment #20) > > I challenged implementing proposed template isVariable. Is this what you want? > > > > https://gist.github.com/4152297 > > It looks good, although I can't test it now. > Does isVariable deal with properties (which I would say are NOT variables). And > how about an isProperty? That's really hard to detect... Added isPropertyFunction (isProperty is a bit ambiguous name to me). I think it's a trait derived from isFunction. > I'm not sold on the term isManifestConstant, I'm sure no non-compiler developer > would have ever heard that term before. I've never seen it in any docs before, > and they're declared with 'enum', that's what most people will call it. http://dlang.org/features2.html > Extended enums to allow declaration of manifest constants. http://dlang.org/enum.html > Manifest Constants > If there is only one member of an anonymous enum, the { } can be omitted: > [snip] > Such declarations are not lvalues, meaning their address cannot be taken.
Comment #24 by turkeyman — 2012-11-27T03:57:58Z
(In reply to comment #22) > > I'm not sold on the term isManifestConstant, I'm sure no non-compiler > > developer would have ever heard that term before. I've never seen it in any > > docs before, and they're declared with 'enum', that's what most people will > > call it. > > I don't know if isManifestConstant is the best name, but it's the official > term, and I'm sure that a large portion of the regular newsgroup goers know > what it is, not just the compiler devs. I don't know how many outside that > group would know it however. Still, given that it's the official name, and that > we don't really _have_ another name, I think that I'd still be in favor of > isManifestConstant. And if we have isManifestConstant and isEnumType and no > isEnum, then we avoid all of the confusion surrounding what exactly an enum is. > And it's not like it will be hard for the docs to explain the terms. There is another name, it's called an 'enum', as clearly stated by the syntax: enum x = 10; Using any term other than that seems crazy by my measure. The user base aren't compiler developers. I don't even know what manifest means. I don't think the distinction isEnumType, isEnumValue is at all confusing, in fact, the function names alone document the important detail that there IS a distinction, and what it is. (In reply to comment #23) > (In reply to comment #21) > > (In reply to comment #20) > > > I challenged implementing proposed template isVariable. Is this what you want? > > > > > > https://gist.github.com/4152297 > > > > It looks good, although I can't test it now. > > Does isVariable deal with properties (which I would say are NOT variables). And > > how about an isProperty? That's really hard to detect... > > Added isPropertyFunction (isProperty is a bit ambiguous name to me). > I think it's a trait derived from isFunction. This highlights another conflict in terminology, currently a property is NOT recognised as a function (at least in my crappy tests). I really think isFunction!someProperty should be true. It's a function, exactly like any other, I can take pointers/delegates of it, it just has a particular usage semantic. Effectively a subset, not a different concept. So I suggest isFunction! should give true for a property function definition. > > I'm not sold on the term isManifestConstant, I'm sure no non-compiler developer > > would have ever heard that term before. I've never seen it in any docs before, > > and they're declared with 'enum', that's what most people will call it. > > http://dlang.org/features2.html > > Extended enums to allow declaration of manifest constants. This clearly implies that 'manifest constant' is in fact some kind of enum. > http://dlang.org/enum.html > > Manifest Constants > > If there is only one member of an anonymous enum, the { } can be omitted: > > [snip] > > Such declarations are not lvalues, meaning their address cannot be taken. Again, referring to 'anonymous enum', it's not distancing its self from the enum terminology. ... I just asked 2 other programmers in the room what enum x = 10; is, they said it's an enum, not a manifest constant ;) None of them could tell me what a manifest constant it, or that they'd ever heard of such a thing before. But I don't actually care at the end of the day, I'm just trying to give a voice of reason. What the compiler calls stuff internally has no bearing on what users of the language will call things. The user facing library should endeavour to match the terminology used by the users IMO.
Comment #25 by turkeyman — 2012-11-27T04:06:02Z
(In reply to comment #24) > This highlights another conflict in terminology, currently a property is NOT > recognised as a function (at least in my crappy tests). I really think > isFunction!someProperty should be true. It's a function, exactly like any > other, I can take pointers/delegates of it, it just has a particular usage > semantic. Effectively a subset, not a different concept. > > So I suggest isFunction! should give true for a property function definition. Sorry! I just saw your unit test asserts isFunction! is true for property functions. I didn't see that behaviour with is(X==function) in my tests.
Comment #26 by andrej.mitrovich — 2012-11-27T04:14:28Z
(In reply to comment #24) > What the compiler calls stuff internally has no bearing on > what users of the language will call things. It's not an internal thing, that declaration is not an enum declaration, period. enum is used as a keyword for more than one thing, which is bad, but it's too late to change it now. We shouldn't name things in Phobos based on what people might think is right or looks right, but based on what the things really are. It's a shame we don't have a 'manifest' keyword of some sort, it would help avoid confusion. I guess 'enum' was used to cut back on having too many keywords in the language. Anyway it's documented behavior, see "manifest constants" here (it's at the bottom): http://dlang.org/enum.html
Comment #27 by andrej.mitrovich — 2012-11-27T04:15:48Z
(In reply to comment #26) > Anyway it's documented behavior, see "manifest constants" here (it's at the > bottom): http://dlang.org/enum.html Actually it's poorly documented, it speaks about "anon" enums but it shouldn't really mention them at all imo.
Comment #28 by doob — 2012-11-27T04:41:46Z
(In reply to comment #26) > It's a shame we don't have a 'manifest' keyword of some sort, it would help > avoid confusion. I guess 'enum' was used to cut back on having too many > keywords in the language. Isn't the whole problem that the compiler and/or linker isn't capable of stripping out symbols that are only used at compile time.
Comment #29 by turkeyman — 2012-11-27T04:51:33Z
(In reply to comment #26) > (In reply to comment #24) > > What the compiler calls stuff internally has no bearing on > > what users of the language will call things. > > It's not an internal thing, that declaration is not an enum declaration, > period. enum is used as a keyword for more than one thing, which is bad, but > it's too late to change it now. > > We shouldn't name things in Phobos based on what people might think is right or > looks right, but based on what the things really are. I couldn't disagree more. Firstly, what it is clearly documented as, is being some subset of enum. That's what it 'really is'. More importantly, if I look at the top of std.traits for something I presume to be called isEnum, and I see isEnumValue, I'll realise that's actually what I'm looking for immediately, use it, and save myself time. If I see something called isEnum, and it doesn't seem to work because it only reports true for enum TYPES (not values), then I'll declare it broken and report a bug. If I scan through everything in std.traits, and find nothing that looks like what I want, I'll get frustrated the thing I need is missing. If I see isManifestConstant, there is _NO WAY_ I would have even read what that is, it's clearly not what I'm looking for, I'm trying to identify if my thing is an enum... > It's a shame we don't have a 'manifest' keyword of some sort, it would help > avoid confusion. I guess 'enum' was used to cut back on having too many > keywords in the language. I honestly don't even know what manifest means. The terminology used in the syntax and the documentation is correct; it's a kind of enum. > Anyway it's documented behavior, see "manifest constants" here (it's at the > bottom): http://dlang.org/enum.html And every place it appears, it is clearly defined as being some subset of enum. Again, if I go looking for isEnum, and find a suite of more specific enum related traits (isEnumType, isEnumValue), I can easily conclude which is the one I'm looking for. If the name is completely unrelated and uses terminology most programmers have never heard before, they'll never spot it. As a side point, what do you call X in: enum E { X = 10 } ? Consider: enum E { X = 10 } enum Y = 10; E.X and Y are both identical as far as I can tell. I would presume: isEnumType!E == true, and isEnumValue!(E.X) == isEnumValue!Y == true. You can tweak the names, but I think traits to that effect are a) useful, b) what (I presume) most typical users will expect. E.X and Y are identical. I think this specialised term 'manifest constant' that only applies to Y can only result in confusion.
Comment #30 by turkeyman — 2012-11-27T04:53:31Z
(In reply to comment #28) > (In reply to comment #26) > > > It's a shame we don't have a 'manifest' keyword of some sort, it would help > > avoid confusion. I guess 'enum' was used to cut back on having too many > > keywords in the language. > > Isn't the whole problem that the compiler and/or linker isn't capable of > stripping out symbols that are only used at compile time. It's also one of the things that D got absolutely right! I never questioned this design for a moment, it makes perfect sense to me, but as of yesterday my concept of what an enum is has been thrown up in the air for absolutely no good reason other than terminology used by the D developers.
Comment #31 by turkeyman — 2012-11-27T05:37:31Z
(In reply to comment #20) > https://gist.github.com/4152297 I am seeing a few error cases: enum j { k = 10 } pragma(msg, isFunction!(j.k)); pragma(msg, isManifestConstant!(j.k)); pragma(msg, isPropertyFunction!(j.k)); pragma(msg, isVariable!(j.k)); These all throw errors.
Comment #32 by andrej.mitrovich — 2012-11-27T06:25:56Z
(In reply to comment #29) > As a side point, what do you call X in: enum E { X = 10 } ? > > Consider: > enum E { X = 10 } > enum Y = 10; > > E.X and Y are both identical as far as I can tell. Run typeid() on them. > And every place it appears, it is clearly defined as being some subset of enum. Well it wouldn't be the first time the docs lie. :) "enum Y = 10;" is a special case which the compiler checks for and then parses this as a manifest constant, it does not parse it as an enum. "Y" is not an enum value of any sort, Y is a VarDeclaration with storage class STCmanifest. Here: enum E { X = 10 } X is an EnumMember. Just to make this clear, these two are different: enum { X = 10 } enum Y = 10; X is an enum value, whereas Y is a manifest constant and is not associated with enums at all in any way. The issue here is that the "enum" keyword can be a lie. It can mean two things depending on the declaration. It also seems that internally the idea of a manifest keyword was thought about, there's a bunch of commented out lines like so: //case TOKmanifest: stc = STCmanifest; goto Lstc; I wonder who put that in and why it wasn't used..
Comment #33 by turkeyman — 2012-11-27T06:43:36Z
(In reply to comment #32) > (In reply to comment #29) > > As a side point, what do you call X in: enum E { X = 10 } ? > > > > Consider: > > enum E { X = 10 } > > enum Y = 10; > > > > E.X and Y are both identical as far as I can tell. > > Run typeid() on them. > > > And every place it appears, it is clearly defined as being some subset of enum. > > Well it wouldn't be the first time the docs lie. :) > > "enum Y = 10;" is a special case which the compiler checks for and then parses > this as a manifest constant, it does not parse it as an enum. "Y" is not an > enum value of any sort, Y is a VarDeclaration with storage class STCmanifest. > > Here: > enum E { X = 10 } > > X is an EnumMember. > > Just to make this clear, these two are different: > enum { X = 10 } > enum Y = 10; > > X is an enum value, whereas Y is a manifest constant and is not associated with > enums at all in any way. That's not really how the doc describes it: "If there is only one member of an anonymous enum, the { } can be omitted:" Either way, it's a perfectly reasonably way to visualise it as a user. As I said before, I couldn't care less about internal compiler terminology, it looks like an anonymous enum value, and the doc even says it's an anonymous enum value. It must be so. The doc is correct as far as I'm concerned, it makes perfect sense, the language shouldn't be defined by implementation details of DMD. > The issue here is that the "enum" keyword can be a lie. It can mean two things > depending on the declaration. > > It also seems that internally the idea of a manifest keyword was thought about, > there's a bunch of commented out lines like so: > //case TOKmanifest: stc = STCmanifest; goto Lstc; > > I wonder who put that in and why it wasn't used.. I'll bet it was realised during implementation that it is actually syntactic sugar for an anonymous enum, and the enum keyword was completely appropriate :)
Comment #34 by turkeyman — 2012-11-27T06:45:04Z
@Kenji: I'm getting an error when using your new isFunction, here's the case: struct S { static string func(alias Class)() { foreach(m; __traits(allMembers, Class)) { pragma(msg, m); static if( isFunction!( mixin( m ) ) ) bool b = true; } } enum nothing = func!(typeof(this))(); mixin(nothing); }
Comment #35 by turkeyman — 2012-11-27T06:54:34Z
I've just this afternoon encountered another one that I can't work out a nice way to discover: struct S { static int x; } bool b = isStatic!(S.x); // <- I can't think of a nice clean way to prove this other than trying to assign to it, which is horrible.
Comment #36 by dmitry.olsh — 2012-11-27T07:16:39Z
(In reply to comment #35) > I've just this afternoon encountered another one that I can't work out a nice > way to discover: > > struct S > { > static int x; > } > > bool b = isStatic!(S.x); // <- I can't think of a nice clean way to prove this > other than trying to assign to it, which is horrible. Trying to take the address should do the trick. is(typeof(&S.x)) that is.
Comment #37 by k.hara.pg — 2012-11-27T07:19:03Z
(In reply to comment #31) > (In reply to comment #20) > > https://gist.github.com/4152297 > > I am seeing a few error cases: > > enum j { k = 10 } > > pragma(msg, isFunction!(j.k)); > pragma(msg, isManifestConstant!(j.k)); > pragma(msg, isPropertyFunction!(j.k)); > pragma(msg, isVariable!(j.k)); > > These all throw errors. Fixed. (In reply to comment #34) > @Kenji: > > I'm getting an error when using your new isFunction, here's the case: > > struct S > { > static string func(alias Class)() > { > foreach(m; __traits(allMembers, Class)) > { > pragma(msg, m); > static if( isFunction!( mixin( m ) ) ) > bool b = true; > } > } > > enum nothing = func!(typeof(this))(); > mixin(nothing); > } This is a compiler bug, not an issue of isFunction template. I filed new bug 9083.
Comment #38 by doob — 2012-11-27T07:35:19Z
(In reply to comment #36) > Trying to take the address should do the trick. > is(typeof(&S.x)) that is. That won't work for methods, but that might not be the use case.
Comment #39 by turkeyman — 2012-11-27T07:52:20Z
(In reply to comment #36) > Trying to take the address should do the trick. > is(typeof(&S.x)) that is. And if I have: S s; bool b = isStatic!(s.x); That solution doesn't work if 's' is an instance. I'm sick of writing really brittle code, I'm just saying that is another important std.traits. There's no trivial way to do this for an _anything_ that I can work out.
Comment #40 by turkeyman — 2012-11-27T07:59:18Z
(In reply to comment #38) > That won't work for methods, but that might not be the use case. Yeah, if it were in std.traits, I would expect it to work on methods too. (In reply to comment #37) > (In reply to comment #31) > > I am seeing a few error cases: > > > > enum j { k = 10 } > > > > pragma(msg, isFunction!(j.k)); > > pragma(msg, isManifestConstant!(j.k)); > > pragma(msg, isPropertyFunction!(j.k)); > > pragma(msg, isVariable!(j.k)); > > > > These all throw errors. > > Fixed. Huzzah! > This is a compiler bug, not an issue of isFunction template. > I filed new bug 9083. Amazing! You sir are awesome! :)
Comment #41 by turkeyman — 2012-11-27T09:26:36Z
(In reply to comment #37) > This is a compiler bug, not an issue of isFunction template. > I filed new bug 9083. I found another one, probably a compiler bug too, but I'll paste it here since it depends on your new isVariable trait: Boiled down as much as I could... class Test { void func() { void templateFunc( T )( ref const T obj ) { foreach( m; __traits( allMembers, T ) ) { pragma(msg, m); static if( isVariable!( __traits(getMember, T, m) ) ) { //... } } } templateFunc( this ); } // some class members int x; } isVariable throws lots of errors when considering the class members. Note: __traits(allMembers, T) and __traits(getMember, T, m) These should also work with class instances, not just types, eg: __traits(allMembers, obj) and __traits(getMember, obj, m) And these combinations should also work: __traits(allMembers, T) and __traits(getMember, obj, m) __traits(allMembers, obj) and __traits(getMember, T, m) All these configurations throw errors, and the errors are different for each configuration.
Comment #42 by alanb — 2012-11-27T22:39:06Z
(In reply to comment #26) > (In reply to comment #24) > > What the compiler calls stuff internally has no bearing on > > what users of the language will call things. > > It's not an internal thing, that declaration is not an enum declaration, > period. enum is used as a keyword for more than one thing, which is bad, but > it's too late to change it now. > > We shouldn't name things in Phobos based on what people might think is right or > looks right, but based on what the things really are. > > It's a shame we don't have a 'manifest' keyword of some sort, it would help > avoid confusion. I guess 'enum' was used to cut back on having too many > keywords in the language. > > Anyway it's documented behavior, see "manifest constants" here (it's at the > bottom): http://dlang.org/enum.html As far as I was concerned, these are anonymous enum members in the case where there's only one member and the { } were omitted. I understood it in this way by reading the specification here: http://dlang.org/enum.html. It actual made some sense to me. The use of "manifest constant" terminology elsewhere will likely cause a lot of confusion. Why not term it as an "anonymous enum constant", or "enum manifest constant" if that suits the usage better. Term it anything so long as "enum" is in there so as to avoid the inevitable confusion elsewhere.
Comment #43 by clugdbug — 2012-11-28T00:36:26Z
(In reply to comment #42) > (In reply to comment #26) > > (In reply to comment #24) > > > What the compiler calls stuff internally has no bearing on > > > what users of the language will call things. > > > > It's not an internal thing, that declaration is not an enum declaration, > > period. enum is used as a keyword for more than one thing, which is bad, but > > it's too late to change it now. > > > > We shouldn't name things in Phobos based on what people might think is right or > > looks right, but based on what the things really are. > > > > It's a shame we don't have a 'manifest' keyword of some sort, it would help > > avoid confusion. I guess 'enum' was used to cut back on having too many > > keywords in the language. > > > > Anyway it's documented behavior, see "manifest constants" here (it's at the > > bottom): http://dlang.org/enum.html > > As far as I was concerned, these are anonymous enum members in the case where > there's only one member and the { } were omitted. I understood it in this way > by reading the specification here: http://dlang.org/enum.html. It actual made > some sense to me. > > The use of "manifest constant" terminology elsewhere will likely cause a lot of > confusion. Why not term it as an "anonymous enum constant", or "enum manifest > constant" if that suits the usage better. Term it anything so long as "enum" is > in there so as to avoid the inevitable confusion elsewhere. The history of this was: In D1, const XXX = YYY; declared a manifest constant. You couldn't take the address of it. With the change to const in D2, this no longer worked. I argued that we needed a way of doing manifest constants. Walter started implementing it as 'manifest'. Andrei argued for it to reuse 'enum' to reduce the number of keywords. Almost everybody was angry about overloading 'enum' and there was a huge fight, but Andrei won in the end. Yes it causes confusion. They are not enums, there is nothing enumerated about them. It's just an overloaded keyword.
Comment #44 by turkeyman — 2012-11-28T01:27:11Z
(In reply to comment #43) > The history of this was: > > In D1, const XXX = YYY; declared a manifest constant. You couldn't take the > address of it. With the change to const in D2, this no longer worked. I argued > that we needed a way of doing manifest constants. Walter started implementing > it as 'manifest'. Andrei argued for it to reuse 'enum' to reduce the number of > keywords. Almost everybody was angry about overloading 'enum' and there was a > huge fight, but Andrei won in the end. Yes it causes confusion. > > They are not enums, there is nothing enumerated about them. It's just an > overloaded keyword. Andrei was absolutely correct, as far as any user is concerned: enum E { K = 10 } // named enum enum { K = 10 } // anonymous enum enum K = 10; // obvious sugar to save bracket spam As said previously, I give exactly zero shits how it's implemented internally, or the history of it. The way it's documented now and the syntax chosen makes perfect sense, and any attempt to make K be somehow distinct in any of these cases is a mistake which will only lead to confusion. If E is an enum type, then K is an enum value, or an enum constant... (i like enum value better; 'constant' is verbose is implicit) Why would any normal user reasonably expect the term 'manifest' (which I still have no idea what it means) to appear in this context?
Comment #45 by alanb — 2012-11-28T01:43:28Z
(In reply to comment #43) > The history of this was: > > In D1, const XXX = YYY; declared a manifest constant. You couldn't take the > address of it. With the change to const in D2, this no longer worked. I argued > that we needed a way of doing manifest constants. Walter started implementing > it as 'manifest'. Andrei argued for it to reuse 'enum' to reduce the number of > keywords. Almost everybody was angry about overloading 'enum' and there was a > huge fight, but Andrei won in the end. Yes it causes confusion. > > They are not enums, there is nothing enumerated about them. It's just an > overloaded keyword. Interesting history, but the thing is that from a D users POV, it is a member of an anonymous enum, and that does not change however it may be internally represented, or historically thought of. I look at it this way enum { a = 2 } // anonymous enum set to 2 with one member enum a = 2; // anonymous enum set to 2 with one member and optional {} removed enum a; // anonymous enum unset with one member and optional {} removed How is this not what the specification says it is? How it may be internally represented is invisible to the user, what the user sees is only what is seen in terms of how the language represents the concept. This is an enum, and if it is not, then that only matters if it does not behave like all the other enum members, otherwise it may as well be viewed as if it is an enum member. I suspect this was Andrei's argument, because it actually does fit nicely into place. If I'm going to get mentally confabulated again because it does not work as I would expect an enum to work, then I'll be up in arms about it for sure, so I'm interested in knowing exactly how it is not like an enum, not internally, not historically, but in terms of usage from a programmers POV?
Comment #46 by andrej.mitrovich — 2012-11-28T03:06:54Z
(In reply to comment #45) > interested in knowing exactly how it is not like an enum, not internally, not > historically, but in terms of usage from a programmers POV? It doesn't enumerate anything? Enumerations are collections of items, aren't they? How is one element a collection?
Comment #47 by turkeyman — 2012-11-28T04:05:14Z
(In reply to comment #46) > (In reply to comment #45) > > interested in knowing exactly how it is not like an enum, not internally, not > > historically, but in terms of usage from a programmers POV? > > It doesn't enumerate anything? Enumerations are collections of items, aren't > they? How is one element a collection? Then you need to disallow assigning explicit values to enum keys, that's not strictly an enumeration either. It just so happens that anonymous enums containing only a single value have a nice little sugar such that you don't need to write the brackets. This is because, in practise, this turns out to be a surprisingly common usage case so the little sugar is nice.
Comment #48 by clugdbug — 2012-11-28T06:39:16Z
(In reply to comment #47) > (In reply to comment #46) > > (In reply to comment #45) > > > interested in knowing exactly how it is not like an enum, not internally, not > > > historically, but in terms of usage from a programmers POV? > > > > It doesn't enumerate anything? Enumerations are collections of items, aren't > > they? How is one element a collection? > > Then you need to disallow assigning explicit values to enum keys, that's not > strictly an enumeration either. > It just so happens that anonymous enums containing only a single value have a > nice little sugar such that you don't need to write the brackets. This is > because, in practise, this turns out to be a surprisingly common usage case so > the little sugar is nice. I believe the similarity you've listed is false. An enumerated type must be integral (it must be countable!) A manifest constant, however, can be anything - a struct literal, for example. I think what has happened is that C had a very sloppy enum design, where it mixed integral manifest constants with enumerated types. Instead of tightening up 'enum', we used the existing sloppiness as an excuse to make it worse.
Comment #49 by turkeyman — 2012-11-28T06:59:34Z
(In reply to comment #48) > I believe the similarity you've listed is false. An enumerated type must be > integral (it must be countable!) A manifest constant, however, can be anything > - a struct literal, for example. ... really? But D supports typing enums: enum E : string { X = "hello", Y = "world" } I tried: enum E : string { X = "hello", Y } Sure, this would normally try and increment by one, but since it's not integral I got this error: incompatible types for (("hello") + (1)): 'S' and 'int' Makes perfect sense to me, apparently you just lose the auto-increment behaviour if the enums are not an integeral type? (although the error message could be clearer) So perhaps you mean, "must be integral if you want auto-increment behaviour"? Often enough you don't even want auto-increment, you want a collection of meaningful pre-defined values, I think that's just as good a definition of the term 'enumeration', auto-increment is not fundamental to this enumerated type concept in my mind. In fact, I think I might even use all-explicit values more often than auto-incremented ones in my code. > I think what has happened is that C had a very sloppy enum design, where it > mixed integral manifest constants with enumerated types. Instead of tightening > up 'enum', we used the existing sloppiness as an excuse to make it worse. Worse? It's perfect... >_<
Comment #50 by turkeyman — 2012-11-28T07:17:41Z
Can I just add, we've derailed my actual bug thread here >_< This is an extremely important issue for me, which has been lost in the meaning of enum and whether 'manifest constant' is a stupid name or not. Any word on isStatic!(), good/bad one to have (I don't know how to do it manually)? These traits are of critical importance to me to help a bunch of non-D programmers feel that the language is awesome and intuitive; ie, 'can understand it when they read the code', and assure it is maintainable going forward.
Comment #51 by issues.dlang — 2012-11-28T08:14:54Z
> An enumerated type must be integral (it must be countable!) This is not true at all in D. An enum can be any type which can be compared and can be use to directly initialize a variable at compile time. On top of that including built-in types like strings, TDPL specifically talks about declaring enums which are structs - and that's enums with a list of values, not manifest constants. It's incredibly valuable to be able to have enumerated lists of user-defined tpes rather than just integral values. I have no problem whatsoever calling enums which are manifest constants manifest constants rather than enums, but at the same time, the only differences between them and normal enums are the fact that they don't have a separate enum type and there's only one of them. Practically speaking, there's really no other difference. Neither have addresses. They're just values that get copy-pasted when used. So, while I have no problem with the term manifest constant, I don't think that it would cause any real problem to refer to them as anonymous enums instead. I do kind of wish that we'd gone with the keyword manifest over enum to better separate the two concepts, because manifest constants are actually enumerated, but from a pratical point of view, it's really a non-issue IMHO. If anything, normal enums are simply an enumeration of manifest constants.
Comment #52 by rjmcguire — 2015-09-18T15:34:13Z
(In reply to Manu from comment #11) > template isEnum( T ) > { > enum isEnum = is( T == enum ); > } > > // test if something is a variable; has a value, has an address > template isVariable( alias T ) > { > enum isVariable = !is( T ) && is( typeof( T ) ) // if it is not a type, and > does have a type, it starts to look like a variable > && !isFunction!T // reject function definitions, they can't be > assigned to > && !isEnum!T // reject enum's > && !is( typeof( T ) == void ); // reject modules, which appear as a > variable of type 'void' > } Thanks for this, I used this in Adam's jsvar to get a more meaningful conversion from enum to bool which was initially causing the module to not be valid because isIntegral!(some enum) == true.
Comment #53 by jack — 2016-01-18T17:04:06Z