July 21, 2023

On Friday, 21 July 2023 at 08:52:47 UTC, Quirin Schroll wrote:

>

On Thursday, 20 July 2023 at 13:44:15 UTC, Commander Zot wrote:

>

wouldn't it be possible to have something like first class types (at compile time) to replace tamplates for type logic with regular functions executed at CTFE?

Of course, that’s possible. You’d have a type called Type that represents types. Of course, Type does not actually exist, a function that takes Type parameters or returns Type cannot end up in the object file. But for CTFE, the compiler can pretend that Type is just a type like Object.

[…]

Note that, in my understanding of “first-class,” this isn’t actually first-class types. You still have transformations. Type is a first-class type, and Type objects are first-class objects – at least in CTFE –, but types (as per D grammar) are not first-class things.

If you had first-class types, you could do something like:

Type[] sort(Type[] types) { … }
auto Integers = sort(int, long, short);
Integers[0] little;
Integers[1] middle;
Integers[2] large;

I guess parsing any expression as a type is out of reach and thus first-class types are as well.

July 21, 2023

On Friday, 21 July 2023 at 08:52:47 UTC, Quirin Schroll wrote:

>

On Thursday, 20 July 2023 at 13:44:15 UTC, Commander Zot wrote:

>

wouldn't it be possible to have something like first class types (at compile time) to replace tamplates for type logic with regular functions executed at CTFE?

Of course, that’s possible. You’d have a type called Type that represents types. Of course, Type does not actually exist, a function that takes Type parameters or returns Type cannot end up in the object file. But for CTFE, the compiler can pretend that Type is just a type like Object.

In my imagination, you’d have a transformation from “actual” types (as per D’s grammar) to Type objects. For built-in types, object.d could provide predefined variables, like Int for int: Int is an object of type Type that represents int. It could be a built-in pseudo-template type!T() that returns the Type for T or maybe the transformation can be applied even implicitly; object.d would contain enum Int = type!int; and friends.

The Type objects can be manipulated with regular functions and function templates. At CTFE, Type is just a type.

we already have TypeInfo, which should be used for this too. i'd just add a alias type_t or something.

>

To get “actual” types back from a Type object at compile time, you need another mechanism, ideally I’d say, use mixin.

Instead of int x; you could mixin(Int) x;.

I don't see how that would be useful. it's like restricting an int parameter to specific values. it can be done in the function body.

>

D’s templates are expressive enough to implement every function you could use Type, so it’s technically redundant, and not everything that can be expressed using templates can (or should) be done by (CTFE-only) functions with Type; essentially every non-alias template is an example. On the other hand, in the current state, every algorithm that could be applied to types (like sorting a bunch of types) must be implemented in templates because the value algorithm cannot be used. That I call redundancy.

As a rule of thumb, if you want to manipulate types like a puppet master, you’d use a Type function; if you’re interested in using the types, e.g. handling objects that have that type, you’d use good old templates.

excatly. and i'm really tired of writing recursive functions for those things.

July 21, 2023

On Friday, 21 July 2023 at 09:03:15 UTC, Quirin Schroll wrote:

>

On Friday, 21 July 2023 at 08:52:47 UTC, Quirin Schroll wrote:

>

On Thursday, 20 July 2023 at 13:44:15 UTC, Commander Zot wrote:

>

wouldn't it be possible to have something like first class types (at compile time) to replace tamplates for type logic with regular functions executed at CTFE?

Of course, that’s possible. You’d have a type called Type that represents types. Of course, Type does not actually exist, a function that takes Type parameters or returns Type cannot end up in the object file. But for CTFE, the compiler can pretend that Type is just a type like Object.

[…]

Note that, in my understanding of “first-class,” this isn’t actually first-class types. You still have transformations. Type is a first-class type, and Type objects are first-class objects – at least in CTFE –, but types (as per D grammar) are not first-class things.

If you had first-class types, you could do something like:

Type[] sort(Type[] types) { … }
auto Integers = sort(int, long, short);
Integers[0] little;
Integers[1] middle;
Integers[2] large;

I guess parsing any expression as a type is out of reach and thus first-class types are as well.

that's why i thought first-class only at compiletime.
in your example 'auto Integers' would be a runtime variable, and while this could be made to work, i don't think it's that useful.

 Type[] sort(Type[] types...) { … }
 alias Integers = sort(int, long, short);
 Integers[0] little;
 Integers[1] middle;
 Integers[2] large;

this should work. however. assigning a Type to an alias would have to make it a real type again. assigning a Type[] to an alias should make it an AliasSeq of types.

July 21, 2023

On Thursday, 20 July 2023 at 21:09:40 UTC, Paul Backus wrote:

>

