www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can we make mixin applications first class at the type level?

reply sighoya <sighoya gmail.com> writes:
Allowing

mixin template GenInt()
{
     const char[] type="Int";
}

void f(T)(T a)
{
     return;
}

void main()
{
   f!(GenInt!()) //or
   f!(mixin GenInt!());
   return ;
}

opens the door for quasi first class type calculus. Type mappings 
can be saved as strings and can be instantiated as types where it 
needs to be, e.g. passing two computed types to a trait function 
and determine their compatibility.

Why isn't it possible in D.

Further, why wee need to state mixin GenInt!() if GenInt is 
already a mixin? Isn't it just redundant?
Mar 20
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
I have no idea what you are trying to achieve, but I am willing 
to bet putting a type in a string is the wrong way to do it.

Why don't you give an example of what *outcome* you are trying to 
achieve first, maybe in a language you are more familiar with, 
and then we can tell you how to do it in D.
Mar 21
parent reply sighoya <sighoya gmail.com> writes:
On Thursday, 21 March 2019 at 09:07:03 UTC, FeepingCreature wrote:
 I have no idea what you are trying to achieve, but I am willing 
 to bet putting a type in a string is the wrong way to do it.

 Why don't you give an example of what *outcome* you are trying 
 to achieve first, maybe in a language you are more familiar 
 with, and then we can tell you how to do it in D.
Thanks for your open-heartedness. My intention is to allow type calculus. For this you would normally need first class types, i.e. types as values: Type type = int; which is not possible in D and may never be. But there is the possibility to model it over mixins if the parser would allow us to do so. For instance a tuple constructor: *(Type type1, Type type2, Type type3)=(type1,type2,type3) could be modeled with: mixin template stringToType(String s) { s } *(String type1, String type2, String type3)=(stringToType!type1,stringToType!type1,stringToType!type1) The idea in general is to generate structures generically over computed types: mixin template mapType(Map!(String,String) typeToTypeMapping,String selectedType) { const char[] s= typeToTypeMapping[selectedType]; } struct S(T,S) { T t; S s; } Map!(String,String) mapping= {"Int"=>"String", "String"=>"Char"} S!(mapType!(mapping,"Int"),mapType!(mapping,"String")) s; Of course we could simply write the complete struct S as string mixin, but most existing code isn't saved in this format because string mixins suck (no syntax highlighting). So allowing mixin expansion in type positions would allow to integrate with existing code better in this manner. It would be that easy if we weren't forced to apply mixins with the mixin keyword, why the hell we need to if we annotate our template with mixin. Then ComputetType!String would be parsed correctly by the parser.Otherwise wee need to allow (mixin ComputedType)!String by changing the grammar.
Mar 21
next sibling parent Nick Treleaven <nick geany.org> writes:
On Thursday, 21 March 2019 at 09:59:48 UTC, sighoya wrote:
 Of course we could simply write the complete struct S as string 
 mixin, but most existing code isn't saved in this format 
 because string mixins suck (no syntax highlighting).
In my editor (Geany/Scintilla) q{} contents are highlighted like code: https://dlang.org/spec/lex.html#token_strings
 It would be that easy if we weren't forced to apply mixins with 
 the mixin keyword, why the hell we need to if we annotate our 
 template with mixin.
For reasoning about code and hygiene, the use site has to indicate to the programmer that code is being inserted into the current scope, rather than just calling a function. The syntax could be tweaked so `$ident!targs(args)` expands to `mixin(ident!targs(args))`.
Mar 21
prev sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 21 March 2019 at 09:59:48 UTC, sighoya wrote:
 On Thursday, 21 March 2019 at 09:07:03 UTC, FeepingCreature 
 wrote:
 I have no idea what you are trying to achieve, but I am 
 willing to bet putting a type in a string is the wrong way to 
 do it.

 Why don't you give an example of what *outcome* you are trying 
 to achieve first, maybe in a language you are more familiar 
 with, and then we can tell you how to do it in D.
Thanks for your open-heartedness. My intention is to allow type calculus. For this you would normally need first class types, i.e. types as values: Type type = int;
Why don't you do alias type = int;? You can't redefine it, sure, but I don't see what mixing in a string buys you there, because that needs to be a compiletime variable and you can't redefine that either.
 which is not possible in D and may never be. But there is the 
 possibility to model it over mixins if the parser would allow 
 us to do so.

 For instance a tuple constructor:

 *(Type type1, Type type2, Type type3)=(type1,type2,type3)

 could be modeled with:

 mixin template stringToType(String s)
 {
    s
 }

 *(String type1, String type2, String 
 type3)=(stringToType!type1,stringToType!type1,stringToType!type1)


 The idea in general is to generate structures generically over 
 computed types:

 mixin template mapType(Map!(String,String) 
 typeToTypeMapping,String selectedType)
 {
 const char[] s= typeToTypeMapping[selectedType];
 }

 struct S(T,S)
 {
    T t;
    S s;
 }

 Map!(String,String) mapping= {"Int"=>"String", "String"=>"Char"}
 S!(mapType!(mapping,"Int"),mapType!(mapping,"String")) s;

 Of course we could simply write the complete struct S as string 
 mixin, but most existing code isn't saved in this format 
 because string mixins suck (no syntax highlighting).
