Jump to page: 1 2
Thread overview
Can we make mixin applications first class at the type level?
Mar 20, 2019
sighoya
Mar 21, 2019
FeepingCreature
Mar 21, 2019
sighoya
Mar 21, 2019
Nick Treleaven
Mar 21, 2019
FeepingCreature
Mar 21, 2019
sighoya
Mar 21, 2019
FeepingCreature
Mar 21, 2019
sighoya
Apr 20, 2019
arakan arkino
Mar 21, 2019
Stefan Koch
Mar 21, 2019
sighoya
Mar 21, 2019
Olivier FAURE
March 20, 2019
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?
March 21, 2019
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.
March 21, 2019
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.
March 21, 2019
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
March 21, 2019
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))`.
March 21, 2019
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!

March 21, 2019
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.
March 21, 2019
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.
March 21, 2019
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.
March 21, 2019
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.

« First   ‹ Prev
1 2