www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - is(x = module) vs. __traits(isModule, x)

reply Steven Schveighoffer <schveiguy gmail.com> writes:
Someone on the discord chat brought this up:

We currently have 2 ways to check if a symbol is a module:

import std.stdio;

pragma(msg, is(std.stdio == module)); // true
pragma(msg, __traits(isModule, std.stdio)); // true

Why both? The first thing that struck me is that, std.stdio is NOT a 
type. `is` specifically says it works with types. So that seems out of 
place (indeed the documentation does not mention anything special about 
this).

And then I looked up when they were added, thinking that maybe one 
predates the other, and there is a reason it didn't handle all cases.

Wrong. They were both added in the *same PR* to mean the *same thing*: 
https://github.com/dlang/dmd/pull/5290

Is there a reason we want to have both? Why did we want to muddy the 
`is` waters?

I really think we should remove one of these, I would recommend removing 
the `is` form, since it's the only exception to the rule that the 
parameter must be a type.

BTW, same with packages:

is(std == package) == __traits(isPackage, std);

-Steve
Oct 07 2020
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven 
Schveighoffer wrote:
 Wrong. They were both added in the *same PR* to mean the *same 
 thing*: https://github.com/dlang/dmd/pull/5290

 Is there a reason we want to have both? Why did we want to 
 muddy the `is` waters?
I tried reading through the PR discussion to see how things ended up this way. The original PR includes only the __traits versions. The is() version was first proposed in a comment by Timothee Cour [1], and supported by several others including Andrei [2]. The PR author continues work on an implementation that includes only the is() versions, until a few months later, Nicholas Wilson leaves the following comment [3]:
 It was decided to have both.
The rationale behind this decision is never elaborated on, and the PR is eventually merged with both versions. [1] https://github.com/dlang/dmd/pull/5290#issuecomment-366826557 [2] https://github.com/dlang/dmd/pull/5290#issuecomment-462884161 [3] https://github.com/dlang/dmd/pull/5290#issuecomment-491509238
Oct 07 2020
prev sibling next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 07, 2020 at 02:46:07PM -0400, Steven Schveighoffer via
Digitalmars-d wrote:
[...]
 We currently have 2 ways to check if a symbol is a module:
 
 import std.stdio;
 
 pragma(msg, is(std.stdio == module)); // true
 pragma(msg, __traits(isModule, std.stdio)); // true
[...]
 I really think we should remove one of these, I would recommend
 removing the `is` form, since it's the only exception to the rule that
 the parameter must be a type.
I'm on the fence about this one. Having to type __traits(isModule,xxx) is painful. Is it really necessary to make it so hard just to check whether a symbol is a module?? It's not as if it's something we want to discourage. Typing is(xxx == module) is much easier, and more pleasant on the eyes. OTOH you have a very good point about is(...) being intended to work only for types. So I dunno.
 BTW, same with packages:
 
 is(std == package) == __traits(isPackage, std);
[...] Ditto. T -- Knowledge is that area of ignorance that we arrange and classify. -- Ambrose Bierce
Oct 07 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 7 October 2020 at 19:07:34 UTC, H. S. Teoh wrote:
 I'm on the fence about this one. Having to type 
 __traits(isModule,xxx) is painful.  Is it really necessary to 
 make it so hard just to check whether a symbol is a module??  
 It's not as if it's something we want to discourage.  Typing 
 is(xxx == module) is much easier, and more pleasant on the eyes.
IMO this is one of those situations where your pain is trying to tell you something. If using __traits syntax is so painful that we're willing to add hacky special cases to other language features in order to avoid it, maybe the message we should take from that is "we need to improve __traits syntax." Here's my strawman proposal: turn all __traits into properties. A few before-and-after examples: __traits(isModule, foo) => foo.__isModule __traits(getMember, T, "x") => T.__member("x") __traits(compiles, some(kind, of + expression)) => (some(kind, of + expression)).__compiles
Oct 07 2020
next sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 07, 2020 at 07:15:30PM +0000, Paul Backus via Digitalmars-d wrote:
 On Wednesday, 7 October 2020 at 19:07:34 UTC, H. S. Teoh wrote:
 I'm on the fence about this one. Having to type
 __traits(isModule,xxx) is painful.  Is it really necessary to make
 it so hard just to check whether a symbol is a module??  It's not as
 if it's something we want to discourage.  Typing is(xxx == module)
 is much easier, and more pleasant on the eyes.
IMO this is one of those situations where your pain is trying to tell you something. If using __traits syntax is so painful that we're willing to add hacky special cases to other language features in order to avoid it, maybe the message we should take from that is "we need to improve __traits syntax."
[...] Actually, I think that *was* the original intent of the __traits syntax. It was meant to be a quick-n-dirty way of exposing various compiler internal structures to Phobos so that it can implement various meta-programming primitives. It was intentionally ugly in order to discourage users from using it directly, and to prefer the nicer packaging in Phobos instead. However, the way things turned out, it appears that __traits has become sorta a de facto standard for metaprogramming primitives, and Phobos has kinda fallen behind on the job. So perhaps it's time to rethink this decision? T -- Be in denial for long enough, and one day you'll deny yourself of things you wish you hadn't.
Oct 07 2020
parent reply Jacob Carlborg <doob me.com> writes:
On Wednesday, 7 October 2020 at 19:44:59 UTC, H. S. Teoh wrote:

 However, the way things turned out, it appears that __traits 
 has become sorta a de facto standard for metaprogramming 
 primitives, and Phobos has kinda fallen behind on the job.  So 
 perhaps it's time to rethink this decision?