I still do not understand in any way what you are trying to achieve. std.typecons.tuple exists. std.meta exists. Templates let you do basically anything with types... This all sounds like gibberish to me. I feel you're probably committing an X-Y problem; you're talking excitedly about an overspecific solution to a problem that I can only dimly guess at. All I can tell you is, I am willing to bet money that whatever problem you have, it has a simpler solution in D and string template mixins aren't related to it.
Mar 21
parent reply sighoya <sighoya gmail.com> writes:
On Thursday, 21 March 2019 at 11:55:44 UTC, FeepingCreature wrote:

 Why don't you do alias type = int;?

 You can't redefine it, sure, but I don't see what mixing in a 
 string buys you there, because that needs to be a compiletime 
 variable and you can't redefine that either.
No, you can redefine in a normal function which is executed at compile time with enum. This function computes over strings, stores strings in arrays and maps and manipulates them which you can't do with normal type level programming in templates. But what you get out of this function is a string, not a type as you wanted so you mixin over it and turn it into an type and apply a template to it.
 I still do not understand in any way what you are trying to 
 achieve. std.typecons.tuple exists. std.meta exists. Templates 
 let you do basically anything with types...
No, they don't. std.meta give you AliasSeq: template AliasSeq(TList...) { alias AliasSeq = TList; } which seems to be a hack. Does it work if I want to have a map instead, no! The limitations we have here are owed by the constrained support of type level programming. We can't do all what we can do at runtime at compile time because the static programming is very limited except? we execute a runtime function at compile time but a runtime function can also be executed at runtime and can't handle transformations on types because types don't exist at runtime, but strings exists at runtime, right? So we can manipulate on them and turn them back to types with mixins until we get a better solution with real first class types.
This all sounds
 like gibberish to me. I feel you're probably committing an X-Y 
 problem; you're talking excitedly about an overspecific 
 solution to a problem that I can only dimly guess at.
Maybe, but also the requested change is really a small one.
 All I can tell you is, I am willing to bet money that whatever 
 problem you have, it has a simpler solution in D and string 
 template mixins aren't related to it.
If we would have AliasMap which is mutable, then maybe.
Mar 21
parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Thursday, 21 March 2019 at 12:49:44 UTC, sighoya wrote:
 No, they don't. std.meta give you AliasSeq:

 template AliasSeq(TList...)
 {
     alias AliasSeq = TList;
 }

 which seems to be a hack.
 Does it work if I want to have a map instead, no!
staticMap... do you mean a mapping of types to other types? Use specialized templates: alias foo(T : int) = string; alias foo(T : string) = float;
 The limitations we have here are owed by the constrained 
 support of type level programming. We can't do all what we can 
 do at runtime at compile time because the static programming is 
 very limited except? we execute a runtime function at compile 
 time but a runtime function can also be executed at runtime and 
 can't handle transformations on types because types don't exist 
 at runtime, but strings exists at runtime, right?
 ...
 If we would have AliasMap which is mutable, then maybe.
It sounds like you're committed to doing imperative logic to manipulate types, when there's a rich library of functional template-based solutions to type problems. Could D offer ctfe native manipulation of types? Sure, and it would be handier in many situations, but there's a rich library of functional, template-based idioms available for your use that can, in my opinion, probably solve any type problem you have, and won't require you using strings as a weird metalanguage out of no clearly established need.
Mar 21
next sibling parent sighoya <sighoya gmail.com> writes:
On Thursday, 21 March 2019 at 12:56:59 UTC, FeepingCreature wrote:
 On Thursday, 21 March 2019 at 12:49:44 UTC, sighoya wrote:
 No, they don't. std.meta give you AliasSeq:

 template AliasSeq(TList...)
 {
     alias AliasSeq = TList;
 }

 which seems to be a hack.
 Does it work if I want to have a map instead, no!
staticMap... do you mean a mapping of types to other types? Use specialized templates: alias foo(T : int) = string; alias foo(T : string) = float;
Okay, thanks for mentioning this. I think this is the mosaic I was missing.
 It sounds like you're committed to doing imperative logic to 
 manipulate types, when there's a rich library of functional 
 template-based solutions to type problems. Could D offer ctfe 
 native manipulation of types? Sure, and it would be handier in 
 many situations, but there's a rich library of functional, 
 template-based idioms available for your use that can, in my 
 opinion, probably solve any type problem you have, and won't 
 require you using strings as a weird metalanguage out of no 
 clearly established need.
Yes, I would like to manipulate on types like I would manipulate on values in D. Playing with alias this to achieve these things feels like a bit of encoding D into another language. But okay, using alias this for maps is a with immutable updates+ recursion seems to be a big step forward until real first class types arrive.
Mar 21
prev sibling parent arakan arkino <marcosdonalonso gmail.com> writes:
On Thursday, 21 March 2019 at 12:56:59 UTC, FeepingCreature wrote:
 On Thursday, 21 March 2019 at 12:49:44 UTC, sighoya wrote:
 No, they don't. std.meta give you AliasSeq:

 template AliasSeq(TList...)
 {
     alias AliasSeq = TList;
 }

 which seems to be a hack.
 Does it work if I want to have a map instead, no!
