www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: First class types (at compiletime)

reply Commander Zot <no no.no> writes:
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?

the idea is to make this work
```
type_t min(type_t a, type_t b){ return a.sizeof<=b.sizeof?a:b; }
alias r = min(short, int);

```
basically an equivalent to
```
int min(int a, int b){return a<=b?a:b;}
enum r = min(2, 4);
```

here's a working example how it's kind of possible in D atm, but 
lacks the syntax sugar:


```
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 ~";");
}

// use normal functions for type logic instead of template magic 
(basically a CTFE equivalant for type logic)
auto min_type(type_t a, type_t b) {
     return a.sizeof_ <= b.sizeof_ ? a : b;
}

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

     alias ST = r!(min_type(t!short, t!int));
     pragma(msg, ST);
}
```
Jul 20 2023
next sibling parent reply "H. S. Teoh" <hsteoh qfbox.info> writes:
On Thu, Jul 20, 2023 at 01:44:15PM +0000, Commander Zot via Digitalmars-d 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?
 
 the idea is to make this work
 ```
 type_t min(type_t a, type_t b){ return a.sizeof<=b.sizeof?a:b; }
 alias r = min(short, int);
 
 ```
[...] What are you trying to accomplish that needs to do this? If you describe your use case, maybe we can better understand why you can't just use a simple template: template Min(T, U) { static if (T.sizeof <= U.sizeof) alias Min = T; else alias Min = U; } alias V = Min!(int, short); static assert(is(V == short)); T -- Some ideas are so stupid that only intellectuals could believe them. -- George Orwell
Jul 20 2023
parent reply Commander Zot <no no.no> writes:
On Thursday, 20 July 2023 at 14:46:49 UTC, H. S. Teoh wrote:
 On Thu, Jul 20, 2023 at 01:44:15PM +0000, Commander Zot via 
 Digitalmars-d 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?
 
 the idea is to make this work
 ```
 type_t min(type_t a, type_t b){ return a.sizeof<=b.sizeof?a:b; 
 }
 alias r = min(short, int);
 
 ```
[...] What are you trying to accomplish that needs to do this? If you describe your use case, maybe we can better understand why you can't just use a simple template: template Min(T, U) { static if (T.sizeof <= U.sizeof) alias Min = T; else alias Min = U; } alias V = Min!(int, short); static assert(is(V == short)); T
yes, you could write it that way, but the same is true for value-based calculations. CTFE allows us to write normal functions instead of templates for value logic at compiletime, we're just lacking first class type support to also use them for type logic compiletime. currently we have runtime functions in std.algorithm and the same in std.meta for types for example. it would be nice if we could do ``` import std.algorithm; alias TS = (int, short, byte).filter!(t=>t.sizeof>1).aliasseq; ``` and it wouldn't need too much changes i think (but i might be wrong there): - make type parameters and return types implicitly convert to TypeInfo - make typeinfo/typeid work properly in CTFE - make an asignment to an alias convert a TypeInfo into the corresponding type at compiletime. - add some special functions to TypeInfo, like comparison, sizeof, ...
Jul 20 2023
parent reply TheGag96 <thegag96 gmail.com> writes:
On Thursday, 20 July 2023 at 15:11:39 UTC, Commander Zot wrote:
 and it wouldn't need too much changes i think (but i might be 
 wrong there):
Unfortunately, I think that may be the case. Work has actually been done to explore in this in the past by Stefan Koch under the name "type functions": https://forum.dlang.org/post/swuxfggqgibqbxoaiheg forum.dlang.org He had a WIP implementation and everything. However, it wasn't met with much reception from the higher-ups and was more or less dropped, I think. Languages like Jai and Zig it seems have shown this as largely been a mistake... D's compile-time capabilities are super cool, but the C++ carryover of having basically a separate language of sorts by way of templates to operate on types is slow to compile and hokey compared to just... writing a function. Honestly, the point where we started having to do recursion to do list operations on `AliasSeq`s should have clued us in that we should have been thinking about type functions.
Jul 20 2023
parent reply jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 20 July 2023 at 15:46:26 UTC, TheGag96 wrote:
 On Thursday, 20 July 2023 at 15:11:39 UTC, Commander Zot wrote:
 and it wouldn't need too much changes i think (but i might be 
 wrong there):