Yes, because just wrapping existing `__traits` in templates don't give you much value, just a different syntax. But it comes with all the problems that templates bring (which have been discussed many times). -- /Jacob Carlborg
Oct 08 2020
parent reply Max Samukha <maxsamukha gmail.com> writes:
On Thursday, 8 October 2020 at 09:42:22 UTC, Jacob Carlborg wrote:
 On Wednesday, 7 October 2020 at 19:44:59 UTC, H. S. Teoh wrote:


 Yes, because just wrapping existing `__traits` in templates 
 don't give you much value, just a different syntax.
Wrapping __traits in templates is a necessity if you want to use them for anything interesting (such as passing them to higher order functions): filter!(__traits(isPOD), A, B C); // no way filter!(isPod, A, B, C); // can be
Oct 08 2020
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 On Thursday, 8 October 2020 at 09:42:22 UTC, Jacob Carlborg 
 wrote:
 On Wednesday, 7 October 2020 at 19:44:59 UTC, H. S. Teoh wrote:


 Yes, because just wrapping existing `__traits` in templates 
 don't give you much value, just a different syntax.
Wrapping __traits in templates is a necessity if you want to use them for anything interesting (such as passing them to higher order functions): filter!(__traits(isPOD), A, B C); // no way filter!(isPod, A, B, C); // can be
or you use type functions which can use them at ctfe;)
Oct 08 2020
parent Max Samukha <maxsamukha gmail.com> writes:
On Thursday, 8 October 2020 at 11:47:48 UTC, Stefan Koch wrote:

 filter!(__traits(isPOD), A, B C); // no way
 filter!(isPod, A, B, C); // can be
or you use type functions which can use them at ctfe;)
You'd still have to wrap __traits in a type function to pass it to another function?
Oct 08 2020
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 Wrapping __traits in templates is a necessity if you want to 
 use them for anything interesting (such as passing them to 
 higher order functions):
I kinda wish we had a template lambda.
Oct 08 2020
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 8 October 2020 at 12:54:49 UTC, Adam D. Ruppe wrote:
 On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 Wrapping __traits in templates is a necessity if you want to 
 use them for anything interesting (such as passing them to 
 higher order functions):
I kinda wish we had a template lambda.
you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?
Oct 08 2020
parent reply Nick Treleaven <nick geany.org> writes:
On Thursday, 8 October 2020 at 13:04:18 UTC, Stefan Koch wrote:
 On Thursday, 8 October 2020 at 12:54:49 UTC, Adam D. Ruppe 
 wrote:
 I kinda wish we had a template lambda.
you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?
Not sure what __top is, but the compiles trait is special in that it can't be wrapped in a template (it's not really a trait of a symbol or expression like its type, but a deeper semantic analysis of an expression). 'Template lambda' is really anonymous templates: enum(alias Sym) => __traits(isModule, Sym)
Oct 11 2020
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 11 October 2020 at 11:30:00 UTC, Nick Treleaven wrote:
 On Thursday, 8 October 2020 at 13:04:18 UTC, Stefan Koch wrote:
 On Thursday, 8 October 2020 at 12:54:49 UTC, Adam D. Ruppe 
 wrote:
 I kinda wish we had a template lambda.
you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?
Not sure what __top is, but the compiles trait is special in that it can't be wrapped in a template (it's not really a trait of a symbol or expression like its type, but a deeper semantic analysis of an expression). 'Template lambda' is really anonymous templates: enum(alias Sym) => __traits(isModule, Sym)
__top is the top type. that which can hold anything. symbols, complete expressions, types, values, tuples. anything.
Oct 11 2020
parent reply Nick Treleaven <nick geany.org> writes:
On Sunday, 11 October 2020 at 11:48:23 UTC, Stefan Koch wrote:
 __top is the top type.
 that which can hold anything.
 symbols, complete expressions, types, values, tuples.
 anything.
OK thanks. Can it also implicitly hold the context that the expression was used in? If not I don't think we can wrap __traits(compiles).
Oct 11 2020
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 11 October 2020 at 14:32:17 UTC, Nick Treleaven wrote:
 On Sunday, 11 October 2020 at 11:48:23 UTC, Stefan Koch wrote:
 __top is the top type.
 that which can hold anything.
 symbols, complete expressions, types, values, tuples.
 anything.
OK thanks. Can it also implicitly hold the context that the expression was used in? If not I don't think we can wrap __traits(compiles).
Well ... I have to think about that. Sounds hairy if you ask me.
Oct 11 2020
prev sibling next sibling parent Max Samukha <maxsamukha gmail.com> writes:
On Thursday, 8 October 2020 at 12:54:49 UTC, Adam D. Ruppe wrote:

 I kinda wish we had a template lambda.
