September 24, 2020
On 24.09.20 07:13, Andrei Alexandrescu wrote:
> 
> A few comments:
> 
> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.

https://issues.dlang.org/show_bug.cgi?id=9945
September 24, 2020
On 9/23/20 11:51 PM, Paul Backus wrote:
> On Thursday, 24 September 2020 at 03:17:44 UTC, Steven Schveighoffer wrote:
>> The recursive version is not nice code. It's hard to understand and difficult to follow. It's main advantage is that because it's essentially a functional language, and all templates are cached, in certain cases, you can see a performance gain because the compiler can avoid actually redoing what it did. In practice, for things like staticMap, this doesn't ever pan out.
>>
>> I don't see how you solve the tail recursion thing anyway -- each template instance cannot rely on the work that has already been done, because of static if.
>>
>> The static map type function is a loop over an array. Easy to understand, easy to write. It's actually boring code. Static map shouldn't be interesting code at all.
>>
> 
> I feel the same way about the naive recursive version:
> 
> template staticMap!(alias F, Args...) {
>      static if (Args.length == 0)
>          alias staticMap = AliasSeq!();
>      else
>          alias staticMap = AliasSeq!(F!(Args[0]), staticMap!(F, Args[1 .. $]));
> }
> 
> One base case, one recursive case, one line for each. The code practically writes itself. How could it be any clearer or more obvious?

It fails to explicitly say what the resulting size of the AliasSeq will be, unlike the type function code (result.length = types.length)

The transformation call to F is buried in the logic to construct the result. Compare to:

result[i] = F(t);

One has to mentally work through how the recursion relationship works to ensure it terminates properly, I have to read every line to see how the thing plays out. Vs. reading each line of the typefunction code and deciding "yes, this does what it's supposed to".

Yes, the code writes itself. Reading and understanding it isn't as straightforward.

BTW, I don't see how tail-recursion can be done still. Even with your tail-recursive version, you still have to rebuild each instance of the template, because you don't know if it's going to generate different code via static if. Whereas runtime tail recursion builds one version of the function, and just jumps back into the same function with modified input.

> Of course, for someone who lacks that background, it might very well look bewildering--in the same way that, say, the Visitor pattern might look bewildering to someone unfamiliar with the idioms of OOP. Does that mean it's a bad pattern? Or does it just mean it's a pattern they haven't learned yet?

Not bewildering, but requires more thought to understand. The array version requires little thought, almost every line is independent, and does not require the context of the whole construct to verify what it does or that it is correct.

It also helps that the "simple naive" version of the typefunction code is the best version. You don't have to write it in a special way to make the compiler perform well.

One of the reasons D's code generation capabilities are so approachable vs. C++ is because it does not need to be written in functional style. You can write code that does it exactly how you would do it if you were doing it by hand.

-Steve
September 24, 2020
On 9/24/20 6:12 AM, Timon Gehr wrote:
> On 24.09.20 07:13, Andrei Alexandrescu wrote:
>>
>> A few comments:
>>
>> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.
> 
> https://issues.dlang.org/show_bug.cgi?id=9945

Hm... so I'm guessing TypeInfo.Type or __traits(typeFromId) would only work for CTFE?

That sounds interesting. Could be a different way to implement type functions, without requiring a new mechanism of passing type data. And there is the potential (I think) of making functions that are only type functions for CTFE, and can do something different if used at runtime (via __ctfe).

-Steve
September 24, 2020
On Thursday, 24 September 2020 at 12:39:57 UTC, Steven Schveighoffer wrote:
> BTW, I don't see how tail-recursion can be done still. Even with your tail-recursive version, you still have to rebuild each instance of the template, because you don't know if it's going to generate different code via static if. Whereas runtime tail recursion builds one version of the function, and just jumps back into the same function with modified input.

You have to build N versions, but (in principle) you don't have to keep them all around in memory--only the last one is necessary. So you still save on resources compared to naive template recursion, although not as much as you would with type functions.

> It also helps that the "simple naive" version of the typefunction code is the best version. You don't have to write it in a special way to make the compiler perform well.

Definitely a valid point. Regardless of one's aesthetic preferences, recursive templates are not a "pit of success."
September 24, 2020
On 9/24/20 8:58 AM, Steven Schveighoffer wrote:
> On 9/24/20 6:12 AM, Timon Gehr wrote:
>> On 24.09.20 07:13, Andrei Alexandrescu wrote:
>>>
>>> A few comments:
>>>
>>> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.
>>
>> https://issues.dlang.org/show_bug.cgi?id=9945
> 
> Hm... so I'm guessing TypeInfo.Type or __traits(typeFromId) would only work for CTFE?

