October 01, 2020
On 10/1/20 1:55 PM, Stefan Koch wrote:
> On Thursday, 1 October 2020 at 17:50:39 UTC, Andrei Alexandrescu wrote:
>> On 10/1/20 1:45 PM, Stefan Koch wrote:
>>> On Thursday, 1 October 2020 at 17:35:51 UTC, Andrei Alexandrescu wrote:
>>>> On 10/1/20 1:26 PM, Steven Schveighoffer wrote:
>>>>> [...]
>>>>
>>>> How do you implement Variant.get(T) without reifying is(T : U)?
>>>
>>> What does it do?
>>> is Variant.get returning type T ?
>>
>> Yes: https://dlang.org/phobos/std_variant.html#.VariantN.get
>>
>>> Then I would assume you don't.
>>> You use a template + typeid that's what they are for!
>>>
>>> Variant.get is not doing any type computation which you would need is (T : U) for?
>>> Where would I use it?
>>
>> Everywhere Variant.get is called. The decision is(T : U) needs to be carried, except that one of the types is available only at runtime.
> 
> Please show me in the code where is(T : U) is used.
> I don't find it in std variant.

It's in the use of ImplicitConversionTargets in std.traits. If I remember correctly that template was created exactly to help with implementing Variant. It does an incomplete and in places incorrect job at figuring out what implicit conversions would work. That information is saved as part of the pointer to function stored in the Variant object, and used to figure out if the call to get() is valid.
October 01, 2020
On Thursday, 1 October 2020 at 18:08:11 UTC, Steven Schveighoffer wrote:
> On 10/1/20 1:35 PM, Andrei Alexandrescu wrote:
> ...
>
> I don't see why Variant's needs have to dictate how you need to reason about types at compile time in CTFE.

Yes.  If we want to talk about how we might eliminate the distinction between run-time and compile-time a separate thread would be useful.  I'd sure follow it!

October 01, 2020
On Thursday, 1 October 2020 at 18:10:05 UTC, Andrei Alexandrescu wrote:
> On 10/1/20 1:55 PM, Stefan Koch wrote:
>> On Thursday, 1 October 2020 at 17:50:39 UTC, Andrei Alexandrescu wrote:
>>> On 10/1/20 1:45 PM, Stefan Koch wrote:
>>>> On Thursday, 1 October 2020 at 17:35:51 UTC, Andrei Alexandrescu wrote:
>>>>> On 10/1/20 1:26 PM, Steven Schveighoffer wrote:
>>>>>> [...]
>>>>>
>>>>> How do you implement Variant.get(T) without reifying is(T : U)?
>>>>
>>>> What does it do?
>>>> is Variant.get returning type T ?
>>>
>>> Yes: https://dlang.org/phobos/std_variant.html#.VariantN.get
>>>
>>>> Then I would assume you don't.
>>>> You use a template + typeid that's what they are for!
>>>>
>>>> Variant.get is not doing any type computation which you would need is (T : U) for?
>>>> Where would I use it?
>>>
>>> Everywhere Variant.get is called. The decision is(T : U) needs to be carried, except that one of the types is available only at runtime.
>> 
>> Please show me in the code where is(T : U) is used.
>> I don't find it in std variant.
>
> It's in the use of ImplicitConversionTargets in std.traits. If I remember correctly that template was created exactly to help with implementing Variant. It does an incomplete and in places incorrect job at figuring out what implicit conversions would work. That information is saved as part of the pointer to function stored in the Variant object, and used to figure out if the call to get() is valid.

So if your question is could you implement 'ImplicitConversionTargets' as a type function the answer is most likely yes.
I haven't looked at the code too deeply.
But it should work in principle if it doesn't it's an implementation bug.
As such this 'ImplicitConversionTargets' is indeed a good use-case to look at.
October 01, 2020
On Thu, Oct 01, 2020 at 05:55:17PM +0000, Stefan Koch via Digitalmars-d wrote: [...]
> Please show me in the code where is(T : U) is used.
> I don't find it in std variant.

That's because it doesn't work.

We would like to be able to do this:

	Variant v;
	v = 100; // v now stores an int
	long l = v.get!long; // currently does not work

We cannot do this because the type of 100 is not known by Variant until runtime, and at runtime, we cannot tell whether the stored type (int) is implicitly convertible to the requested type (long).  So, we cannot perform the implicit conversion.

This limitation makes Variant very unfriendly to use: unless you know precisely what type is stored, you cannot get anything useful out of it. If somebody stored an int in a Variant and hands it to you, and you want a long (after all, the language supports implicit conversion from int to long), you cannot get a long out of it without knowing beforehand it was an int. In practice, this means lots of code duplication: if v.get!long throws, try v.get!int; if that still doesn't do it, try v.get!short; etc.. Basically you end up duplicating implicit conversions that the compiler should have done for you.

We would like Variant to behave like a top type, but we can't because we cannot duplicate the implicit conversions of stored types. We can get most of the way there, but not all the way.


T

-- 
"Maybe" is a strange word.  When mom or dad says it it means "yes", but when my big brothers say it it means "no"! -- PJ jr.
October 01, 2020
On 10/1/20 2:10 PM, Andrei Alexandrescu wrote:
> On 10/1/20 1:55 PM, Stefan Koch wrote:
>> Please show me in the code where is(T : U) is used.
>> I don't find it in std variant.
> 
> It's in the use of ImplicitConversionTargets in std.traits.