Would be cool.
Oct 08 2020
prev sibling parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Oct 08, 2020 at 12:54:49PM +0000, Adam D. Ruppe via Digitalmars-d wrote:
 On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 Wrapping __traits in templates is a necessity if you want to use
 them for anything interesting (such as passing them to higher order
 functions):
I kinda wish we had a template lambda.
Me too!!! I've been avoiding to use Filter, et al, because I'm forced to declare a bunch of helper templates just for trivial predicates. If lambda syntax could be extended to templates, that would make it much nicer to use. T -- Fact is stranger than fiction.
Oct 08 2020
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/8/20 10:08 AM, H. S. Teoh wrote:
 On Thu, Oct 08, 2020 at 12:54:49PM +0000, Adam D. Ruppe via Digitalmars-d
wrote:
 On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 Wrapping __traits in templates is a necessity if you want to use
 them for anything interesting (such as passing them to higher order
 functions):
I kinda wish we had a template lambda.
Me too!!! I've been avoiding to use Filter, et al, because I'm forced to declare a bunch of helper templates just for trivial predicates. If lambda syntax could be extended to templates, that would make it much nicer to use.
We recently had the AliasSeq pattern recognized by the compiler and it no longer stores a template for it. can we do the same thing for __traits? In other words, it can recognize the pattern: template foo(...) { alias foo = __traits(...); } And avoid setting up templates for this. I think the AliasSeq pattern still runs through the template machinery, which is not a good thing. But perhaps this can be addressed orthogonally. -Steve
Oct 08 2020
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/8/2020 7:19 AM, Steven Schveighoffer wrote:
 I think the AliasSeq pattern still runs through the template machinery, which
is 
 not a good thing. But perhaps this can be addressed orthogonally.
Nah, it short-circuits that machinery. Recognizing the AliasSeq pattern and doing it directly was a huge win. We can do the same as necessary for __traits if it comes to that.
Oct 09 2020
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 10 October 2020 at 00:16:32 UTC, Walter Bright wrote:
 On 10/8/2020 7:19 AM, Steven Schveighoffer wrote:
 I think the AliasSeq pattern still runs through the template 
 machinery, which is not a good thing. But perhaps this can be 
 addressed orthogonally.
Nah, it short-circuits that machinery. Recognizing the AliasSeq pattern and doing it directly was a huge win. We can do the same as necessary for __traits if it comes to that.
It was not a huge win. In certain circumstances it won. On others it lost. Bypassing the cache is not good.
Oct 09 2020
prev sibling parent Q. Schroll <qs.il.paperinik gmail.com> writes:
On Thursday, 8 October 2020 at 14:08:40 UTC, H. S. Teoh wrote:
 On Thu, Oct 08, 2020 at 12:54:49PM +0000, Adam D. Ruppe via 
 Digitalmars-d wrote:
 On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote:
 Wrapping __traits in templates is a necessity if you want to 
 use them for anything interesting (such as passing them to 
 higher order functions):
I kinda wish we had a template lambda.
Me too!!! I've been avoiding to use Filter, et al, because I'm forced to declare a bunch of helper templates just for trivial predicates. If lambda syntax could be extended to templates, that would make it much nicer to use.
Analogously to auto result = range.filter!(function(int i) => i % 1 == 0); we should make alias Result = Filter!(template(T) => is(T == Unqual!T), seq); a thing.
Oct 12 2020
prev sibling next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 7 October 2020 at 19:15:30 UTC, Paul Backus wrote:
 On Wednesday, 7 October 2020 at 19:07:34 UTC, H. S. Teoh wrote:
 I'm on the fence about this one. Having to type 
 __traits(isModule,xxx) is painful.  Is it really necessary to 
 make it so hard just to check whether a symbol is a module??  
 It's not as if it's something we want to discourage.  Typing 
 is(xxx == module) is much easier, and more pleasant on the 
 eyes.
IMO this is one of those situations where your pain is trying to tell you something. If using __traits syntax is so painful that we're willing to add hacky special cases to other language features in order to avoid it, maybe the message we should take from that is "we need to improve __traits syntax." Here's my strawman proposal: turn all __traits into properties. A few before-and-after examples: __traits(isModule, foo) => foo.__isModule __traits(getMember, T, "x") => T.__member("x") __traits(compiles, some(kind, of + expression)) => (some(kind, of + expression)).__compiles
I don't think that looks much better. __traits are actually fine in my eyes. It's easy on semantic and parser. Determining whether a trait applies and therefore should be imported into the properties of a given node is more nasty. (It means you have to change semanticX and semanticY as well as resolvePropertiesX in DMD)
Oct 07 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 7 October 2020 at 20:08:02 UTC, Stefan Koch wrote:
 On Wednesday, 7 October 2020 at 19:15:30 UTC, Paul Backus wrote:
 Here's my strawman proposal: turn all __traits into 
 properties. A few before-and-after examples:

 __traits(isModule, foo)
   => foo.__isModule

 __traits(getMember, T, "x")
   => T.__member("x")

 __traits(compiles, some(kind, of + expression))
   => (some(kind, of + expression)).__compiles
