digitalmars.D - is(x = module) vs. __traits(isModule, x)
- Steven Schveighoffer (21/21) Oct 07 2020 Someone on the discord chat brought this up:
- Paul Backus (15/20) Oct 07 2020 I tried reading through the PR discussion to see how things ended
- H. S. Teoh (16/28) Oct 07 2020 [...]
- Paul Backus (14/19) Oct 07 2020 IMO this is one of those situations where your pain is trying to
- H. S. Teoh (15/27) Oct 07 2020 [...]
- Jacob Carlborg (7/11) Oct 08 2020 Yes, because just wrapping existing `__traits` in templates don't
- Max Samukha (6/9) Oct 08 2020 Wrapping __traits in templates is a necessity if you want to use
- Stefan Koch (2/14) Oct 08 2020 or you use type functions which can use them at ctfe;)
- Max Samukha (3/7) Oct 08 2020 You'd still have to wrap __traits in a type function to pass it
- Adam D. Ruppe (2/5) Oct 08 2020 I kinda wish we had a template lambda.
- Stefan Koch (3/8) Oct 08 2020 you mean alias compiles (__top A) = ((__top x) =>
- Nick Treleaven (7/12) Oct 11 2020 Not sure what __top is, but the compiles trait is special in that
- Stefan Koch (5/18) Oct 11 2020 __top is the top type.
- Nick Treleaven (4/8) Oct 11 2020 OK thanks. Can it also implicitly hold the context that the
- Stefan Koch (3/11) Oct 11 2020 Well ... I have to think about that.
- Max Samukha (2/3) Oct 08 2020 Would be cool.
- H. S. Teoh (8/14) Oct 08 2020 Me too!!! I've been avoiding to use Filter, et al, because I'm forced
- Steven Schveighoffer (13/25) Oct 08 2020 We recently had the AliasSeq pattern recognized by the compiler and it
- Walter Bright (4/6) Oct 09 2020 Nah, it short-circuits that machinery. Recognizing the AliasSeq pattern ...
- Stefan Koch (5/13) Oct 09 2020 It was not a huge win.
- Q. Schroll (6/18) Oct 12 2020 Analogously to
- Stefan Koch (8/28) Oct 07 2020 I don't think that looks much better.
-
Paul Backus
(7/27)
Oct 07 2020
Couldn't you unconditionally lower
.__isModule to - Stefan Koch (2/8) Oct 07 2020 I could do that.
- Meta (18/24) Oct 07 2020 struct Wrapper(alias sym)
- Stefan Koch (5/19) Oct 07 2020 As a little side notion.
- Meta (3/23) Oct 07 2020 I'm hoping and praying for a string interpolation dip that gets
- Paul Backus (14/38) Oct 07 2020 Clever. Now try this:
- Stefan Koch (5/18) Oct 07 2020 Hmm seeing this written out I have to say ...
- Imperatorn (4/28) Oct 07 2020 Agree about the underscores. Would be splendid is it was
- Steven Schveighoffer (15/43) Oct 08 2020 If we got type functions, and have UFCS, isn't this as easy as e.g.:
- Paul Backus (9/13) Oct 08 2020 Ok, now do __traits(getMember, variable, "foo") and
- Steven Schveighoffer (7/22) Oct 08 2020 I did getMember already.
- Paul Backus (8/11) Oct 08 2020 You did getMember for a type, not a variable. getMember for a
- Steven Schveighoffer (28/41) Oct 08 2020 I wrote:
- Steven Schveighoffer (5/7) Oct 08 2020 Ugh, actually, you can only pass in an alias to a variable. Why you
- Jacob Carlborg (19/27) Oct 08 2020 Oh, god, no. We already have way too many special magic members.
- Paul Backus (22/51) Oct 08 2020 Except, you know, the language spec. I understand the desire to
- Walter Bright (3/7) Oct 09 2020 Exactly right. It's also there to avoid the need for inventing special s...
- Adam D. Ruppe (15/17) Oct 07 2020 Why *either*?
- Steven Schveighoffer (12/23) Oct 07 2020 If you want to determine if a given symbol is a module. Is there another...
- Adam D. Ruppe (50/57) Oct 07 2020 A module symbol should *only* be given through means specific to
- Walter Bright (2/6) Oct 08 2020 Please file regressions on bugzilla.
- Walter Bright (1/1) Oct 07 2020 I'm not sure which is the one to go, but we should not have both.
- Stefan Koch (4/6) Oct 07 2020 I agree.
- H. S. Teoh (6/13) Oct 07 2020 Maybe grep through the most popular code.dlang.org repos?
- RazvanN (6/8) Oct 08 2020 I think that the line of reasoning is that __traits should be
- Nick Treleaven (12/20) Oct 11 2020 We could just ghost it. Deprecation would be fine except people
- Kagamin (4/8) Oct 08 2020 Guess, it's a bit of pl theory, I saw mentioned somewhere that
- Stefan Koch (3/11) Oct 08 2020 Well you can see a module as being the same as a singleton struct.
- Steven Schveighoffer (4/12) Oct 08 2020 It's literally special cased in the code:
- Walter Bright (2/5) Oct 09 2020 Good point. Remove the `is` version.
- Stefan Koch (2/7) Oct 09 2020 I agree.
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
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
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
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
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:[...] 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.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."
Oct 07 2020
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
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
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:or you use type functions which can use them at ctfe;)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
On Thursday, 8 October 2020 at 11:47:48 UTC, Stefan Koch wrote:You'd still have to wrap __traits in a type function to pass it to another function?filter!(__traits(isPOD), A, B C); // no way filter!(isPod, A, B, C); // can beor you use type functions which can use them at ctfe;)
Oct 08 2020
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
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:you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?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
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: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)I kinda wish we had a template lambda.you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?
Oct 11 2020
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:__top is the top type. that which can hold anything. symbols, complete expressions, types, values, tuples. anything.On Thursday, 8 October 2020 at 12:54:49 UTC, Adam D. Ruppe wrote: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)I kinda wish we had a template lambda.you mean alias compiles (__top A) = ((__top x) => __traits(compiles, x)(A); ?
Oct 11 2020
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
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:Well ... I have to think about that. Sounds hairy if you ask me.__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
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
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: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.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
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: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. -SteveOn Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote: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.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
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
On Saturday, 10 October 2020 at 00:16:32 UTC, Walter Bright wrote:On 10/8/2020 7:19 AM, Steven Schveighoffer wrote:It was not a huge win. In certain circumstances it won. On others it lost. Bypassing the cache is not good.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
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: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.On Thursday, 8 October 2020 at 10:11:59 UTC, Max Samukha wrote: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.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 12 2020
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 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)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
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: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.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)).__compilesI 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
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
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
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
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:I'm hoping and praying for a string interpolation dip that gets accepted.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
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: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.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
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
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: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 traitClever. 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
On 10/7/20 4:19 PM, Paul Backus wrote:On Wednesday, 7 October 2020 at 20:08:02 UTC, Stefan Koch 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); 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). -SteveOn Wednesday, 7 October 2020 at 19:15:30 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.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)).__compilesI 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 08 2020
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
On 10/8/20 1:43 PM, Paul Backus wrote:On Thursday, 8 October 2020 at 16:49:50 UTC, Steven Schveighoffer wrote:I did getMember already. And it can't do __traits(compiles). So, just use __traits(compiles) for that one.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.I'm not sure that's 100% true for type functions -- they aren't in there yet. -Steve
Oct 08 2020
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
On 10/8/20 3:15 PM, Paul Backus wrote:On Thursday, 8 October 2020 at 17:55:21 UTC, Steven Schveighoffer wrote: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 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.__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. -SteveI'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
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 memberUgh, 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
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)).__compilesOh, 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
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: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.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)).__compilesOh, 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`.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
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
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
On 10/7/20 3:44 PM, Adam D. Ruppe wrote:On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven Schveighoffer wrote:If you want to determine if a given symbol is a module. Is there another way?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).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.Not sure what this was supposed to mean. -Steveis(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.
Oct 07 2020
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
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
I'm not sure which is the one to go, but we should not have both.
Oct 07 2020
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
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:We could go through the deprecation process...?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?Maybe grep through the most popular code.dlang.org repos? T -- Государство делает вид, что платит нам зарплату, а мы делаем вид, что работаем.
Oct 07 2020
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
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.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:is ( Type == TypeSpecialization )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
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
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:Well you can see a module as being the same as a singleton struct. In D though Modules are not types. IIRC.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
On 10/8/20 4:12 AM, Kagamin wrote:On Wednesday, 7 October 2020 at 18:46:07 UTC, Steven Schveighoffer wrote:It's literally special cased in the code: https://github.com/dlang/dmd/blob/013eccaf113e6f23784c615d3ec66434c3629197/src/dmd/expressionsem.d#L5418 -SteveWhy 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
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#L5418Good point. Remove the `is` version.
Oct 09 2020
On Saturday, 10 October 2020 at 00:13:15 UTC, Walter Bright wrote:On 10/8/2020 7:04 AM, Steven Schveighoffer wrote:I agree.It's literally special cased in the code: https://github.com/dlang/dmd/blob/013eccaf113e6f23784c615d3ec66434c3629197/src/dmd/expressionsem.d#L5418Good point. Remove the `is` version.
Oct 09 2020