The only use of is(T:U) in that trait is:

is(T : typeof(null))

-Steve
October 01, 2020
On Thu, Oct 01, 2020 at 02:08:11PM -0400, Steven Schveighoffer via Digitalmars-d wrote:
> On 10/1/20 1:35 PM, Andrei Alexandrescu wrote:
[...]
> > How do you implement Variant.get(T) without reifying is(T : U)?
> 
> How do you implement Variant.get(T) *with* reifying is(U : T)?
> 
> step 1: determine using reified constructs that U is convertible to T
> step 2: ?????
> step 3: Return a T from a U!
> 
> BTW, Variant already does a *limited* form of this. reification doesn't change what needs to happen.
> 
> I don't see why Variant's needs have to dictate how you need to reason about types at compile time in CTFE.
[...]

I think what Andrei is getting at is, we want to be able to transfer the compiler's knowledge and implementation of implicit type conversions to runtime.  I.e., we'd like to do this:

	Variant v;
	v = 100;	// v now stores an int
	...
	long l = v.get!long; // currently doesn't work

Or maybe, a more motivating example:

	T calculate(T)(Variant number) {
		T val = number.get!T;
		return val*2 + 1;
	}

	Variant v;
	v = 100;
	assert(calculate!long(v) == 201L);
	assert(calculate!float(v) == 201.0f);

I.e., you don't know what type you'd like to get from the Variant until the caller calls you, but you also don't know what type is stored in Variant until runtime.  Currently, there's a hack in Variant to handle some of these implicit conversions, but as Andrei says, it's incomplete and may have some wrong cases.  This knowledge is already in the compiler; there should be a way to transfer this to Variant at runtime without having to re-implement implicit conversions in library code.


T

-- 
A mathematician is a device for turning coffee into theorems. -- P. Erdos
October 01, 2020
On 10/1/20 2:19 PM, H. S. Teoh wrote:
> On Thu, Oct 01, 2020 at 05:55:17PM +0000, Stefan Koch via Digitalmars-d wrote:
> [...]
>> Please show me in the code where is(T : U) is used.
>> I don't find it in std variant.
> 
> That's because it doesn't work.
> 
> We would like to be able to do this:
> 
> 	Variant v;
> 	v = 100; // v now stores an int
> 	long l = v.get!long; // currently does not work

I'm not going to respond to anything else here, but just want to point out, this does work.

https://run.dlang.io/is/S3sqDF

-Steve
October 01, 2020
On 10/1/20 2:08 PM, Steven Schveighoffer wrote:
> On 10/1/20 1:35 PM, Andrei Alexandrescu wrote:
>> On 10/1/20 1:26 PM, Steven Schveighoffer wrote:
>>> But when the compiler MUST implement is(T : U), and that expression is well defined, I don't see why we have to reimplement that feature in the runtime as well. At least without a compelling example of "the compiler can't do this as well".
>>
>> How do you implement Variant.get(T) without reifying is(T : U)?
> 
> How do you implement Variant.get(T) *with* reifying is(U : T)?
> 
> step 1: determine using reified constructs that U is convertible to T
> step 2: ?????
> step 3: Return a T from a U!

The mechanics of carrying the conversion are also needed, especially if a change in format is necessary (e.g. int to long or float to double). It looks like a pointer to function needs to be part of the solution.

Nevertheless, the challenge remains - need to list the possible types a given type could convert to. Or in some way or another figure out whether T converts to U, when only one of them is available during compilation.

> BTW, Variant already does a *limited* form of this. reification doesn't change what needs to happen.
> 
> I don't see why Variant's needs have to dictate how you need to reason about types at compile time in CTFE.

The more applicability to difficult problems a mechanism has, the more useful it is. Looking good on cherry-picked examples is not enough. Difficult related problems that we have are introspection, std.meta, std.traits, and std.variant. The latter can be considered a problem of having the right primitives in std.traits.
October 01, 2020
On 10/1/20 2:26 PM, Steven Schveighoffer wrote:
> On 10/1/20 2:10 PM, Andrei Alexandrescu wrote:
>> On 10/1/20 1:55 PM, Stefan Koch wrote:
>>> Please show me in the code where is(T : U) is used.
>>> I don't find it in std variant.
>>
>> It's in the use of ImplicitConversionTargets in std.traits.
> 
> The only use of is(T:U) in that trait is:
> 
> is(T : typeof(null))

Of course. That's the point - the template is not using "is", because it can't: it only has one type available. It "implements" a subset of it. See what I'm saying? ImplicitConversionTargets helps Variant implement a kinda sorta "is" at runtime.

October 01, 2020
On 10/1/20 2:19 PM, H. S. Teoh wrote:
> On Thu, Oct 01, 2020 at 05:55:17PM +0000, Stefan Koch via Digitalmars-d wrote:
> [...]
>> Please show me in the code where is(T : U) is used.
>> I don't find it in std variant.
> 
> That's because it doesn't work.
> 
> We would like to be able to do this:
> 
> 	Variant v;
> 	v = 100; // v now stores an int
> 	long l = v.get!long; // currently does not work

It actually should. ImplicitConversionTargets does cover numerics pretty well.