I don't think that looks much better. __traits are actually fine in my eyes. It's easy on semantic and parser. Determining whether a trait applies and therefore should be imported into the properties of a given node is more nasty. (It means you have to change semanticX and semanticY as well as resolvePropertiesX in DMD)
Couldn't you unconditionally lower <Node>.__isModule to __traits(isModule, <Node>) regardless of what type of node it is, and rely on the existing error messages for incorrect trait arguments? You don't have to worry about shadowing "real" properties, because anything that starts with "__" is a reserved identifier.
Oct 07 2020
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 7 October 2020 at 20:19:57 UTC, Paul Backus wrote:
 Couldn't you unconditionally lower <Node>.__isModule to 
 __traits(isModule, <Node>) regardless of what type of node it 
 is, and rely on the existing error messages for incorrect trait 
 arguments? You don't have to worry about shadowing "real" 
 properties, because anything that starts with "__" is a 
 reserved identifier.
I could do that.
Oct 07 2020
prev sibling next sibling parent reply Meta <jared771 gmail.com> writes:
On Wednesday, 7 October 2020 at 20:19:57 UTC, Paul Backus wrote:
 Couldn't you unconditionally lower <Node>.__isModule to 
 __traits(isModule, <Node>) regardless of what type of node it 
 is, and rely on the existing error messages for incorrect trait 
 arguments? You don't have to worry about shadowing "real" 
 properties, because anything that starts with "__" is a 
 reserved identifier.
struct Wrapper(alias sym) { import std.format: format; template opDispatch(string name, Args...) //Not DRY, probably a better way to do this if (__traits(compiles, mixin(`__traits(%s, sym, Args)`.format(name)))) { enum opDispatch = mixin(`__traits(%s, sym, Args)`.format(name)); } } void main() { import std.stdio; writeln(Wrapper!(std).isPackage); }
Oct 07 2020
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 7 October 2020 at 20:37:47 UTC, Meta wrote:
 On Wednesday, 7 October 2020 at 20:19:57 UTC, Paul Backus wrote:
 Couldn't you unconditionally lower <Node>.__isModule to 
 __traits(isModule, <Node>) regardless of what type of node it 
 is, and rely on the existing error messages for incorrect 
 trait arguments? You don't have to worry about shadowing 
 "real" properties, because anything that starts with "__" is a 
 reserved identifier.
     if (__traits(compiles, mixin(`__traits(%s, sym, 
 Args)`.format(name))))
     {
         enum opDispatch = mixin(`__traits(%s, sym, 
 Args)`.format(name));
     }
 }
As a little side notion. Avoid the use of std.format.format at CTFE. It's very expensive. And please cache the string :)
Oct 07 2020
parent Meta <jared771 gmail.com> writes:
On Wednesday, 7 October 2020 at 20:41:19 UTC, Stefan Koch wrote:
 On Wednesday, 7 October 2020 at 20:37:47 UTC, Meta wrote:
 On Wednesday, 7 October 2020 at 20:19:57 UTC, Paul Backus 
 wrote:
 Couldn't you unconditionally lower <Node>.__isModule to 
 __traits(isModule, <Node>) regardless of what type of node it 
 is, and rely on the existing error messages for incorrect 
 trait arguments? You don't have to worry about shadowing 
 "real" properties, because anything that starts with "__" is 
 a reserved identifier.
     if (__traits(compiles, mixin(`__traits(%s, sym, 
 Args)`.format(name))))
     {
         enum opDispatch = mixin(`__traits(%s, sym, 
 Args)`.format(name));
     }
 }
As a little side notion. Avoid the use of std.format.format at CTFE. It's very expensive. And please cache the string :)
I'm hoping and praying for a string interpolation dip that gets accepted.
Oct 07 2020
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 7 October 2020 at 20:37:47 UTC, Meta wrote:
 On Wednesday, 7 October 2020 at 20:19:57 UTC, Paul Backus wrote:
 Couldn't you unconditionally lower <Node>.__isModule to 
 __traits(isModule, <Node>) regardless of what type of node it 
 is, and rely on the existing error messages for incorrect 
 trait arguments? You don't have to worry about shadowing 
 "real" properties, because anything that starts with "__" is a 
 reserved identifier.
struct Wrapper(alias sym) { import std.format: format; template opDispatch(string name, Args...) //Not DRY, probably a better way to do this if (__traits(compiles, mixin(`__traits(%s, sym, Args)`.format(name)))) { enum opDispatch = mixin(`__traits(%s, sym, Args)`.format(name)); } } void main() { import std.stdio; writeln(Wrapper!(std).isPackage); }
Clever. Now try this: writeln(Wrapper!("hello" + 1).compiles); Or this: struct S { int x; } int n = 123; writeln(Wrapper!(S(n)).getMember!"x"); Or this: struct S { in x, y; } writeln(Wrapper!S.allMembers); The problem with using templates to wrap __traits is that __traits can do things that templates aren't allowed to do, like take expressions as arguments, or evaluate to expressions rather than symbols or constants.
Oct 07 2020
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 7 October 2020 at 20:58:21 UTC, Paul Backus wrote:

 Clever. Now try this:

     writeln(Wrapper!("hello" + 1).compiles);

 Or this:

     struct S { int x; }
     int n = 123;
     writeln(Wrapper!(S(n)).getMember!"x");

 Or this:

     struct S { in x, y; }
     writeln(Wrapper!S.allMembers);

 The problem with using templates to wrap __traits is that 
 __traits can do things that templates aren't allowed to do, 
 like take expressions as arguments, or evaluate to expressions 
 rather than symbols or constants.
Hmm seeing this written out I have to say ... It does look neat .... If you submit a DIP I can implement it for you. It only looks neat without the underscores though :)
Oct 07 2020
parent Imperatorn <johan_forsberg_86 hotmail.com> writes:
On Wednesday, 7 October 2020 at 21:01:28 UTC, Stefan Koch wrote:
 On Wednesday, 7 October 2020 at 20:58:21 UTC, Paul Backus wrote:

 Clever. Now try this:

     writeln(Wrapper!("hello" + 1).compiles);

 Or this:

     struct S { int x; }
     int n = 123;
     writeln(Wrapper!(S(n)).getMember!"x");

 Or this:

     struct S { in x, y; }
     writeln(Wrapper!S.allMembers);

 The problem with using templates to wrap __traits is that 
 __traits can do things that templates aren't allowed to do, 
 like take expressions as arguments, or evaluate to expressions 
 rather than symbols or constants.
Hmm seeing this written out I have to say ... It does look neat .... If you submit a DIP I can implement it for you. It only looks neat without the underscores though :)
Agree about the underscores. Would be splendid is it was possible. Dunno how you would fix naming issues though, like is someone has a function already named some trait
Oct 07 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/7/20 4:19 PM, Paul Backus wrote:
 On Wednesday, 7 October 2020 at 20:08:02 UTC, Stefan Koch wrote:
 On Wednesday, 7 October 2020 at 19:15:30 UTC, Paul Backus wrote:
 Here's my strawman proposal: turn all __traits into properties. A few 
 before-and-after examples:

 __traits(isModule, foo)
   => foo.__isModule

 __traits(getMember, T, "x")
   => T.__member("x")

 __traits(compiles, some(kind, of + expression))
   => (some(kind, of + expression)).__compiles
I don't think that looks much better. __traits are actually fine in my eyes. It's easy on semantic and parser. Determining whether a trait applies and therefore should be imported into the properties of a given node is more nasty. (It means you have to change semanticX and semanticY as well as resolvePropertiesX in DMD)
Couldn't you unconditionally lower <Node>.__isModule to __traits(isModule, <Node>) regardless of what type of node it is, and rely on the existing error messages for incorrect trait arguments? You don't have to worry about shadowing "real" properties, because anything that starts with "__" is a reserved identifier.
If we got type functions, and have UFCS, isn't this as easy as e.g.: bool isModule(alias x) { return __traits(isModule, x); } static assert(std.stdio.isModule); bool hasMember(alias T, string membername) { return __traits(hasMember, T, membername); } static assert(T.hasMember("foo")); alias getMember(alias T, string membername) { return __traits(getMember, T, membername)); static assert(is(typeof(T.getMember("foo")) == int); alias foomember = someTInstance.getMember("foo"); Then we don't have to care about keywords, or adding properties that can't be overridden, etc. It's just a normal symbol we can define wherever (like std.traits). -Steve
Oct 08 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 8 October 2020 at 16:49:50 UTC, Steven Schveighoffer 
wrote:
 If we got type functions, and have UFCS, isn't this as easy as 
 e.g.:

 bool isModule(alias x) { return __traits(isModule, x); }

 static assert(std.stdio.isModule);
Ok, now do __traits(getMember, variable, "foo") and __traits(compiles, expression). :) Using type functions to wrap __traits has the same issues as using templates to wrap __traits. In the common case, it works, but in the general case, it fails, because __traits have special privileges that no other construct in the D language (including templates, functions, and type functions) can fully imitate.
Oct 08 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/8/20 1:43 PM, Paul Backus wrote:
 On Thursday, 8 October 2020 at 16:49:50 UTC, Steven Schveighoffer wrote:
 If we got type functions, and have UFCS, isn't this as easy as e.g.:

 bool isModule(alias x) { return __traits(isModule, x); }

 static assert(std.stdio.isModule);
Ok, now do __traits(getMember, variable, "foo") and __traits(compiles, expression). :)
I did getMember already. And it can't do __traits(compiles). So, just use __traits(compiles) for that one.
 Using type functions to wrap __traits has the same issues as using 
 templates to wrap __traits. In the common case, it works, but in the 
 general case, it fails, because __traits have special privileges that no 
 other construct in the D language (including templates, functions, and 
 type functions) can fully imitate.