Affirmative - wouldn't make sense otherwise as you could create (or receive) such an object dynamically.

> That sounds interesting. Could be a different way to implement type functions, without requiring a new mechanism of passing type data. And there is the potential (I think) of making functions that are only type functions for CTFE, and can do something different if used at runtime (via __ctfe).

<nod>

One nice thing it would do is unify CTTI with RTTI. There's a lot of unused potential in the run-time use of TypeInfo that I'll discuss at a later time.

September 24, 2020
On 9/24/20 6:12 AM, Timon Gehr wrote:
> On 24.09.20 07:13, Andrei Alexandrescu wrote:
>>
>> A few comments:
>>
>> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.
> 
> https://issues.dlang.org/show_bug.cgi?id=9945

Great, thanks!
September 24, 2020
On 9/24/20 2:49 AM, Stefan Koch wrote:
> On Thursday, 24 September 2020 at 05:13:49 UTC, Andrei Alexandrescu wrote:
>>
>> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.
> 
> This would make typeid polymorphic in other words a template.
> 
>> * In the current implementation values returned by typeid() cannot be read during compilation.
> 
> Wrong. parts of typeid can be read by ctfe.
> I extended this functionality myself.

Great. Can you please elaborate a little? What bits work and what bits don't? Thanks!
September 24, 2020
On 9/24/20 1:51 AM, H. S. Teoh wrote:
> On Thu, Sep 24, 2020 at 01:13:49AM -0400, Andrei Alexandrescu via Digitalmars-d wrote:
> [...]
>> Was looking at this example thinking, if only we had an object
>> available for each type. And then it struck me - we already do. It's
>> typeid. For each type T, typeid(T) yields an object (of type class
>> TypeInfo) that can be copied, compared etc.
> 
> +1.  I've mentioned this before in one of Stefan's threads.  Basically,
> to promote types to first-class citizen status, all we need to do is to
> treat typeid specially at compile-time.  At compile-time, typeid becomes
> something like a glorified alias to the type itself, such that it can be
> manipulated, passed around, interrogated, etc..  At runtime, typeid
> becomes the familiar RTTI object.

Indeed you did! https://forum.dlang.org/post/mailman.5271.1597852264.31109.digitalmars-d@puremagic.com

> [...]
>> * This could work with no change to the language definition. All
>> that's needed to get lift is make TypeInfo values usable during
>> compilation.
> 
> Or treat typeid specially at compile-time, as a compile-time object not
> necessarily tied to TypeInfo (but "collapses" into TypeInfo at runtime).

Looks like we have something here!
September 24, 2020
On Thursday, 24 September 2020 at 14:07:17 UTC, Andrei Alexandrescu wrote:
> On 9/24/20 2:49 AM, Stefan Koch wrote:
>> On Thursday, 24 September 2020 at 05:13:49 UTC, Andrei Alexandrescu wrote:
>>>
>>> * Currently mixin closes the circle by taking back the typeid to the type it started from. It would be nice to have something better, e.g. t.Type would just be the type.
>> 
>> This would make typeid polymorphic in other words a template.
>> 
>>> * In the current implementation values returned by typeid() cannot be read during compilation.
>> 
>> Wrong. parts of typeid can be read by ctfe.
>> I extended this functionality myself.
>
> Great. Can you please elaborate a little? What bits work and what bits don't? Thanks!

classinfo.name works.
As well as equality between typeid objects.

to make everything work at ctfe you would have to duplicate the whole typeinfo generation code. At least naively that's what you would have to do.
September 24, 2020
On Thursday, 24 September 2020 at 14:25:37 UTC, Stefan Koch wrote:
>
> to make everything of typeid work at ctfe you would have to duplicate the whole typeinfo generation code. At least naively that's what you would have to do.

Let me show you the code that does only typeid.name

        else if (ex.op == TOK.typeid_)
        {
            if (v.ident == Identifier.idPool("name"))
            {
                if (auto t = isType(ex.isTypeidExp().obj))
                {
                    auto sym = t.toDsymbol(null);
                    if (auto ident = (sym ? sym.ident : null))
                    {
                        result = new StringExp(e.loc, ident.toString());
                        result.expressionSemantic(null);
                        return ;
                    }
                }
            }
            return notImplementedYet();
        }

You would need at least 5-10 lines for every member of typeinfo.