September 26, 2020
On 9/26/20 6:00 PM, Steven Schveighoffer wrote:
> On 9/26/20 5:42 PM, Andrei Alexandrescu wrote:
>> On 9/26/20 5:16 PM, Timon Gehr wrote:
>>> On 26.09.20 18:18, Andrei Alexandrescu wrote:
> Such things are not *impossible*, but something like derivative is a great example that is really difficult to generate a runtime function that can do it.

Yah, you'd need i.e. a member function in Id called bool implicitlyConvertible(Id another). The functionality needed is similar to that in Variant, which created the need for implicitConversionTargets (https://dlang.org/library/std/traits/implicit_conversion_targets.html). You'd need to have access to those in the Id space.
September 26, 2020
On 9/26/20 6:03 PM, Andrei Alexandrescu wrote:
> On 9/26/20 6:00 PM, Steven Schveighoffer wrote:
>> On 9/26/20 5:42 PM, Andrei Alexandrescu wrote:
>>> On 9/26/20 5:16 PM, Timon Gehr wrote:
>>>> On 26.09.20 18:18, Andrei Alexandrescu wrote:
>> Such things are not *impossible*, but something like derivative is a great example that is really difficult to generate a runtime function that can do it.
> 
> Yah, you'd need i.e. a member function in Id called bool implicitlyConvertible(Id another). The functionality needed is similar to that in Variant, which created the need for implicitConversionTargets (https://dlang.org/library/std/traits/implicit_conversion_targets.html). You'd need to have access to those in the Id space.

Variant works because it can access the parameter type. If you need to access only one type, and the other information is runtime-accessible, then you can do it. If both need to be runtime-accessible, you are in trouble.

Like try comparing 2 Variants. If Variant doesn't have any idea about how the types might compare ahead of time, it gives up.

-Steve
September 26, 2020
On 9/26/20 6:15 PM, Steven Schveighoffer wrote:
> On 9/26/20 6:03 PM, Andrei Alexandrescu wrote:
>> On 9/26/20 6:00 PM, Steven Schveighoffer wrote:
>>> On 9/26/20 5:42 PM, Andrei Alexandrescu wrote:
>>>> On 9/26/20 5:16 PM, Timon Gehr wrote:
>>>>> On 26.09.20 18:18, Andrei Alexandrescu wrote:
>>> Such things are not *impossible*, but something like derivative is a great example that is really difficult to generate a runtime function that can do it.
>>
>> Yah, you'd need i.e. a member function in Id called bool implicitlyConvertible(Id another). The functionality needed is similar to that in Variant, which created the need for implicitConversionTargets (https://dlang.org/library/std/traits/implicit_conversion_targets.html). You'd need to have access to those in the Id space.
> 
> Variant works because it can access the parameter type. If you need to access only one type, and the other information is runtime-accessible, then you can do it. If both need to be runtime-accessible, you are in trouble.

A possibly angle here is to save that information in runtime format, i.e. reify the inheritance/convertibility relationship. I'm unclear on how to do that efficiently. (The inefficient solution would save the result of ImplicitConversionTargets in a member of Id that has type Id[] and then do searches in it.)

In an ideal world it would be nice if id1 <= id2 iff is(reify!id1 : reify!id2).
September 27, 2020
On Saturday, 26 September 2020 at 21:42:19 UTC, Andrei Alexandrescu wrote:
> On 9/26/20 5:16 PM, Timon Gehr wrote:
>> On 26.09.20 18:18, Andrei Alexandrescu wrote:
>>>
>>> Most implementations are a few lines long because they get to leverage algorithms as implemented in std. Here's a typical one:
>>>
>>> alias MostDerived(Args...) = dereify!({
>>>      auto ids = reify!Args;
>>>      sort!((a, b) => is(dereify!a : dereify!b))(ids);
>>>      return ids;
>>> }());
>> 
>> I am pretty sure this one does not work though.
>
> Oops, Steve just told me it doesn't have any unittests. So it may as well not be working. Research goes on.

This example on the other hand works:

---
/+
import std.array;
import std.range;
import std.algorithm;
doesn't work yet but only because the template connstraints need to be adjusted.
+/

alias alias_array = typeof(((alias T) => __traits(getAttributes, T))(Byte));

auto sort3(alias less, R)(ref /*alias_array*/ R r)
{
    alias tmp;

    if (r.length == 1)
        return r;
    if (r.length == 2)
    {
        if (less(r[1], r[0]))
        {
            tmp = r[0];
            r[0] = r[1];
            r[1] = tmp;
        }
    }
    if (r.length == 3)
    {
        if (less(r[2], r[1]))
        {
            tmp = r[1];
            r[1] = r[2];
            r[2] = tmp;
        }
        if (less(r[1], r[0]))
        {
            tmp = r[0];
            r[0] = r[1];
            r[1] = tmp;
        }
    }
    return r;
}

auto sortBySize(alias_array types ...)
{
    types.sort3!((alias a, alias b) => a.sizeof < b.sizeof);

    return types;
};

alias Int = int;
alias Ushort = ushort; // we need these aliases because the parser won't allow us to call a function with a reserved type identifier.
alias Byte = byte;
pragma(msg, sortBySize(Ushort, Int, Byte)); //Output:  [(byte), (ushort), (int)]

---
September 27, 2020
On Sunday, 27 September 2020 at 00:10:33 UTC, Stefan Koch wrote:
> This example on the other hand works:

Here's another potential implementation:

---
template sortBySize(T...) {
        string code() {
                // step one: gather the data in the template shim
                static struct Item {
                        size_t idx;
                        size_t size;
                }
                Item[] items;
                foreach(idx, t; T)
                        items ~= Item(idx, t.sizeof);

                // step two: process the data with normal CTFE
                import std.algorithm;
                items.sort!((a, b) => a.size < b.size);

                // step three: build the code for the template to bring it back
                // this would be a fine candidate for a helper function
                string code;
                foreach(item; items) {
                        if(code.length) code ~= ", ";
                        code ~= "T[" ~ cast(char)(item.idx + '0') ~ "]";
                }

                return "AliasSeq!(" ~ code ~ ")";
        }

        import std.meta;
        alias sortBySize = mixin(code());
}

pragma(msg, sortBySize!(ushort, int, byte));
---

(byte, ushort, int)



There's several optimizations here, preallocating, factoring out of the template, not using std.algorithm, you know those tricks already.

I'm not in love with it but there is some potential in the concept in today's D.
September 26, 2020
On 9/26/20 6:48 PM, Andrei Alexandrescu wrote:
> A possibly angle here is to save that information in runtime format, i.e. reify the inheritance/convertibility relationship.

thinking it through...

All basic types have well-defined relationships that can be figured out using a big statically generated switch thing.

Enums can be converted to their bases, and whatever they can convert to.

Structs can be converted to their alias-this parameter.

Classes can also have an alias this, AND a set of base types.

Arrays and pointers can convert in certain cases to base types.

And, multiply all this by mutability changes (convert to const for things with references, and convert to anything for value types).

So for each type, you could generate a list of types that it can convert to. Then on each comparison, search for the target type in the list, and if you find it, return true.

I can't imagine this is going to be efficient. We might be able to factor out the const changes. But it still leaves a big list of things. Honestly, I don't know if the compiler does it in a more efficient way than searching through a list.

So it's definitely doable, I just don't know if we would want to do it, or if the complication of the resulting code is worth it. is(a : b) is just soooo much easier! And I think this is one aspect that type functions allow.

If there were a way to go back to type-land in the middle of runtime-at-ctfe-land, even if you had to define it in a restrictive way, then it might be much more palatable.

-Steve
September 26, 2020
On 9/26/20 8:27 PM, Adam D. Ruppe wrote:
> On Sunday, 27 September 2020 at 00:10:33 UTC, Stefan Koch wrote:
>> This example on the other hand works:
> 
> Here's another potential implementation:
> 
> ---
> template sortBySize(T...) {
[snip]

Sorting by inheritance relationship is more difficult. I got this going, alpha quality:

https://gist.github.com/andralex/0d85df38be2d9ffbe89cf1fb51c44213

Now indeed calling std.sort against (reified) types just works.

The point is to reify the bases of the type as well so they can be manipulated as objects. Then defining the comparison relation is easy (didn't bother to make it efficient here - just a linear search).

More introspection-related matters would be reified the same way (store during construction in the reified object, potentially via pointers if they don't apply to all objects). Instance size would be an obvious candidate, prolly I'll put it in there as a demo. More interestingly we'd have things like parameters/return for functions and data members for classes and structs. Long way ahead.

This is really exciting stuff.

September 26, 2020
On 9/26/20 10:30 PM, Steven Schveighoffer wrote:
> On 9/26/20 6:48 PM, Andrei Alexandrescu wrote:
>> A possibly angle here is to save that information in runtime format, i.e. reify the inheritance/convertibility relationship.
> 
> thinking it through...
> 
> All basic types have well-defined relationships that can be figured out using a big statically generated switch thing.
> 
> Enums can be converted to their bases, and whatever they can convert to.

*nod*

> Structs can be converted to their alias-this parameter.

*nod* but how is that detectable for a given type T?

> Classes can also have an alias this, AND a set of base types.

yes... https://gist.github.com/andralex/0d85df38be2d9ffbe89cf1fb51c44213 has the base classes part as a proof of concept (no interfaces and no alias this).

> Arrays and pointers can convert in certain cases to base types.

*nod* these would be detectable with relative eash

> And, multiply all this by mutability changes (convert to const for things with references, and convert to anything for value types).
> 
> So for each type, you could generate a list of types that it can convert to. Then on each comparison, search for the target type in the list, and if you find it, return true.
> 
> I can't imagine this is going to be efficient.

In the words of a guy I heard at a panel: "I'm not sure I understood what the problem is, but it seems the solution is a hashtable". :o)

> We might be able to factor out the const changes. But it still leaves a big list of things. Honestly, I don't know if the compiler does it in a more efficient way than searching through a list.
> 
> So it's definitely doable, I just don't know if we would want to do it, or if the complication of the resulting code is worth it. is(a : b) is just soooo much easier! And I think this is one aspect that type functions allow.

It's part of the introspection game: gather the information typically present in the innards of the compiler, in programmatically-available form. I mean you would expect to grab access to implicit conversion targets in a language that has introspection.

> If there were a way to go back to type-land in the middle of runtime-at-ctfe-land, even if you had to define it in a restrictive way, then it might be much more palatable.

I'd be very interested too! (My experience with reification is now measured in hours.) This is the kind of stuff Timon would know.

September 27, 2020
On Sunday, 27 September 2020 at 02:31:36 UTC, Andrei Alexandrescu wrote:
>
> This is really exciting stuff.

Which I came up with.
September 26, 2020
On 9/26/20 10:48 PM, Stefan Koch wrote:
> On Sunday, 27 September 2020 at 02:31:36 UTC, Andrei Alexandrescu wrote:
>>
>> This is really exciting stuff.
> 
> Which I came up with.

Congratulations!