I'm not sure that's 100% true for type functions -- they aren't in there yet. -Steve
Oct 08 2020
parent reply Paul Backus <snarwin gmail.com> writes:
On Thursday, 8 October 2020 at 17:55:21 UTC, Steven Schveighoffer 
wrote:
 I did getMember already.
You did getMember for a type, not a variable. getMember for a type evaluates to a symbol, but getMember for a variable evaluates to an expression.
 I'm not sure that's 100% true for type functions -- they aren't 
 in there yet.
In order to do everything that __traits can do, type functions would have to be polymorphic, which is contrary to their design goals.
Oct 08 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/8/20 3:15 PM, Paul Backus wrote:
 On Thursday, 8 October 2020 at 17:55:21 UTC, Steven Schveighoffer wrote:
 I did getMember already.
You did getMember for a type, not a variable. getMember for a type evaluates to a symbol, but getMember for a variable evaluates to an expression.
I wrote: alias foomember = someTInstance.getMember("foo"); with the assumption that someTInstance was an instance of T. I didn't specify it was a runtime variable, but I did intend for that to be the case. It's not possible to use a template to get an alias to a runtime member, so it would have to be something different that is allowed in typefunctions. But I assume the "alias" type that is inside a typefunction can be able to handle this, as you can *pass in* an alias to a runtime member, you just can't *return* an alias to a runtime member (well, you can, but it doesn't retain it's runtime component).
 
 I'm not sure that's 100% true for type functions -- they aren't in 
 there yet.
In order to do everything that __traits can do, type functions would have to be polymorphic, which is contrary to their design goals.
__traits are a way to introspect what already exist. At least the *traits* part of __traits (not __traits(compiles)). This doesn't mean it's polymorphic necessarily. I can see for instance, __traits(getMember, x, "foo") returning an alias to the foo member inside a type function. It can't fetch a *usable value* from that alias. So for example, if x.foo is an integer, you can't get at the integer. But you can get at an alias to that member to do further introspection (i.e. check if it has a certain UDA, etc.) In the land of reification for example, you could have a "getMember" method that uses pre-built information to fetch an alias to the member you are concerned about, which could carry along the type information about that member, and it's offset, and whatever else you need to then `dereify` the result. __traits are like a window into the compiler's already-existing type information system. I can't see why we couldn't allow access to those things for type functions. -Steve
Oct 08 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/8/20 8:23 PM, Steven Schveighoffer wrote:
 as you can *pass in* an alias to a runtime member, you just can't 
 *return* an alias to a runtime member
Ugh, actually, you can only pass in an alias to a variable. Why you can't pass in an alias to a member doesn't make a lot of sense to me. But still, the compiler should be able to handle this. -Steve
Oct 08 2020
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On Wednesday, 7 October 2020 at 19:15:30 UTC, Paul Backus wrote:

 Here's my strawman proposal: turn all __traits into properties. 
 A few before-and-after examples:

 __traits(isModule, foo)
   => foo.__isModule

 __traits(getMember, T, "x")
   => T.__member("x")

 __traits(compiles, some(kind, of + expression))
   => (some(kind, of + expression)).__compiles