staticMap... do you mean a mapping of types to other types? Use specialized templates: alias foo(T : int) = string; alias foo(T : string) = float;
 The limitations we have here are owed by the constrained 
 support of type level programming. We can't do all what we can 
 do at runtime at compile time because the static programming 
 is very limited except? we execute a runtime function at 
 compile time but a runtime function can also be executed at 
 runtime and can't handle transformations on types because 
 types don't exist at runtime, but strings exists at runtime, 
 right?
 ...
 If we would have AliasMap which is mutable, then maybe.
It sounds like you're committed to doing imperative logic to manipulate types, when there's a rich library of functional template-based solutions to type problems. Could D offer ctfe native manipulation of types? Sure, and it would be handier in many situations, but there's a rich library of functional, template-based idioms available for your use that can, in my opinion, probably solve any type problem you have, and won't require you using strings as a weird metalanguage out of no clearly established need.
Why don't you give an example of what *outcome* you are trying to achieve first, maybe in a language you are more familiar with, and then we can tell you how to do it in D.
Apr 20
prev sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Wednesday, 20 March 2019 at 17:34:49 UTC, sighoya wrote:
 Allowing

 mixin template GenInt()
 {
     const char[] type="Int";
 }

 { ... }
I am already working on proper first class types for D. Which don't have to go through strings to work. However this work is progressing very slowly as my day-job and newCTFE have higher priority. Using strings and mixins to emulate first-class types may work for some cases but will inevitably fall short for others. I'd be interested in your usecase so I can add more do the motivation section of my future DIP draft. Cheers, Stefan
Mar 21
parent reply sighoya <sighoya gmail.com> writes:
On Thursday, 21 March 2019 at 10:11:34 UTC, Stefan Koch wrote:
 On Wednesday, 20 March 2019 at 17:34:49 UTC, sighoya wrote:
 Allowing

 mixin template GenInt()
 {
     const char[] type="Int";
 }

 { ... }
I am already working on proper first class types for D.
Awesome. Wouldn't it complicate the grammar of D?,e.g.: int * p; // does it mean declaring an int or calling * : (Type,Type)->(Type,Type)
 Which don't have to go through strings to work.
It would be much more of semantic than handling with strings.
 However this work is progressing very slowly as my day-job and 
 newCTFE have higher priority.
Understand, is there any repo where you work on first class types?
 Using strings and mixins to emulate first-class types may work 
 for some cases but will inevitably fall short for others.
I don't how you want to implement first class types, but type comparison would trigger anyway pointer comparison of strings. But steady converting type to string to type is ugly with the mixin solution, but better than nothing. We have currently limited type calculus with AliasSeq which is however complicated to use and very limited.
 I'd be interested in your usecase so I can add more do the 
 motivation section of my future DIP draft.
Nice, beside mapping of types it would be nice to switch case over them: switch(typeof(expr)) { case int: case float: } instead of creating enums tediously.
 Cheers,
 Stefan
For reasoning about code and hygiene, the use site has to 
indicate to the programmer that code is being inserted into the 
current scope, rather than just calling a function.
Hygiene isn't the problem as mixins are semi hygenic, in case of collision the collided parts get scoped. Further, when we already annotate a template as mixin, then it is clear that it will be expanded. it would only make sense for a template without mixin annotation.
In my editor (Geany/Scintilla) q{} contents are highlighted like 
code:
Yes, but fortunately most written code isn't wrapped inside a quote environment ;). But quoting is a good solution for AST parsing though. However, I don't understand why the people here don't like macros, they would be much more idiomatic than STRING MIXINS!!!
The syntax could be tweaked so `$ident!targs(args)` expands to 
`mixin(ident!targs(args))`.
OMG is this ugly!
Mar 21
parent Olivier FAURE <couteaubleu gmail.com> writes:
On Thursday, 21 March 2019 at 11:10:27 UTC, sighoya wrote:
 Awesome. Wouldn't it complicate the grammar of D?,e.g.:

 int * p; // does it mean declaring an int or calling * : 
 (Type,Type)->(Type,Type)
He could add an additional syntax, eg alias(expr), which would box expressions in places where the grammar expect a type. So, this would be forbidden: type myCustomType(int, string); myCustomType(42, "foobar")* myVar; But this would be allowed: type myCustomType(int, string); alias(myCustomType(42, "foobar"))* myVar; (notice the placement of the * in the second version, which is outside of the alias expression, because a postfix * isn't valid syntax in an expression)
For reasoning about code and hygiene, the use site has to 
indicate to the programmer that code is being inserted into the 
current scope, rather than just calling a function.
Hygiene isn't the problem as mixins are semi hygenic, in case of collision the collided parts get scoped.
Please make multiple posts when answering different people. Some people use this forum as a mailing list, and it's generally expected that each message answers one specific message.
Mar 21