Unfortunately, I think that may be the case. Work has actually been done to explore in this in the past by Stefan Koch under the name "type functions": https://forum.dlang.org/post/swuxfggqgibqbxoaiheg forum.dlang.org He had a WIP implementation and everything. However, it wasn't met with much reception from the higher-ups and was more or less dropped, I think. Languages like Jai and Zig it seems have shown this as largely been a mistake... [snip]
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)
Jul 20 2023
parent reply TheGag96 <thegag96 gmail.com> writes:
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.
Jul 20 2023
next sibling parent reply jmh530 <john.michael.hall gmail.com> writes:
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.
Thanks. Stefan's work seemed interesting to me at the time, but it didn't seem like something trivial. I'm not sure I recall in the forums Walter or Atila having a strong opinion, but that could just be my recollection. Maybe something for D3?
Jul 20 2023
parent TheGag96 <thegag96 gmail.com> writes:
On Thursday, 20 July 2023 at 18:55:14 UTC, jmh530 wrote:
 Stefan's work seemed interesting to me at the time, but it 
 didn't seem like something trivial. I'm not sure I recall in 
 the forums Walter or Atila having a strong opinion, but that 
 could just be my recollection.

 Maybe something for D3?
Again, my original comment may have been unclear (argh lol). It was indeed very non-trivial, which is the unfortunate part.
Jul 20 2023
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
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.
Jul 20 2023
parent reply Commander Zot <no no.no> writes:
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.
Jul 21 2023
next sibling parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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.
Jul 21 2023
parent reply Commander Zot <no no.no> writes:
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.
Jul 21 2023
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
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.
Jul 21 2023
parent Commander Zot <no no.no> writes:
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[]: ```d 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))); ```
Jul 21 2023
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
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.
Jul 21 2023
parent Commander Zot <no no.no> writes:
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).
Jul 21 2023
prev sibling next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
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. 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;`. 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.
Jul 21 2023
next sibling parent reply Quirin Schroll <qs.il.paperinik gmail.com> writes:
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: ```d 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.
Jul 21 2023
parent Commander Zot <no no.no> writes:
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: ```d 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. ```d 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.
Jul 21 2023
prev sibling parent Commander Zot <no no.no> writes:
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.
Jul 21 2023
prev sibling next sibling parent MrJay <mrjcraft2021 gmail.com> writes:
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?
In some programming languages I have used in the past specifically Lisp when you define a type you can give it a list of predicates to define the type, its mostly the same except slightly fancier in scala and haskell, in D we already have support for predicates they are called contracts, and you can apply type checking using in, out, assert, and invariant, this gets you most of the way there. when you click on references you will also find one mentions theorem proving using contracts, so its been used in that same area before, if you use mixins and anonymous/voldemort types I bet you could create a nicer way of applying these contracts, getting 95% the way there. that last 5 percent would be recursive peano types and verifying coverage etc... which both are still probably possible using structs. https://dlang.org/spec/contracts.html reference I mentioned: https://web.archive.org/web/20080919174640/http://people.cs.uchicago.edu/~robby/contract-reading-list/ I guess the next question is, how far can you take contracts for type level programming, and at that point would first class types be necessary. ``` //basic example auto test(float a) in (a < 255) out(r; r.x > 0) { Struct S { float x; float y; float z; invariant { assert(x < 255 && x > -60); assert(y < 255 && y > -60); assert(z < 255 && z > -60); } } S ret = S(a, 100.0, 100.0); return ret; } ``` I did not check if the code compiled but that is the basic idea, there are also examples on the docs I linked, hopefully this gave you some more stuff you can try out / research, and this is also something I am interested in so I would like to see updates if you come up with anything.
Jul 21 2023
prev sibling parent reply sighoya <sighoya gmail.com> writes:
Nice work.

It seems you want to lower types to values representing types.
The problem is that types in single init a declaration.