Oh, god, no. We already have way too many special magic members. We don't need any more. `__traits` is great because it has it's own namespace. It occupies just one keyword and you can add all the identifiers in the world without the risk of breaking existing code. Yes, I know that identifiers starting with `__` is reserved, but there's nothing that stops anyone from using an identifier which starts with `__`. In my opinion is the double underscores that makes it look ugly. Your suggestion is not an improvement. Same thing with UFCS, it's no point if you need to use parentheses anyway: `(1 + 2).toString`. If we need a new syntax for this (which I don't think we need), it would be better with some built-in/compiler recognized functions declared somewhere in the `core` package. Then it would be possible to use standard language constructs to deal with multiple symbols with the same name. -- /Jacob Carlborg
Oct 08 2020
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Thursday, 8 October 2020 at 09:38:40 UTC, Jacob Carlborg wrote:
 On Wednesday, 7 October 2020 at 19:15:30 UTC, Paul Backus wrote:

 Here's my strawman proposal: turn all __traits into 
 properties. A few before-and-after examples:

 __traits(isModule, foo)
   => foo.__isModule

 __traits(getMember, T, "x")
   => T.__member("x")

 __traits(compiles, some(kind, of + expression))
   => (some(kind, of + expression)).__compiles
Oh, god, no. We already have way too many special magic members. We don't need any more. `__traits` is great because it has it's own namespace. It occupies just one keyword and you can add all the identifiers in the world without the risk of breaking existing code. Yes, I know that identifiers starting with `__` is reserved, but there's nothing that stops anyone from using an identifier which starts with `__`.
Except, you know, the language spec. I understand the desire to avoid breaking changes if possible, but why even bother to have reserved identifiers in the first place if you're going to treat them as sarcosanct? Even the ISO C standard is willing to make changes like this, and that's about the most conservative language there is. In any case, it would be quite easy to put this behind a `-preview` flag and have a deprecation period during which the compiler warns about all uses of the identifiers that the language is planning to claim.
 In my opinion is the double underscores that makes it look 
 ugly. Your suggestion is not an improvement. Same thing with 
 UFCS, it's no point if you need to use parentheses anyway: `(1 
 + 2).toString`.
Are you sure? You really can't see any difference in readability between this __traits(getOverloads, __traits(parent, sym), __traits(identifier, sym)) and this? sym.__parent.__overloads(sym.__identifier) Maybe it's not perfect, but surely it's still an improvement.
 If we need a new syntax for this (which I don't think we need), 
 it would be better with some built-in/compiler recognized 
 functions declared somewhere in the `core` package. Then it 
 would be possible to use standard language constructs to deal 
 with multiple symbols with the same name.
I would also be fine with this. Of course, they would have to be "magic" functions specially recognized by the compiler, not real ones, but at least that would let us get rid of the dreaded double-underscore.
Oct 08 2020
prev sibling parent Walter Bright <newshound2 digitalmars.com> writes:
On 10/8/2020 2:38 AM, Jacob Carlborg wrote:
 Oh, god, no. We already have way too many special magic members. We don't need 
 any more. `__traits` is great because it has it's own namespace. It occupies 
 just one keyword and you can add all the identifiers in the world without the 
 risk of breaking existing code.
Exactly right. It's also there to avoid the need for inventing special syntax for all the weird things it does.
Oct 09 2020
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven 
Schveighoffer wrote:
 Why both?
Why *either*? The real WTF is that modules - which are not actually a child member of another module - is returned in listings of __traits(allMembers). That's the root bug and should be fixed. These were just added to let user code hack around this bug instead of just fixing the bug in the compiler. If you want to get imports, we should provide an alternate trait for that.
 is(std == package) == __traits(isPackage, std);
And this is broken af right now anyway. As of the newest dmd, trying to actually modules from imports slams into a brick wall. The status quo is even worse than it was a couple releases ago... and it was already broken. Let's just fix it all.
Oct 07 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/7/20 3:44 PM, Adam D. Ruppe wrote:
 On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven Schveighoffer wrote:
 Why both?
Why *either*?
If you want to determine if a given symbol is a module. Is there another way?
 The real WTF is that modules - which are not actually a child member of 
 another module - is returned in listings of __traits(allMembers).
Yeah, that's a problem we should fix. There was an almost-released fix which made __traits(allMembers, mod) return the full name of the modules, which I don't think was right either. But aside from that, I don't see a problem with allowing someone to introspect whether something is a module or package.
 If you want to get imports, we should provide an alternate trait for that.
Agreed. But still I would think some mechanism to see if a symbol is a module is fine.
 is(std == package) == __traits(isPackage, std);
And this is broken af right now anyway. As of the newest dmd, trying to actually modules from imports slams into a brick wall.
Not sure what this was supposed to mean. -Steve
Oct 07 2020
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 7 October 2020 at 20:01:53 UTC, Steven 
Schveighoffer wrote:
 If you want to determine if a given symbol is a module. Is 
 there another way?
A module symbol should *only* be given through means specific to modules. Which means the code knows it is dealing with a module without any introspection. So like __traits(getImports) would ONLY return modules. __traits(allMembers) would NEVER return modules. That said I guess I can see the point for completeness' sake to make __traits(isModule, mixin(__MODULE__)) return true.... but I don't see why you'd ever use that since it would be obvious in the code itself. Perhaps useful if you get an aliased module though.
 Yeah, that's a problem we should fix. There was an 
 almost-released fix which made __traits(allMembers, mod) return 
 the full name of the modules, which I don't think was right 
 either.
That would be slightly less bad than it is now (a module should ALWAYS be referred to by its full name) but it is still the wrong place to put it. D modules do not have parents. They may be associated with a package, but the package is not its parent in the strict sense since you can compile them independently.
 Not sure what this was supposed to mean.
Check this out: ``` module wtf.useless; static if(is(wtf.useless MOD == module)) static if(is(__traits(parent, wtf.useless) PKG == package)) static assert(0); // NOT TRIGGERED! ``` Yet: // this prints "package wtf" pragma(msg, __traits(parent, wtf.useless).stringof); And: module wtf.useless; pragma(msg, wtf.stringof); // works Yet: static if(is(wtf.useless MOD == module)) static if(is(wtf PKG == module)) static assert(0); Error: module wtf.useless from file wtf.d must be imported with 'import wtf.useless;' (notice no line number either) Unless you make wtf/package.d. Then it thinks it is a module instead of a package. Oh and check this out: pragma(msg, wtf.useless.stringof); // module useless Good luck `import useless;`. Obviously won't work. It is right now *impossible* to get a module name back in the general case. tbh, I don't even know what dmd is doing here. There seems to be no logic to it whatsoever. If the language is going to pretend modules are children of packages (which they aren't and it shouldn't!!!!!), it should at least do so consistently. As it sits now, stuff that was possible a couple versions ago is now just hopelessly broken now.
Oct 07 2020
parent Walter Bright <newshound2 digitalmars.com> writes:
On 10/7/2020 1:29 PM, Adam D. Ruppe wrote:
 If the language is going to pretend modules are children of packages (which
they 
 aren't and it shouldn't!!!!!), it should at least do so consistently. As it