On Thursday, 20 July 2023 at 17:52:38 UTC, TheGag96 wrote:

>

On Thursday, 20 July 2023 at 16:57:16 UTC, jmh530 wrote:

>

You mean dropping it was a mistake? Or you mean type functions are a mistake? (if so, can you explain more what those languages had an issue with)

Sorry, I was probably unclear - not going the type functions route was a mistake.

The thing is, none of the existing template stuff is ever going away. So the choice is not really between "templates" and "first-class types", it's between "templates" and "both".

I agree that, in retrospect, relying on templates for metaprogramming was probably a mistake. But at this point, there's nothing we can do to fix it short of starting a new language from scratch--which is not something the D leadership has any interest in.

but without having them, we still have to write recursive template metaprogramming for things that would be expressed better with normal functions.
yes, it wouldn't remove old code, but it would be a huge win for any new code in my opinion.
and I remember walter mention CTFE for calculations as a huge win, so i'm really not sure if this wouldn't be either. which is the point why I bring up the topic.

July 21, 2023

On Friday, 21 July 2023 at 11:53:23 UTC, Commander Zot wrote:

>

On Thursday, 20 July 2023 at 21:09:40 UTC, Paul Backus wrote:

>

On Thursday, 20 July 2023 at 17:52:38 UTC, TheGag96 wrote:

>

On Thursday, 20 July 2023 at 16:57:16 UTC, jmh530 wrote:

>

You mean dropping it was a mistake? Or you mean type functions are a mistake? (if so, can you explain more what those languages had an issue with)

Sorry, I was probably unclear - not going the type functions route was a mistake.

The thing is, none of the existing template stuff is ever going away. So the choice is not really between "templates" and "first-class types", it's between "templates" and "both".

I agree that, in retrospect, relying on templates for metaprogramming was probably a mistake. But at this point, there's nothing we can do to fix it short of starting a new language from scratch--which is not something the D leadership has any interest in.

but without having them, we still have to write recursive template metaprogramming for things that would be expressed better with normal functions.
yes, it wouldn't remove old code, but it would be a huge win for any new code in my opinion.
and I remember walter mention CTFE for calculations as a huge win, so i'm really not sure if this wouldn't be either. which is the point why I bring up the topic.

I can share the latest state of type-functions before I abandoned the approach.
I believe this is close to what I ended up wtih:
https://github.com/dlang/dmd/compare/master...UplinkCoder:dmd:talias_master
as you can see it's quite a bit of code and it leads to strange interactions with the type system.
At the time I thought that's inherent and cannot be fixed, and indeed if you want to keep the same syntax that you are used to using in templates, this may very well be the case.
However if you are willing to accept changes to your code, it might be possible to do something like this.

July 21, 2023

On Friday, 21 July 2023 at 12:14:01 UTC, Stefan Koch wrote:

>

On Friday, 21 July 2023 at 11:53:23 UTC, Commander Zot wrote:

>

On Thursday, 20 July 2023 at 21:09:40 UTC, Paul Backus wrote:

>

On Thursday, 20 July 2023 at 17:52:38 UTC, TheGag96 wrote:

>

On Thursday, 20 July 2023 at 16:57:16 UTC, jmh530 wrote:

>

You mean dropping it was a mistake? Or you mean type functions are a mistake? (if so, can you explain more what those languages had an issue with)

Sorry, I was probably unclear - not going the type functions route was a mistake.

The thing is, none of the existing template stuff is ever going away. So the choice is not really between "templates" and "first-class types", it's between "templates" and "both".

I agree that, in retrospect, relying on templates for metaprogramming was probably a mistake. But at this point, there's nothing we can do to fix it short of starting a new language from scratch--which is not something the D leadership has any interest in.

but without having them, we still have to write recursive template metaprogramming for things that would be expressed better with normal functions.
yes, it wouldn't remove old code, but it would be a huge win for any new code in my opinion.
and I remember walter mention CTFE for calculations as a huge win, so i'm really not sure if this wouldn't be either. which is the point why I bring up the topic.

I can share the latest state of type-functions before I abandoned the approach.
I believe this is close to what I ended up wtih:
https://github.com/dlang/dmd/compare/master...UplinkCoder:dmd:talias_master
as you can see it's quite a bit of code and it leads to strange interactions with the type system.
At the time I thought that's inherent and cannot be fixed, and indeed if you want to keep the same syntax that you are used to using in templates, this may very well be the case.
However if you are willing to accept changes to your code, it might be possible to do something like this.

thx. but as i understand it, this is quite different then what i proposed, right?
my proposal doesn't implement 'type functions' as a special kind of function, it just uses TypeInfo as a parameter and return type.