I think grammar alone could not solve the idea of type as 
expression and type as declaration on it's own:
```D
int i,short* s;
```

So is short* s an expression type_t!short * type_t!s or a 
declaration?
The former is useless but there probably trickier examples I 
don't know.

Further you still need to write:
```D
type_t min(type_t t1,type_t t2)(){...}
```

otherwise it would be a runtime function which can't do the alias 
seq magic.
Maybe adding syntactic sugar for it could alleviate:
```D
type_t min!(type_t t1,type_t t2){...}
```

but I think it is ambiguous too.

Maybe it would be better to pass strings instead of types and 
retrieve type with its properties out of the string via 
reflection/traits?
Aug 07 2023
parent reply Commander Zot <no no.no> writes:
On Monday, 7 August 2023 at 23:06:56 UTC, sighoya wrote:
 I think grammar alone could not solve the idea of type as 
 expression and type as declaration on it's own:
 ```D
 int i,short* s;
 ```

 So is short* s an expression type_t!short * type_t!s or a 
 declaration?
 The former is useless but there probably trickier examples I 
 don't know.
I could imagine rules where this works, as type_t!short would be known at compiletime, so it could be converted to a type. But I agree, there might be cases where it doesn't work. I don't have enough knowledge of the D compiler to evaluate or implement my Idea.
 Further you still need to write:
 ```D
 type_t min(type_t t1,type_t t2)(){...}
 ```

 otherwise it would be a runtime function which can't do the 
 alias seq magic.
the point of my idea is for it to be a runtime function. only that in some cases it can be run in CTFE and the result can be converted back to a type as we are in compiletime.
 Maybe adding syntactic sugar for it could alleviate:
 ```D
 type_t min!(type_t t1,type_t t2){...}
 ```

 but I think it is ambiguous too.

 Maybe it would be better to pass strings instead of types and 
 retrieve type with its properties out of the string via 
 reflection/traits?
that's basically special type functions again, which just complicate the language, and I don't see much benefit over templates.
Aug 08 2023
parent reply sighoya <sighoya gmail.com> writes:
On Tuesday, 8 August 2023 at 09:27:46 UTC, Commander Zot wrote:
 I could imagine rules where this works, as type_t!short would 
 be known at compiletime, so it could be converted to a type. 
 But I agree, there might be cases where it doesn't work. I 
 don't have enough knowledge of the D compiler to evaluate or 
 implement my Idea.
 that's basically special type functions again, which just 
 complicate the language, and I don't see much benefit over 
 templates.
The more I think about your solution, the more I like it. It would really easy complex type calculations using normal functions, and it's not a change to the type system, just sugar on top of templates. If we could eliminate ambiguities, I would be definitely for it. Though we may need to know more about a type than just the name and the size.
Aug 08 2023
parent reply Stefan Koch <uplink.coder googlemail.com> writes:
On Tuesday, 8 August 2023 at 12:26:06 UTC, sighoya wrote:
 If we could eliminate ambiguities, I would be definitely for 
 it. Though we may need to know more about a type than just the 
 name and the size.
You need the origin so you can compute a mangle. f(t1, t2) needs to yield mangle_of(f) ~ mangle(t1) ~ mangle(t2)
Aug 09 2023
parent Commander Zot <no no.no> writes:
On Thursday, 10 August 2023 at 04:13:57 UTC, Stefan Koch wrote:
 On Tuesday, 8 August 2023 at 12:26:06 UTC, sighoya wrote:
 If we could eliminate ambiguities, I would be definitely for 
 it. Though we may need to know more about a type than just the 
 name and the size.
You need the origin so you can compute a mangle. f(t1, t2) needs to yield mangle_of(f) ~ mangle(t1) ~ mangle(t2)
you need the fully qualified name anyway, or else you run into ambiguities when turning it back into an alias. but i don't understand why you'd need the mangle. your function signature should look like this, so the mangle shouldn't be anything special: ``` struct type_t { string fullyqualifiednameoftype; size_t sizeoftype; } type_t fun(type_t, type_t); ```
Aug 10 2023