sits 
 now, stuff that was possible a couple versions ago is now just hopelessly
broken 
 now.
Please file regressions on bugzilla.
Oct 08 2020
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
I'm not sure which is the one to go, but we should not have both.
Oct 07 2020
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 7 October 2020 at 23:17:18 UTC, Walter Bright wrote:
 I'm not sure which is the one to go, but we should not have 
 both.
I agree. Removing the either now would be a breaking change though ... Which version is used more often?
Oct 07 2020
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 07, 2020 at 11:37:06PM +0000, Stefan Koch via Digitalmars-d wrote:
 On Wednesday, 7 October 2020 at 23:17:18 UTC, Walter Bright wrote:
 I'm not sure which is the one to go, but we should not have both.
I agree. Removing the either now would be a breaking change though ...
We could go through the deprecation process...?
 Which version is used more often?
Maybe grep through the most popular code.dlang.org repos? T -- Государство делает вид, что платит нам зарплату, а мы делаем вид, что работаем.
Oct 07 2020
prev sibling parent reply RazvanN <razvan.nitu1305 gmail.com> writes:
On Wednesday, 7 October 2020 at 23:17:18 UTC, Walter Bright wrote:
 I'm not sure which is the one to go, but we should not have 
 both.
I think that the line of reasoning is that __traits should be leveraged by std.traits to offer user-friendly alternatives. Therefore, in this case I suggest that we deprecate `is(S == module)` (since a module is not a type anyway) and implement an isModule template in std.traits.
Oct 08 2020
parent Nick Treleaven <nick geany.org> writes:
On Thursday, 8 October 2020 at 12:43:48 UTC, RazvanN wrote:
 Therefore, in this case I suggest that we deprecate `is(S == 
 module)`
We could just ghost it. Deprecation would be fine except people tend to remove deprecated things after a while, breaking legacy code.
 (since a module is not a type anyway)
Absolutely, it is very jarring if a conceptual feature is broken like this.
 is ( Type == TypeSpecialization )
A module/package clearly isn't a type, so either the docs are wrong or is(mod == module) is wrong. All the other keywords are types, as the design intended:
 If TypeSpecialization is one of struct union class interface 
 enum function delegate const immutable shared module package 
 then the condition is satisfied if Type is one of those.
 and implement an isModule template in std.traits.
Don't template wrappers create bloat when instantiated? For a module name, probably not important. But the principle of wrapping every __traits might not scale well.
Oct 11 2020
prev sibling parent reply Kagamin <spam here.lot> writes:
On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven 
Schveighoffer wrote:
 Why both? The first thing that struck me is that, std.stdio is 
 NOT a type. `is` specifically says it works with types. So that 
 seems out of place (indeed the documentation does not mention 
 anything special about this).
Guess, it's a bit of pl theory, I saw mentioned somewhere that modules count as types. Not sure about packages though.
Oct 08 2020
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Thursday, 8 October 2020 at 08:12:51 UTC, Kagamin wrote:
 On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven 
 Schveighoffer wrote:
 Why both? The first thing that struck me is that, std.stdio is 
 NOT a type. `is` specifically says it works with types. So 
 that seems out of place (indeed the documentation does not 
 mention anything special about this).
Guess, it's a bit of pl theory, I saw mentioned somewhere that modules count as types. Not sure about packages though.
Well you can see a module as being the same as a singleton struct. In D though Modules are not types. IIRC.
Oct 08 2020
prev sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 10/8/20 4:12 AM, Kagamin wrote:
 On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven Schveighoffer wrote:
 Why both? The first thing that struck me is that, std.stdio is NOT a 
 type. `is` specifically says it works with types. So that seems out of 
 place (indeed the documentation does not mention anything special 
 about this).
Guess, it's a bit of pl theory, I saw mentioned somewhere that modules count as types. Not sure about packages though.
It's literally special cased in the code: https://github.com/dlang/dmd/blob/013eccaf113e6f23784c615d3ec66434c3629197/src/dmd/expressionsem.d#L5418 -Steve
Oct 08 2020
parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 10/8/2020 7:04 AM, Steven Schveighoffer wrote:
 It's literally special cased in the code:
 
 https://github.com/dlang/dmd/blob/013eccaf113e6f23784c615d3ec66434c3629197/src/dmd/e
pressionsem.d#L5418 
Good point. Remove the `is` version.
Oct 09 2020
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 10 October 2020 at 00:13:15 UTC, Walter Bright wrote:
 On 10/8/2020 7:04 AM, Steven Schveighoffer wrote:
 It's literally special cased in the code:
 
 https://github.com/dlang/dmd/blob/013eccaf113e6f23784c615d3ec66434c3629197/src/dmd/expressionsem.d#L5418
Good point. Remove the `is` version.
I agree.
Oct 09 2020