so my idea would be to allow any type to implicitly convert to it's TypeInfo when a type is used in a expression.
and any TypeInfo to be converted back to it's type when assigned to an alias.
TypeInfo[] should be converted back to an AliasSeq of those types.

July 21, 2023

On Friday, 21 July 2023 at 12:42:06 UTC, Commander Zot wrote:

>

thx. but as i understand it, this is quite different then what i proposed, right?
my proposal doesn't implement 'type functions' as a special kind of function, it just uses TypeInfo as a parameter and return type.

so my idea would be to allow any type to implicitly convert to it's TypeInfo when a type is used in a expression.
and any TypeInfo to be converted back to it's type when assigned to an alias.
TypeInfo[] should be converted back to an AliasSeq of those types.

In order to convert TypeInfo[] into real types you need to build special code into the compiler.
The information contained in TypeInfo[] is not by itself enough to create a type.
All of the transformation on types or TypeInfo need to be threaded through the compiler.

July 21, 2023

On Friday, 21 July 2023 at 12:46:12 UTC, Stefan Koch wrote:

>

On Friday, 21 July 2023 at 12:42:06 UTC, Commander Zot wrote:

>

thx. but as i understand it, this is quite different then what i proposed, right?
my proposal doesn't implement 'type functions' as a special kind of function, it just uses TypeInfo as a parameter and return type.

so my idea would be to allow any type to implicitly convert to it's TypeInfo when a type is used in a expression.
and any TypeInfo to be converted back to it's type when assigned to an alias.
TypeInfo[] should be converted back to an AliasSeq of those types.

In order to convert TypeInfo[] into real types you need to build special code into the compiler.
The information contained in TypeInfo[] is not by itself enough to create a type.
All of the transformation on types or TypeInfo need to be threaded through the compiler.

yes, you need a TypeInfo like type_t in my example.

for reference here's how my example currently works with TypeInfo[]:

struct Foo {
    struct Bar {
    }
}

struct type_t {
private:
    size_t _size;
    string _ts;
public:
    size_t sizeof_() {
        return _size;
    }
}

auto t(T)() {
    import std.traits : fullyQualifiedName;
    enum tis = fullyQualifiedName!T;
    return type_t(T.sizeof, tis);
}

template r(type_t xt) {
    mixin("alias r = " ~ xt._ts ~";");
}
template r(type_t[] xts) {
    import std.algorithm;
    import std.array;
    import std.meta;
    mixin("alias r = AliasSeq!(" ~ xts.map!(t=>t._ts).array.join(",") ~");");
}

// use normal functions for type logic instead of template magic (basically a CTFE equivalant for type logic)
auto both(type_t a, type_t b) {
    return [a,b];
}

void main() {
    enum XT = t!(Foo.Bar);
    alias T = r!(XT);
    pragma(msg, T);

    alias ST = r!(both(t!short, t!int));
    pragma(msg, ST);
}

this is already working in current D. it's just annoying that you have to write t! and r! everywhere.

the idea is to lower

alias T = foo(short, int);

into some sort of

alias T = type_from_typeid(foo(typeid(short), typeid(int)));
July 21, 2023

On Friday, 21 July 2023 at 11:53:23 UTC, Commander Zot wrote:

>

but without having them, we still have to write recursive template metaprogramming for things that would be expressed better with normal functions.
yes, it wouldn't remove old code, but it would be a huge win for any new code in my opinion.

Following this design philosophy is how you end up with C++.

What happens if you want to use "old code" and "new code" in the same project? Not only do you have to deal with both approaches, you have to write extra glue code to make them work together.

July 21, 2023

On Friday, 21 July 2023 at 13:51:55 UTC, Paul Backus wrote:

>

On Friday, 21 July 2023 at 11:53:23 UTC, Commander Zot wrote:

>

but without having them, we still have to write recursive template metaprogramming for things that would be expressed better with normal functions.
yes, it wouldn't remove old code, but it would be a huge win for any new code in my opinion.

Following this design philosophy is how you end up with C++.

What happens if you want to use "old code" and "new code" in the same project? Not only do you have to deal with both approaches, you have to write extra glue code to make them work together.

I'm sorry, but I don't see how you'd have to write any glue code. in fact, they work together perfectly fine in my example code. but maybe I'm missing something, could you write an example where the two would actually clash?

and also: you could say the same thing about CTFE (for value types) vs templates to do metaprogramming, yet no one complains about that, quite the opposite.

I'm also not suggesting adding type functions or new types to D, but automatically lowering the use of a type in an expression into typeid(type), and if a TypeInfo is assigned to an alias, turn it into it's type again. as I've demonstrated you can do this per hand with templates in existing D code without any problems (except I couldn't get it to work with the builtin TypeInfo, so I've created type_t instead).