www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - First life-signs of type functions

reply Stefan Koch <uplink.coder googlemail.com> writes:
Hello Guys,

After a bit of fiddling the following code (and only that because 
I need do partial evaluation of the semantic pass on the function 
body manually ...)
Now works on the Talias branch

string F(alias y)
{
     return y.stringof;
}
static assert(F!(ulong) == "ulong");
static assert(F!(uint) == "uint");
May 06
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Now works on the Talias branch
https://github.com/dlang/dmd/compare/master...UplinkCoder:talias?expand=1 As you can see the modifications aren't even that heavy. I am confident this will take significantly less time than re-implementing CTFE. Because it's a new feature and bugs can be fixed as they are discovered.
May 06
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 6 May 2020 at 12:01:11 UTC, Stefan Koch wrote:
 On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Now works on the Talias branch
https://github.com/dlang/dmd/compare/master...UplinkCoder:talias?expand=1 As you can see the modifications aren't even that heavy. I am confident this will take significantly less time than re-implementing CTFE. Because it's a new feature and bugs can be fixed as they are discovered.
As of a few minutes ago the following code compiles without errors! string fqn(alias t) { string result = t.stringof; alias p; p = t; bool good = is(typeof(__traits(parent, p))); while(good) { p = __traits(parent, p); result = p.stringof ~ "." ~ result; // result = __traits(identifier, parent) ~ "." ~ result; good = is(typeof(__traits(parent, p))); } return cast(string) result; } struct S { struct X { int xx; static assert( fqn!xx == "module " ~ __MODULE__ ~ ".S.X.xx" ); } }
May 08
next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Saturday, 9 May 2020 at 06:39:20 UTC, Stefan Koch wrote:
     alias p;
     p = t;
     bool good = is(typeof(__traits(parent, p)));
     while(good)
     {
         p = __traits(parent, p);
         result = p.stringof ~ "." ~ result;
 //        result = __traits(identifier, parent) ~ "." ~ result;
         good = is(typeof(__traits(parent, p)));
     }
Very cool! When `alias[]` is implemented, would this work: bool anySatisfy(alias Tem, alias[] S) { bool found; alias E; while (S.length) { E = S[0]; if (Tem!E) return true; S = S[1..$]; } return false; } enum isPointer(T) = is(T : E*, E); pragma(msg, anySatisfy!(isPointer, int, char, void, int*, byte)); Any idea how fast this would be compared to `core.internal.traits : anySatisfy` (which doesn't use template recursion)?
May 10
next sibling parent reply Nick Treleaven <nick geany.org> writes:
On Sunday, 10 May 2020 at 08:39:42 UTC, Nick Treleaven wrote:
     alias E;
     while (S.length)
     {
         E = S[0];
         if (Tem!E)
             return true;
         S = S[1..$];
     }
     return false;
I had assumed that indexing an alias[] with a mutable variable wouldn't work. If it does, then I don't even need to mutate S: bool anySatisfy(alias Tem, alias[] S) { size_t i = 0; while (i != S.length) { if (Tem!(S[i])) return true; i++; } return false; }
May 10
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 10 May 2020 at 10:28:17 UTC, Nick Treleaven wrote:
 On Sunday, 10 May 2020 at 08:39:42 UTC, Nick Treleaven wrote:
     alias E;
     while (S.length)
     {
         E = S[0];
         if (Tem!E)
             return true;
         S = S[1..$];
     }
     return false;
I had assumed that indexing an alias[] with a mutable variable wouldn't work. If it does, then I don't even need to mutate S: bool anySatisfy(alias Tem, alias[] S) { size_t i = 0; while (i != S.length) { if (Tem!(S[i])) return true; i++; } return false; }
I've posted the simplest version in the other thread. this version can still lose a few lines :)
May 10
prev sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 10 May 2020 at 10:28:17 UTC, Nick Treleaven wrote:
 On Sunday, 10 May 2020 at 08:39:42 UTC, Nick Treleaven wrote:
     alias E;
     while (S.length)
     {
         E = S[0];
         if (Tem!E)
             return true;
         S = S[1..$];
     }
     return false;
I had assumed that indexing an alias[] with a mutable variable wouldn't work. If it does, then I don't even need to mutate S: bool anySatisfy(alias Tem, alias[] S) { size_t i = 0; while (i != S.length) { if (Tem!(S[i])) return true; i++; } return false; }
What do you think about this one? bool anySatisfy(bool function (alias) pred, alias[] types ...) { foreach(type; types) { if (pred(type)) return true; } return false; }
May 10
parent reply Nick Treleaven <nick geany.org> writes:
On Sunday, 10 May 2020 at 10:33:42 UTC, Stefan Koch wrote:
 What do you think about this one?

 bool anySatisfy(bool function (alias) pred, alias[] types ...)
 {
     foreach(type; types)
     {
         if (pred(type))
             return true;
     }
     return false;
 }
Nice, this makes me wonder if later we'll get type function lambdas :-D I was using `while` instead of foreach because I thought the `alias[]` would be treated like a parameter sequence `S...`, and so the loop would be inlined. I suppose that wouldn't be bad here because your version takes a type function instead of a template predicate, so there's no need to avoid instantiating a template after the first true result. I suppose you can define foreach on an alias[] to still work like a runtime foreach; that way the loop doesn't have to be inlined. (If the programmer wants inlining, use static foreach).
May 10
parent reply Q. Schroll <qs.il.paperinik gmail.com> writes:
On Sunday, 10 May 2020 at 10:45:54 UTC, Nick Treleaven wrote:
 On Sunday, 10 May 2020 at 10:33:42 UTC, Stefan Koch wrote:
 What do you think about this one?

 bool anySatisfy(bool function (alias) pred, alias[] types ...)
 {
     foreach(type; types)
     {
         if (pred(type))
             return true;
     }
     return false;
 }
Nice, this makes me wonder if later we'll get type function lambdas :-D
That would be massive. struct S(Ts...) { Ts values; bool opCmp()(S!Ts other) if (allSatisfy!((alias T) => is(typeof(T.init < T.init)), Ts)) { /* implementation */ } } I wonder if it there's value in not only having aliases as parameters but also specifying types and values in particular. An alias is an unrestricted compile-time name (ctName), so why not have the "subtypes" ctType and ctValue of ctName? That way, a "type function" would signal: That parameter is supposed to be (the alias of) a type, this other one (the alias of) a value. It wouldn't add any power, with this, you can do is(name) and is(typeof(name)) already to find out if `name` is an alias of a type or value respectively.
May 11
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Monday, 11 May 2020 at 21:14:47 UTC, Q. Schroll wrote:
 On Sunday, 10 May 2020 at 10:45:54 UTC, Nick Treleaven wrote:
 On Sunday, 10 May 2020 at 10:33:42 UTC, Stefan Koch wrote:
 What do you think about this one?

 bool anySatisfy(bool function (alias) pred, alias[] types ...)
 {
     foreach(type; types)
     {
         if (pred(type))
             return true;
     }
     return false;
 }
Nice, this makes me wonder if later we'll get type function lambdas :-D
That would be massive. struct S(Ts...) { Ts values; bool opCmp()(S!Ts other) if (allSatisfy!((alias T) => is(typeof(T.init < T.init)), Ts)) { /* implementation */ } } I wonder if it there's value in not only having aliases as parameters but also specifying types and values in particular. An alias is an unrestricted compile-time name (ctName), so why not have the "subtypes" ctType and ctValue of ctName? That way, a "type function" would signal: That parameter is supposed to be (the alias of) a type, this other one (the alias of) a value. It wouldn't add any power, with this, you can do is(name) and is(typeof(name)) already to find out if `name` is an alias of a type or value respectively.
In a type function alias cannot bind to values at all. It can only bind to symbols or types. If you want to pass values in you have to use the appropriate parameter type. Which just like in regular functions has to be something the argument can convert to. all types can implicitly convert to alias, which is a regular type. and signifies that the variable holds types or symbols.
May 11
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/11/20 5:58 PM, Stefan Koch wrote:
 
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
This seems like a problematic limitation. Aliases can bind to values when they are part of a tuple. This means that type functions are going to have to do weird template acrobatics to deal with mixed tuples. For a long time, alias parameters wouldn't bind to int because it was a keyword, not a symbol, even though you could alias int as a declaration. It wouldn't even bind them to aliases of int (even though they are not keywords). That has since been fixed, and everything works great. I think we should avoid arbitrary limitations like this, even if it's harder to get it to work. -Steve
May 11
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 12 May 2020 at 01:44:45 UTC, Steven Schveighoffer 
wrote:
 On 5/11/20 5:58 PM, Stefan Koch wrote:
 
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
This seems like a problematic limitation. Aliases can bind to values when they are part of a tuple. This means that type functions are going to have to do weird template acrobatics to deal with mixed tuples. For a long time, alias parameters wouldn't bind to int because it was a keyword, not a symbol, even though you could alias int as a declaration. It wouldn't even bind them to aliases of int (even though they are not keywords). That has since been fixed, and everything works great. I think we should avoid arbitrary limitations like this, even if it's harder to get it to work. -Steve
The reason that happened is not so much a parser issue. But it's because Basic Types are not Symbols. Which is indeed a more or less arbitrary limitation. Let's go through the reasoning for type functions now. let's assume alias A would bind to a value, the string foo for example. string F(alias A) { pragma(msg, typeof(A).stringof); // prints alias pragma(__traits(identifier, A); // error a string has no identifer. pragma(msg, is(A == string)); // prints false, because A does not hold the type string pragma(msg, A) prints "foo"; pragma(msg, A == "foo") // error can't compare alias and string pragma(msg, is(A, "foo") // doesn't parse. The string "foo" is not a valid identifier return A; // error alias does not implicitly convert to string. } You see there would not be a point in allowing that.
May 11
next sibling parent reply Johannes Loher <johannes.loher fg4f.de> writes:
On Tuesday, 12 May 2020 at 06:42:02 UTC, Stefan Koch wrote:
 On Tuesday, 12 May 2020 at 01:44:45 UTC, Steven Schveighoffer 
 wrote:
 On 5/11/20 5:58 PM, Stefan Koch wrote:
 
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
This seems like a problematic limitation. Aliases can bind to values when they are part of a tuple. This means that type functions are going to have to do weird template acrobatics to deal with mixed tuples. For a long time, alias parameters wouldn't bind to int because it was a keyword, not a symbol, even though you could alias int as a declaration. It wouldn't even bind them to aliases of int (even though they are not keywords). That has since been fixed, and everything works great. I think we should avoid arbitrary limitations like this, even if it's harder to get it to work. -Steve
The reason that happened is not so much a parser issue. But it's because Basic Types are not Symbols. Which is indeed a more or less arbitrary limitation. Let's go through the reasoning for type functions now. let's assume alias A would bind to a value, the string foo for example. string F(alias A) { pragma(msg, typeof(A).stringof); // prints alias pragma(__traits(identifier, A); // error a string has no identifer. pragma(msg, is(A == string)); // prints false, because A does not hold the type string pragma(msg, A) prints "foo"; pragma(msg, A == "foo") // error can't compare alias and string pragma(msg, is(A, "foo") // doesn't parse. The string "foo" is not a valid identifier return A; // error alias does not implicitly convert to string. } You see there would not be a point in allowing that.
Honestly, to me this sounds like a total mess. If templates allow binding alias to a value, so should type functions, or they should use a different name than "alias". Otherwise it's just confusing. To me it looks like for type functions, you think of "alias" as something like the "type of types". But for alias template parameters and alias declarations it is something completely different (as Steven explained, it is an unrestricted compile-time name). For example, consider the following: enum tpl(alias i) = i == "foo"; void main() { static immutable a = "foo"; enum b = "foo"; auto c = "foo"; static assert(tpl!"foo" == true); static assert(tpl!a == true); static assert(tpl!b == true); static assert(!__traits(compiles, tpl!c)); } If type functions also use "alias" to specify their parameters, from a user perspective, I'd expect the following to work just as well: auto tpl(alias i) { return i == "foo"; } void main() { static immutable a = "foo"; enum b = "foo"; auto c = "foo"; static assert(tpl("foo") == true); static assert(tpl(a) == true); static assert(tpl(b) == true); static assert(!__traits(compiles, tpl(c))); } I agree that maybe there is not that much for it (you do intend to allow mixing with "regular" parameters, right? So these could be used most of the time to solve this). But if "alias" is used and this does not work, it just breaks expectations. What about simply using "type" (or something similar) instead? You are calling it _type_ functions after all... I believe most of these issues misunderstandings come from the fact that people seem to look at this from different angles. From what I understand, you mostly look at it from an implementation perspective (and I am very grateful to you for taking the initiative here) but most others will look at it from a theoretical / language design perspective. For example, you mentioned somewhere that you believe this could be part of the language much sooner than new CTFE. Personally, I couldn't agree less: The implementation as you envision it now might be done much faster than finishing new CTFE but for CTFE, we already know all the interactions with with other language features and how it should behave. For type functions, we basically have no clue. A _lot_ of thought needs to go into that before we can actually get something like this in the language in order to ensure we still have a consistent language and its features interact well. A "naive" implementation just won't do it. That said, I don't want to diminish your efforts here at all. On the contrary, I appreciate it very much. This is a very good way to actually learn about possible interactions and how this fits in the language.
May 12
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 12 May 2020 at 11:39:04 UTC, Johannes Loher wrote:

 Honestly, to me this sounds like a total mess. If templates 
 allow binding alias to a value, so should type functions, or 
 they should use a different name than "alias". Otherwise it's 
 just confusing. To me it looks like for type functions, you 
 think of "alias" as something like the "type of types". But for 
 alias template parameters and alias declarations it is 
 something completely different (as Steven  explained, it is an 
 unrestricted compile-time name). For example, consider the 
 following: [ ... ]
are you sure that that is the case for alias declarations? For me alias a = "Foo"; causes Error: basic type expected, not "Foo" Which is identical to how they behave in type functions;
May 12
next sibling parent reply Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 12 May 2020 at 14:14:21 UTC, Stefan Koch wrote:
 are you sure that that is the case for alias declarations?
 For me
 alias a = "Foo";
 causes Error: basic type expected, not "Foo"
 Which is identical to how they behave in type functions;
Can this old design flaw be fixed? 'alias a = Alias!"Foo"' shouldn't be necessary. We should be able to alias directly everything that we can pass to an alias template parameter.
May 12
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 12 May 2020 at 14:41:52 UTC, Max Samukha wrote:
 On Tuesday, 12 May 2020 at 14:14:21 UTC, Stefan Koch wrote:
 are you sure that that is the case for alias declarations?
 For me
 alias a = "Foo";
 causes Error: basic type expected, not "Foo"
 Which is identical to how they behave in type functions;
Can this old design flaw be fixed? 'alias a = Alias!"Foo"' shouldn't be necessary. We should be able to alias directly everything that we can pass to an alias template parameter.
Which name does "Foo" have? There is a good reason for separating Symbols and literals.
May 12
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/12/20 11:42 AM, Stefan Koch wrote:
 On Tuesday, 12 May 2020 at 14:41:52 UTC, Max Samukha wrote:
 On Tuesday, 12 May 2020 at 14:14:21 UTC, Stefan Koch wrote:
 are you sure that that is the case for alias declarations?
 For me
 alias a = "Foo";
 causes Error: basic type expected, not "Foo"
 Which is identical to how they behave in type functions;
Can this old design flaw be fixed? 'alias a = Alias!"Foo"' shouldn't be necessary. We should be able to alias directly everything that we can pass to an alias template parameter.
Which name does "Foo" have?
alias x = Alias!"Foo"; pragma(msg, __traits(identifier, x)); // `a` This is the name the Alias template gives to its parameter.
 There is a good reason for separating Symbols and literals.
Whether or not you like it, you can alias literals to template parameters. If type functions cannot do this, we will be stuck with using templates for many things, and I can totally envision this pattern happening: string foo(string[] symbolnames, alias dummy = __dummy) { // do something with symbolnames, ignore dummy } to force a type function instead of using CTFE when you want to manipulate a compile-time list of strings. It reminds me of what people would resort to for a static foreach over a tuple when all they wanted was the index (before static foreach existed). -Steve
May 12
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 12 May 2020 at 16:12:42 UTC, Steven Schveighoffer 
wrote:

 to force a type function instead of using CTFE when you want to 
 manipulate a compile-time list of strings.

 It reminds me of what people would resort to for a static 
 foreach over a tuple when all they wanted was the index (before 
 static foreach existed).

 -Steve
There is no essentially no difference between a type function and CTFE. Type functions are really just passing type as expressions to CTFE. Well that and the ability to assign to alias. Having a dummy alias parameter just means ctfeOnly.
May 12
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/12/20 12:20 PM, Stefan Koch wrote:
 On Tuesday, 12 May 2020 at 16:12:42 UTC, Steven Schveighoffer wrote:
 
 to force a type function instead of using CTFE when you want to 
 manipulate a compile-time list of strings.

 It reminds me of what people would resort to for a static foreach over 
 a tuple when all they wanted was the index (before static foreach 
 existed).
There is no essentially no difference between a type function and CTFE. Type functions are really just passing type as expressions to CTFE. Well that and the ability to assign to alias. Having a dummy alias parameter just means ctfeOnly.
Right. What I meant was, a function that is intended ONLY to be used for ctfe could put in a dummy parameter, or they could take the string array as an alias[] (if it's supported), and have it support more than just string arrays. It would be awkward and confusing to have a different set of functions or constructs to deal with type lists than ones that deal with values (or mixed lists). Not only that, but it's also odd for aliases to work differently here than they do in templates. -Steve
May 12
prev sibling parent reply Johannes Loher <johannes.loher fg4f.de> writes:
On Tuesday, 12 May 2020 at 16:12:42 UTC, Steven Schveighoffer 
wrote:
 I can totally envision this pattern happening:

 string foo(string[] symbolnames, alias dummy = __dummy)
 {
    // do something with symbolnames, ignore dummy
 }

 to force a type function instead of using CTFE when you want to 
 manipulate a compile-time list of strings.
Why would you ever do this? To enforce that the function is only called at compile time? Looks like a hack to a problem we should have a proper solution for instead.
May 12
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/12/20 1:32 PM, Johannes Loher wrote:
 On Tuesday, 12 May 2020 at 16:12:42 UTC, Steven Schveighoffer wrote:
 I can totally envision this pattern happening:

 string foo(string[] symbolnames, alias dummy = __dummy)
 {
    // do something with symbolnames, ignore dummy
 }

 to force a type function instead of using CTFE when you want to 
 manipulate a compile-time list of strings.
Why would you ever do this? To enforce that the function is only called at compile time?
I'm assuming in type functions we have further leeways that aren't available in CTFE functions. Like, for instance, SomeTemplate!(symbolnames[0]) should be doable in a type function, but would not be doable in a CTFE function. Not sure if that would require symbolnames be an alias[] or not.
 Looks like a hack to a problem we should have a proper 
 solution for instead.
I agree, which is why I brought it up. -Steve
May 12
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 12 May 2020 at 19:15:54 UTC, Steven Schveighoffer 
wrote:
 On 5/12/20 1:32 PM, Johannes Loher wrote:
 On Tuesday, 12 May 2020 at 16:12:42 UTC, Steven Schveighoffer 
 wrote:
 I can totally envision this pattern happening:

 string foo(string[] symbolnames, alias dummy = __dummy)
 {
    // do something with symbolnames, ignore dummy
 }

 to force a type function instead of using CTFE when you want 
 to manipulate a compile-time list of strings.
Why would you ever do this? To enforce that the function is only called at compile time?
I'm assuming in type functions we have further leeways that aren't available in CTFE functions. Like, for instance, SomeTemplate!(symbolnames[0]) should be doable in a type function, but would not be doable in a CTFE function. Not sure if that would require symbolnames be an alias[] or not.
 Looks like a hack to a problem we should have a proper 
 solution for instead.
I agree, which is why I brought it up. -Steve
Actually no. If you did that you would be passing a variable to a template which expects a type. Type functions are not magic and they are not dynamically typed. Their purpose is to offer an alternative for introspection usecases. In which the genericty of templates is not needed and the compiler is unwanted. There no way to make a type out of a type variable. Because that would require a dynamic type system. Type functions provide a compile time interface to introspection abilities without incurring the cost of templates, and providing the option to write code in imperative programming style. johannes I have put some thought into how this will interact and so far the simplicity of the implementation seems to point towards very nice interplay as well. When there is more of a full featured implementation I will present a more formal discussion about the topic.
May 12
prev sibling parent Max Samukha <maxsamukha gmail.com> writes:
On Tuesday, 12 May 2020 at 15:42:03 UTC, Stefan Koch wrote:
that we can pass to an alias template parameter.
 Which name does "Foo" have?
Any unique identifier that would distinguish it from other symbols and literals? Give it the name of Alias!"Foo"? Currently, this fails: static assert(__traits(isSame, Alias!"Foo", "Foo")); Error: static assert: __traits(isSame, "Foo", "Foo") is false The language is desperately trying to do the right thing but fails.
 There is a good reason for separating Symbols and literals.
I think there is not, just as there is no good reason for separating built-in and user-defined types.
May 12
prev sibling parent Johannes Loher <johannes.loher fg4f.de> writes:
On Tuesday, 12 May 2020 at 14:14:21 UTC, Stefan Koch wrote:
 On Tuesday, 12 May 2020 at 11:39:04 UTC, Johannes Loher wrote:

 Honestly, to me this sounds like a total mess. If templates 
 allow binding alias to a value, so should type functions, or 
 they should use a different name than "alias". Otherwise it's 
 just confusing. To me it looks like for type functions, you 
 think of "alias" as something like the "type of types". But 
 for alias template parameters and alias declarations it is 
 something completely different (as Steven  explained, it is an 
 unrestricted compile-time name). For example, consider the 
 following: [ ... ]
are you sure that that is the case for alias declarations? For me alias a = "Foo"; causes Error: basic type expected, not "Foo" Which is identical to how they behave in type functions;
You are right, the language is already inconsistent in that regard. However, the following works: void main() { static immutable x = 0; alias other_x = x; static assert(other_x == 0); } So type functions should support at least that much (I don't know if they do in what you have in mind right now): auto typeFun(alias x) { return x == "foo"; } void main() { static immutable x = "foo" static assert(typeFun(x)); } Let's not create a third meaning for "alias"... Rather we should unify the 2 meanings that exist right now.
May 12
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/12/20 2:42 AM, Stefan Koch wrote:
 On Tuesday, 12 May 2020 at 01:44:45 UTC, Steven Schveighoffer wrote:
 On 5/11/20 5:58 PM, Stefan Koch wrote:
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
This seems like a problematic limitation. Aliases can bind to values when they are part of a tuple. This means that type functions are going to have to do weird template acrobatics to deal with mixed tuples. For a long time, alias parameters wouldn't bind to int because it was a keyword, not a symbol, even though you could alias int as a declaration. It wouldn't even bind them to aliases of int (even though they are not keywords). That has since been fixed, and everything works great. I think we should avoid arbitrary limitations like this, even if it's harder to get it to work. -Steve
The reason that happened is not so much a parser issue. But it's because Basic Types are not Symbols. Which is indeed a more or less arbitrary limitation. Let's go through the reasoning for type functions now. let's assume alias A would bind to a value, the string foo for example. string F(alias A) {     pragma(msg, typeof(A).stringof); // prints alias     pragma(__traits(identifier, A); // error a string has no identifer.     pragma(msg, is(A == string)); // prints false, because A does not hold the type string     pragma(msg, A) prints "foo";     pragma(msg, A == "foo") // error can't compare alias and string     pragma(msg, is(A, "foo") // doesn't parse. The string "foo" is not a valid identifier     return A; // error alias does not implicitly convert to string. } You see there would not be a point in allowing that.
This is what happens now: string F(A...)() { pragma(msg, typeof(A[0]).stringof); // prints string //pragma(msg, __traits(identifier, A[0])); // [1] error a string has no identifer. pragma(msg, is(A[0] == string)); // prints false, because A does not hold the type string pragma(msg, A[0]); // prints "foo" pragma(msg, A[0] == "foo"); // prints true //pragma(msg, is(A[0], "foo")); // [2] doesn't parse. The string "foo" is not a valid identifier return A[0]; // works } enum x = F!"foo"; For [1], yeah, I get that it doesn't have an identifier. So what. Make the identifier null if you need it. Or it's an error and we deal with that. There should be mechanisms we can use to find common properties about everything. We already do that quite a bit with template programming. For [2], there was some kind of syntax error. I'm not sure what it was you were trying to say there. The compiler complained that the comma wasn't valid. I don't want to assume anything for is expressions, there are so many forms. If I have a function that sorts an alias[], I want it to sort AliasSeq!(62, 56, 23, 77) just as well as it sorts AliasSeq!(int, long, bool), just as well as it sorts AliasSeq!(int, 5, "hello"). The thing I want is a) mutable array instead of tuple, and b) everything is compile-time, so there is no "You can't use this at compile time" nonsense. I was hoping that your type functions would fulfill this. -Steve
May 12
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Monday, 11 May 2020 at 21:58:57 UTC, Stefan Koch wrote:
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
 If you want to pass values in you have to use the appropriate 
 parameter type.
Although it's called a 'type function', it should be able to make all template recursion unnecessary. If you can't have an alias parameter that might be a value or not, then that's a fundamental limitation to achieving this goal. A template alias parameter can take anything, as can a template sequence parameter. We need type functions to be able to accept any compile-time sequence for processing, how can we do that if alias[] won't accept values? People will have to resort to template recursion to have a complete solution.
May 30
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 30 May 2020 at 11:15:19 UTC, Nick Treleaven wrote:
 On Monday, 11 May 2020 at 21:58:57 UTC, Stefan Koch wrote:
 In a type function alias cannot bind to values at all.
 It can only bind to symbols or types.
 If you want to pass values in you have to use the appropriate 
 parameter type.
Although it's called a 'type function', it should be able to make all template recursion unnecessary. If you can't have an alias parameter that might be a value or not, then that's a fundamental limitation to achieving this goal. A template alias parameter can take anything, as can a template sequence parameter. We need type functions to be able to accept any compile-time sequence for processing, how can we do that if alias[] won't accept values? People will have to resort to template recursion to have a complete solution.
so alias can and does accept values, BUT, you cannot really do anything with them inside the type function. since the only expose __traits, and a few bultin properties such as tupleof sizeof and stringof.
May 30
prev sibling parent reply Nick Treleaven <nick geany.org> writes:
On Sunday, 10 May 2020 at 08:39:42 UTC, Nick Treleaven wrote:
 bool anySatisfy(alias Tem, alias[] S)
 {
     bool found;
     alias E;
     while (S.length)
     {
         E = S[0];
         if (Tem!E)
             return true;
         S = S[1..$];
     }
     return false;
 }
I realized after posting that Tem!E can't work, because the type function must be compiled before evaluating E. Tem!E would be a constant expression, if it even compiles (e.g. passing E as a template alias parameter, not passing the value of E). So as Stefan wrote, type functions are like CTFE functions (maybe a superset?).
May 13
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 13 May 2020 at 19:22:36 UTC, Nick Treleaven wrote:
 On Sunday, 10 May 2020 at 08:39:42 UTC, Nick Treleaven wrote:
 bool anySatisfy(alias Tem, alias[] S)
 {
     bool found;
     alias E;
     while (S.length)
     {
         E = S[0];
         if (Tem!E)
             return true;
         S = S[1..$];
     }
     return false;
 }
I realized after posting that Tem!E can't work, because the type function must be compiled before evaluating E. Tem!E would be a constant expression, if it even compiles (e.g. passing E as a template alias parameter, not passing the value of E). So as Stefan wrote, type functions are like CTFE functions (maybe a superset?).
Well E is defined at interpretation time. So I _could_ deferr the template instance until the value of E is known. But the whole purpose of type functions is to discourage the use of templates in cases in which they are not needed. In cases where a template is needed type functions will be of little use. (you can possibly use them to remove internal instances).
May 14
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 14.05.20 10:09, Stefan Koch wrote:
 
 In cases where a template is needed type functions will be of little use.
This is just not true. Template instantiations can be the result of the type function. E.g., map a list of types of struct members to array types with those types as element types. And anything that can be the result of a type function can be an argument to a type function for further processing. In general, it is vastly more often than not a bad idea to claim that feature A shouldn't work with feature B because nobody would want to combine them anyway.
May 17
prev sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Saturday, 9 May 2020 at 06:39:20 UTC, Stefan Koch wrote:
 On Wednesday, 6 May 2020 at 12:01:11 UTC, Stefan Koch wrote:
 On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Now works on the Talias branch
https://github.com/dlang/dmd/compare/master...UplinkCoder:talias?expand=1 As you can see the modifications aren't even that heavy. I am confident this will take significantly less time than re-implementing CTFE. Because it's a new feature and bugs can be fixed as they are discovered.
As of a few minutes ago the following code compiles without errors! string fqn(alias t) { string result = t.stringof; alias p; p = t; bool good = is(typeof(__traits(parent, p))); while(good) { p = __traits(parent, p); result = p.stringof ~ "." ~ result; // result = __traits(identifier, parent) ~ "." ~ result; good = is(typeof(__traits(parent, p))); } return cast(string) result; } struct S { struct X { int xx; static assert( fqn!xx == "module " ~ __MODULE__ ~ ".S.X.xx" ); } }
__traits(identifier) works now making the code a little bit nicer. string fqn(alias t) { string result = t.stringof; alias p; p = t; bool good = is(typeof(__traits(parent, p))); while(good) { p = __traits(parent, p); result = __traits(identifier, p) ~ "." ~ result; good = is(typeof(__traits(parent, p))); } return result; } struct S { struct X { int xx; static assert( fqn!xx == __MODULE__ ~ ".S.X.xx" ); } } I am not super happy with how this looks ... especially that I am getting __traits(parent) twice. If anyone has a suggestion of how to write this better. Please tell me :) Also I am documenting the process of implementing this on my youtube channel (https://www.youtube.com/channel/UCdZpjZcilNRQH2GDY-P5yVA) (whenever I remember to press record and having something interesting to show)
May 10
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Sunday, 10 May 2020 at 08:43:42 UTC, Stefan Koch wrote:
 On Saturday, 9 May 2020 at 06:39:20 UTC, Stefan Koch wrote:
 [...]
__traits(identifier) works now making the code a little bit nicer. [...]
Would have been nice to have that video in a better quality than 360p. The text is completely unreadable.
May 10
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 10 May 2020 at 09:33:29 UTC, Patrick Schluter wrote:
 On Sunday, 10 May 2020 at 08:43:42 UTC, Stefan Koch wrote:
 On Saturday, 9 May 2020 at 06:39:20 UTC, Stefan Koch wrote:
 [...]
__traits(identifier) works now making the code a little bit nicer. [...]
Would have been nice to have that video in a better quality than 360p. The text is completely unreadable.
Oh yes I just realized that. Let me to a reupload ... youtube seems to have problems.
May 10
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Sunday, 10 May 2020 at 09:35:18 UTC, Stefan Koch wrote:
 On Sunday, 10 May 2020 at 09:33:29 UTC, Patrick Schluter wrote:
 On Sunday, 10 May 2020 at 08:43:42 UTC, Stefan Koch wrote:
 On Saturday, 9 May 2020 at 06:39:20 UTC, Stefan Koch wrote:
 [...]
__traits(identifier) works now making the code a little bit nicer. [...]
Would have been nice to have that video in a better quality than 360p. The text is completely unreadable.
Oh yes I just realized that. Let me to a reupload ... youtube seems to have problems.
youtube changed to only showing SD content it seems like? Because of covid-19 ... this is crazy.
May 10
parent Nick Treleaven <nick geany.org> writes:
On Sunday, 10 May 2020 at 09:50:09 UTC, Stefan Koch wrote:
 youtube changed to only showing SD content it seems like?
 Because of covid-19 ... this is crazy.
I think it defaults to SD, but you can change the resolution to HD. Although for me the text is still too small to read ;-)
May 10
prev sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 6 May 2020 at 12:01:11 UTC, Stefan Koch wrote:
 On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Now works on the Talias branch
https://github.com/dlang/dmd/compare/master...UplinkCoder:talias?expand=1 As you can see the modifications aren't even that heavy. I am confident this will take significantly less time than re-implementing CTFE. Because it's a new feature and bugs can be fixed as they are discovered.
I've switched my development process, to targeting stable and keeping the new branch green all the time. That way super-delays like what happend with newCTFE should be avoided. On the new branch the following example will compile ``` auto getUDAs(alias T) { return __traits(getAttributes, T); pragma(msg, typeof(return)); } // we can't parse alias[]; alias alias_array = typeof(getUDAs(SerializeTag)); struct SerializeTag {} struct TypeWithTags { alias type; alias_array tags; } TypeWithTags typeWithTags(alias T) { auto udas = __traits(getAttributes, T); return TypeWithTags(T, udas); } SerializeTag struct ThisIsAType {} alias x = ThisIsAType; pragma(msg, typeWithTags(x)); // output TypeWithTags((ThisIsAType), [(SerializeTag)]) ``` The branch is here (and should be green most of the time): Note that you'll have to comment out the call to `preFunctionParameters` in expressionsem.d : 4176. That is not done by default because it breaks the tests which make sure that functions are not called with types. (which is obivously what type functions want to do ;) )
May 19
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 19 May 2020 at 14:42:25 UTC, Stefan Koch wrote:
 The branch is here (and should be green most of the time):
Doh ... I have to actually link it :) And I didn't mean branch I meant PR ;) https://github.com/dlang/dmd/pull/11146
May 19
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 19 May 2020 at 14:48:31 UTC, Stefan Koch wrote:
 On Tuesday, 19 May 2020 at 14:42:25 UTC, Stefan Koch wrote:
 The branch is here (and should be green most of the time):
Doh ... I have to actually link it :) And I didn't mean branch I meant PR ;) https://github.com/dlang/dmd/pull/11146
This test now works with the GREEN branch auto getUDAs(alias T) { return __traits(getAttributes, T); pragma(msg, typeof(return)); } // we can't parse alias[]; alias alias_array = typeof(getUDAs(SerializeTag)); struct SerializeTag {} struct SuperTag {string s;} struct TypeWithTags { alias type; alias_array tags; } TypeWithTags typeWithTags(alias T) { auto udas = __traits(getAttributes, T); return TypeWithTags(T, udas); } SuperTag("Smoking Super Style") SerializeTag SuperTag("Super!") struct ThisIsAType { double y; } alias x = ThisIsAType; pragma(msg, "typeWithTags: ", typeWithTags(x)); //output: TypeWithTags((ThisIsAType), [SuperTag("Smoking Super Style"), (SerializeTag), SuperTag("Super!")])
May 27
parent Seb <seb wilzba.ch> writes:
On Wednesday, 27 May 2020 at 12:56:32 UTC, Stefan Koch wrote:
 On Tuesday, 19 May 2020 at 14:48:31 UTC, Stefan Koch wrote:
 [...]
This test now works with the GREEN branch auto getUDAs(alias T) { return __traits(getAttributes, T); pragma(msg, typeof(return)); } [...]
Nice! I guess it's slowly becoming time to write that DIP ;-)
May 27
prev sibling next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Hello Guys,

 After a bit of fiddling the following code (and only that 
 because I need do partial evaluation of the semantic pass on 
 the function body manually ...)
 Now works on the Talias branch

 string F(alias y)
 {
     return y.stringof;
 }
 static assert(F!(ulong) == "ulong");
 static assert(F!(uint) == "uint");
The following now works: string[] memberNames(alias T) { string[] result; if (is(typeof(T.tupleof))) { auto memberTuple = T.tupleof; // needs to be auto because we can't parse "alias[]" yet. // result.length = memberTuple.length; // we don't yet exclude this expression from semantic // will not be unrolled :) foreach(i, f; memberTuple) { // result[i] = __traits(identifier, f); // because we couldn't set the length this wouldn't work result ~= __traits(identifier, f); // note how the identifier f does not result in "f" ... this is neat but I might have to go back on this. } } return result; } struct S { int x; double y; float z; } pragma(msg, memberNames(S)); // output is ["x","y","z"] Note that the bang is not needed. Because in the end a type function is a regular function. The call mechanism just works, as long as an implicit cast from any type to alias is provided.
May 12
prev sibling parent reply Clueless <c lue.less> writes:
On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Hello Guys,

 After a bit of fiddling the following code (and only that 
 because I need do partial evaluation of the semantic pass on 
 the function body manually ...)
 Now works on the Talias branch

 string F(alias y)
 {
     return y.stringof;
 }
 static assert(F!(ulong) == "ulong");
 static assert(F!(uint) == "uint");
Since type functions aren't templates, and they're functions, could UFCS be applied?
May 12
parent Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 13 May 2020 at 02:00:37 UTC, Clueless wrote:
 On Wednesday, 6 May 2020 at 11:58:36 UTC, Stefan Koch wrote:
 Hello Guys,

 After a bit of fiddling the following code (and only that 
 because I need do partial evaluation of the semantic pass on 
 the function body manually ...)
 Now works on the Talias branch

 string F(alias y)
 {
     return y.stringof;
 }
 static assert(F!(ulong) == "ulong");
 static assert(F!(uint) == "uint");
Since type functions aren't templates, and they're functions, could UFCS be applied?
Possibly depends on whether it introduces ambiguity.
May 12