www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - return type and templates

reply "Andrea Fontana" <nospam example.com> writes:
I've seen many different topic about this, but they don't explain 
what's wrong with this "proposed" feature. Who can explain me why 
this can't be added to language? Does it broke something?

// Trivial example:

struct Test
{
	 property
	auto value(T)() if (is(T == int)) { return _intValue; }

	 property
	void value(T)(T val) if (is(T == int)) { _intValue = val; }

	private int _intValue;
}

void main()
{
	Test s;
	s.value!int(3); // Works

	s.value(3); // Works, magic
	s.value = 3; // Works, magic

	int t1 = s.value!int; // Works
	auto t2 = s.value!int; // Works

	auto t4 = s.value; // Doesn't work (and i don't think it should)
	int t3 = s.value; // <--Doesn't work (can this feature be 
implemented?)
	
}
Nov 22 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 10:24:38 Andrea Fontana wrote:
 I've seen many different topic about this, but they don't explain
 what's wrong with this "proposed" feature. Who can explain me why
 this can't be added to language? Does it broke something?
 
 // Trivial example:
 
 struct Test
 {
 	 property
 	auto value(T)() if (is(T == int)) { return _intValue; }
 
 	 property
 	void value(T)(T val) if (is(T == int)) { _intValue = val; }
 
 	private int _intValue;
 }
 	auto t4 = s.value; // Doesn't work (and i don't think it should)
 	int t3 = s.value; // <--Doesn't work (can this feature be
 implemented?)
And how would it know what type value should be instantiated with. It only knows in the case of s.value(7) because you gave it a value from which the compiler was able to infer the type. You didn't tell it anything in the case of s.value And it's not like the compiler can look at the template constraint and guess what will work or not - especially when template constraints can be arbitrarily complex. As far as the compiler is concerned, it's just a boolean expression which determines whether a given template instantiation is valid or not. At most, it's used for overloading when there are multiple templates which would otherwise match the given arguments. It's not going to work for the compiler to figure out what types might work with a given template constraint and then have it pick one when you don't tell the template what type to be instantiated with. - Jonathan M Davis
Nov 22 2013
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 It's not going to work for the compiler to figure out what
 types might work with a given template constraint and then
 have it pick one when you don't tell the template what
 type to be instantiated with.
It could work if the type system become more powerful, but what are the costs in compiler complexity, compilation times, and possible bugs in user code? Bye, bearophile
Nov 22 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 11:24:30 bearophile wrote:
 Jonathan M Davis:
 It's not going to work for the compiler to figure out what
 types might work with a given template constraint and then
 have it pick one when you don't tell the template what
 type to be instantiated with.
It could work if the type system become more powerful, but what are the costs in compiler complexity, compilation times, and possible bugs in user code?
What's it going to do? Try ever type that it knows about and see which happens to work? Try every type that it sees in the template constraint (particularly those in is expressions) and see if any of them work? It's a feature which sounds like you're trying to write AI. I don't think that even makes sense to attempt it. If there's really a type that makes sense by default, then just give a default template argument. Why try and make the compiler more complicated, particularly when it's questionable that it's a solvable problem, and it's pretty much a guarantee that it would have a high efficiency cost even if you could pull it off. - Jonathan M Davis
Nov 22 2013
parent reply "Andrea Fontana" <nospam example.com> writes:
On Friday, 22 November 2013 at 10:34:12 UTC, Jonathan M Davis 
wrote:
 On Friday, November 22, 2013 11:24:30 bearophile wrote:
 Jonathan M Davis:
 It's not going to work for the compiler to figure out what
 types might work with a given template constraint and then
 have it pick one when you don't tell the template what
 type to be instantiated with.
It could work if the type system become more powerful, but what are the costs in compiler complexity, compilation times, and possible bugs in user code?
What's it going to do? Try ever type that it knows about and see which happens to work? Try every type that it sees in the template constraint (particularly those in is expressions) and see if any of them work? It's a feature which sounds like you're trying to write AI. I don't think that even makes sense to attempt it. If there's really a type that makes sense by default, then just give a default template argument. Why try and make the compiler more complicated, particularly when it's questionable that it's a solvable problem, and it's pretty much a guarantee that it would have a high efficiency cost even if you could pull it off. - Jonathan M Davis
I just mean: int t = s.value; // Means int t = s.value!int; If there's a problem with template instantiatio is the same we have now. Now I have to write: int t = s.value!int; so if there's a problem with !int, it's just like now. It's just a syntactic sugar, no new feature... Am I wrong?
Nov 22 2013
next sibling parent "Andrea Fontana" <nospam example.com> writes:
On Friday, 22 November 2013 at 10:50:58 UTC, Andrea Fontana wrote:
 On Friday, 22 November 2013 at 10:34:12 UTC, Jonathan M Davis 
 wrote:
 On Friday, November 22, 2013 11:24:30 bearophile wrote:
 Jonathan M Davis:
 It's not going to work for the compiler to figure out what
 types might work with a given template constraint and then
 have it pick one when you don't tell the template what
 type to be instantiated with.
It could work if the type system become more powerful, but what are the costs in compiler complexity, compilation times, and possible bugs in user code?
What's it going to do? Try ever type that it knows about and see which happens to work? Try every type that it sees in the template constraint (particularly those in is expressions) and see if any of them work? It's a feature which sounds like you're trying to write AI. I don't think that even makes sense to attempt it. If there's really a type that makes sense by default, then just give a default template argument. Why try and make the compiler more complicated, particularly when it's questionable that it's a solvable problem, and it's pretty much a guarantee that it would have a high efficiency cost even if you could pull it off. - Jonathan M Davis
I just mean: int t = s.value; // Means int t = s.value!int; If there's a problem with template instantiatio is the same we have now. Now I have to write: int t = s.value!int; so if there's a problem with !int, it's just like now. It's just a syntactic sugar, no new feature... Am I wrong?
Maybe it could be extended to function call if there's no ambiguities. something like; void test(int a, long b); test(s.value, t.value); => test(s.value!int, t.value!long); just if test has no abiguities with overload or template params. In this case we can throw an exception and template param must be explicit.
Nov 22 2013
prev sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 11:50:57 Andrea Fontana wrote:
 I just mean:
 
 int t = s.value;  // Means  int t = s.value!int;
 
 If there's a problem with template instantiatio is the same we
 have now.
 Now I have to write:
 
 int t = s.value!int;
 
 so if there's a problem with !int, it's just like now.
 
 It's just a syntactic sugar, no new feature... Am I wrong?
Again, how is the compiler supposed to have any clue that you want to instantiate value with int in the case of int t = s.value; The left-hand side of the expression has no impact on the type of the right- hand side, and you have not given the compiler any information as to what template argument should be given to value. s.value(3) only works because you've given value a function argument from which the corresponding template argument can be inforred. With s.value, you've given no indication whatsoever as to what value should be instantiated with. If you want a default template argument, then give it one. e.g. property auto value(T = int)() if (is(T == int)) { return _intValue; } But I don't know how you expect the compiler to have any clue what type value should be instantiated with when you haven't given it any template arguments and there are no function arguments to infer the template arguments from - especially when this what the compiler really sees template value(T) if(is(T == int)) { property auto value() { return _intValue; } } and it doesn't even look at the template constraint, let alone the contents of the template, until you attempt to instantiate the template. And it's not going to be able to try and instantiate the template without having any template arguments. - Jonathan M Davis
Nov 22 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/22/2013 01:29 PM, Jonathan M Davis wrote:
 On Friday, November 22, 2013 11:50:57 Andrea Fontana wrote:
 I just mean:

 int t = s.value;  // Means  int t = s.value!int;

 If there's a problem with template instantiatio is the same we
 have now.
 Now I have to write:

 int t = s.value!int;

 so if there's a problem with !int, it's just like now.

 It's just a syntactic sugar, no new feature... Am I wrong?
Again, how is the compiler supposed to have any clue that you want to instantiate value with int in the case of int t = s.value; The left-hand side of the expression has no impact on the type of the right- hand side,
If you mean the type of the variable declaration, then yes it does. int delegate(int) dg1 = x=>x; float delegate(float) dg2 = x=>x; static assert(!is(typeof(x=>x)));
 and you have not given the compiler any information as to what
 template argument should be given to value.
Well, technically, in this case there is only one choice.
 s.value(3) only works because
 you've given value a function argument from which the corresponding template
 argument can be inforred. With s.value, you've given no indication whatsoever
 as to what value should be instantiated with.

 If you want a default template argument, then give it one. e.g.

  property auto value(T = int)() if (is(T == int)) { return _intValue; }

 But I don't know how you expect the compiler to have any clue what type value
 should be instantiated with when you haven't given it any template arguments
 and there are no function arguments to infer the template arguments from -
 especially when this what the compiler really sees

 template value(T)
      if(is(T == int))
 {
       property auto value() { return _intValue; }
 }

 and it doesn't even look at the template constraint, let alone the contents of
 the template, until you attempt to instantiate the template. And it's not
 going to be able to try and instantiate the template without having any
 template arguments.
 ...
The request would be reasonable if 'value' was declared as follows though: property T value(T)() if (is(T == int)) { return _intValue; } i.e. the fact that the template argument equals the type of the resulting call can be read off directly from the signature. This is in the same ballpark as the existing IFTI features.
Nov 22 2013
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 14:29:46 Timon Gehr wrote:
 The request would be reasonable if 'value' was declared as follows though:
 
  property T value(T)() if (is(T == int)) { return _intValue; }
 
 i.e. the fact that the template argument equals the type of the
 resulting call can be read off directly from the signature. This is in
 the same ballpark as the existing IFTI features.
How so? IFTI works by inferring the template arguments from the function arguments. In this case, for the compiler to figure out a type that it could use to instantiate the template, it would have to disect the template constraint, which is compeletly different. Yes, this particular template constraint is very simplistic, but the compiler doesn't even look at the template constraint until it has a type to test with it, and in most cases, it would have no way of inferring what types might work even if it did look. Trying to get the compiler to infer T for value would be a drastic change to how it deals with templates, and at best, it would be able to figure out what to do in only the most simplistic of cases. In fact, the only cases that it could figure it out would very simplistic cases where there was only one possible answer, and if there's only one type that will work with a template, then there really wasn't much point in templatizing it in the first place. And as soon as there are multiple types which could work with a template, the compiler couldn't figure out the correct type no matter how smart it was, because it would need a way of choosing which of the options to take. e.g. template foo(T) if(is(T == int) || is(T == byte)) { ... } Should foo be instantiated with int or byte? Both are equally valid. The OP has come up with a contrived example where it seems obvious to him what type the compiler should use to instantiate a template which has been given no template arguments and no function arguments to infer the template arguments from. But the compiler has no plumbing for figuring out such a thing, and adding such plumbing would be pointless, because it would only work in contrived cases such as this one where there was no point in templatizing the function in the first place. - Jonathan M Davis
Nov 22 2013
next sibling parent "Andrea Fontana" <nospam example.com> writes:
On Friday, 22 November 2013 at 13:43:59 UTC, Jonathan M Davis 
wrote:
 On Friday, November 22, 2013 14:29:46 Timon Gehr wrote:
 The request would be reasonable if 'value' was declared as 
 follows though:
 
  property T value(T)() if (is(T == int)) { return _intValue; }
 
 i.e. the fact that the template argument equals the type of the
 resulting call can be read off directly from the signature. 
 This is in
 the same ballpark as the existing IFTI features.
How so? IFTI works by inferring the template arguments from the function arguments. In this case, for the compiler to figure out a type that it could use to instantiate the template, it would have to disect the template constraint, which is compeletly different. Yes, this particular template constraint is very simplistic, but the compiler doesn't even look at the template constraint until it has a type to test with it, and in most cases, it would have no way of inferring what types might work even if it did look. Trying to get the compiler to infer T for value would be a drastic change to how it deals with templates, and at best, it would be able to figure out what to do in only the most simplistic of cases. In fact, the only cases that it could figure it out would very simplistic cases where there was only one possible answer, and if there's only one type that will work with a template, then there really wasn't much point in templatizing it in the first place. And as soon as there are multiple types which could work with a template, the compiler couldn't figure out the correct type no matter how smart it was, because it would need a way of choosing which of the options to take. e.g. template foo(T) if(is(T == int) || is(T == byte)) { ... } Should foo be instantiated with int or byte? Both are equally valid. The OP has come up with a contrived example where it seems obvious to him what type the compiler should use to instantiate a template which has been given no template arguments and no function arguments to infer the template arguments from. But the compiler has no plumbing for figuring out such a thing, and adding such plumbing would be pointless, because it would only work in contrived cases such as this one where there was no point in templatizing the function in the first place. - Jonathan M Davis
Timon was right, I mean T as return type. Too bad it needs a drastic change :\
Nov 22 2013
prev sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 11/22/2013 02:43 PM, Jonathan M Davis wrote:
 On Friday, November 22, 2013 14:29:46 Timon Gehr wrote:
The request would be reasonable if 'value' was declared as follows though:

 property T value(T)() if (is(T == int)) { return _intValue; }

i.e. the fact that the template argument equals the type of the
resulting call can be read off directly from the signature. This is in
the same ballpark as the existing IFTI features.
How so? IFTI works by inferring the template arguments from the function arguments. In this case, for the compiler to figure out a type that it could use to instantiate the template, it would have to disect the template constraint, which is compeletly different. Yes, this particular template constraint ...
I am not talking about the template constraint at all. The following would still be a signature that would work: property T value(T)(){ return _intValue; }
Nov 22 2013
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 14:29:46 Timon Gehr wrote:
 On 11/22/2013 01:29 PM, Jonathan M Davis wrote:
 On Friday, November 22, 2013 11:50:57 Andrea Fontana wrote:
 I just mean:
 
 int t = s.value;  // Means  int t = s.value!int;
 
 If there's a problem with template instantiatio is the same we
 have now.
 Now I have to write:
 
 int t = s.value!int;
 
 so if there's a problem with !int, it's just like now.
 
 It's just a syntactic sugar, no new feature... Am I wrong?
Again, how is the compiler supposed to have any clue that you want to instantiate value with int in the case of int t = s.value; The left-hand side of the expression has no impact on the type of the right- hand side,
If you mean the type of the variable declaration, then yes it does. int delegate(int) dg1 = x=>x; float delegate(float) dg2 = x=>x; static assert(!is(typeof(x=>x)));
There are a few cases where the compiler does that but not many. In general, the right-hand side of an assignment is evaluated separately from the left and thus gets no type information from the left-hand side. But even if it did, in this case, that would mean determining the template argument from the return type, which is completely backwards to how template instantiation works, and attempting that would be a lot like attempting to overload on the return type of a function, which completely goes against how C-based languages work. - Jonathan M Davis
Nov 22 2013
prev sibling parent reply "Andrea Fontana" <nospam example.com> writes:
On Friday, 22 November 2013 at 12:29:25 UTC, Jonathan M Davis 
wrote:
 On Friday, November 22, 2013 11:50:57 Andrea Fontana wrote:
 I just mean:
 
 int t = s.value;  // Means  int t = s.value!int;
 
 If there's a problem with template instantiatio is the same we
 have now.
 Now I have to write:
 
 int t = s.value!int;
 
 so if there's a problem with !int, it's just like now.
 
 It's just a syntactic sugar, no new feature... Am I wrong?
Again, how is the compiler supposed to have any clue that you want to instantiate value with int in the case of int t = s.value; The left-hand side of the expression has no impact on the type of the right- hand side, and you have not given the compiler any information as to what template argument should be given to value. s.value(3) only works because you've given value a function argument from which the corresponding template argument can be inforred. With s.value, you've given no indication whatsoever as to what value should be instantiated with. If you want a default template argument, then give it one. e.g. property auto value(T = int)() if (is(T == int)) { return _intValue; } But I don't know how you expect the compiler to have any clue what type value should be instantiated with when you haven't given it any template arguments and there are no function arguments to infer the template arguments from - especially when this what the compiler really sees template value(T) if(is(T == int)) { property auto value() { return _intValue; } } and it doesn't even look at the template constraint, let alone the contents of the template, until you attempt to instantiate the template. And it's not going to be able to try and instantiate the template without having any template arguments. - Jonathan M Davis
I don't know how compiler works internally. (is there any documentation other than the comments and code itself?) So probably I'm wrong about what compiler knows and not. Parsing this: int t = s.value; I assumed that it knows - when is trying to instatiate s.value template - that "s.value" is part of an assignment and that it will be assigned to an int. So if template argument is missed and s.value returns T, T should be int. But if I understand your answer, right-hand side can't see left-hand side. By the way the default value doesn't works for me because in my library I have to choose from many different template. So i have to do every time: int i = asd.value!int; string s = asd.value!string; long l = asd.value!long; and so on... and i hoped I could do: int i = asd.value; string s = asd.value; long l = asd.value; Ok, if it's impossible, never mind :)
Nov 22 2013
parent reply "Dicebot" <public dicebot.lv> writes:
On Friday, 22 November 2013 at 13:43:49 UTC, Andrea Fontana wrote:
 I assumed that it knows - when is trying to instatiate s.value 
 template - that "s.value" is part of an assignment and that it 
 will be assigned to an int.
This is somewhat wrong part. "s.value" is distinct separate expression that must be evaluated by compiler on its own before proceeding. The fact that it is later used in assignment expression is not know at that moment. One can define some analysis rules that will make it do so but it is a very major change to compiler internals.
Nov 22 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/22/2013 02:50 PM, Dicebot wrote:
 On Friday, 22 November 2013 at 13:43:49 UTC, Andrea Fontana wrote:
 I assumed that it knows - when is trying to instatiate s.value
 template - that "s.value" is part of an assignment and that it will be
 assigned to an int.
This is somewhat wrong part. "s.value" is distinct separate expression that must be evaluated by compiler on its own before proceeding. The fact that it is later used in assignment expression is not know at that moment.
Lambda parameter type deduction needs to know this too.
 One can define some analysis rules that will make it do so but
 it is a very major change to compiler internals.
Do you know the relevant compiler internals? I cannot really imagine it being a major change. (It wouldn't be in the D frontend I am currently developing as a side project.)
Nov 22 2013
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 15:20:30 Timon Gehr wrote:
 On 11/22/2013 02:50 PM, Dicebot wrote:
 On Friday, 22 November 2013 at 13:43:49 UTC, Andrea Fontana wrote:
 I assumed that it knows - when is trying to instatiate s.value
 template - that "s.value" is part of an assignment and that it will be
 assigned to an int.
This is somewhat wrong part. "s.value" is distinct separate expression that must be evaluated by compiler on its own before proceeding. The fact that it is later used in assignment expression is not know at that moment.
Lambda parameter type deduction needs to know this too.
I believe that the only cases where the compiler uses the left-hand side of of an assignment or initialization to determine anything about the type of the right-hand side is when the right-hand side is a literal (be it a lambda literal, array literal, or some other kind of literal). - Jonathan M Davis
Nov 22 2013
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 11/22/2013 04:14 PM, Jonathan M Davis wrote:
 On Friday, November 22, 2013 15:20:30 Timon Gehr wrote:
 On 11/22/2013 02:50 PM, Dicebot wrote:
 On Friday, 22 November 2013 at 13:43:49 UTC, Andrea Fontana wrote:
 I assumed that it knows - when is trying to instatiate s.value
 template - that "s.value" is part of an assignment and that it will be
 assigned to an int.
This is somewhat wrong part. "s.value" is distinct separate expression that must be evaluated by compiler on its own before proceeding. The fact that it is later used in assignment expression is not know at that moment.
Lambda parameter type deduction needs to know this too.
I believe that the only cases where the compiler uses the left-hand side of of an assignment or initialization to determine anything about the type of the right-hand side is when the right-hand side is a literal (be it a lambda literal, array literal, or some other kind of literal). - Jonathan M Davis
int delegate(int) dg = b?x=>x:x=>2*x;
Nov 22 2013
parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, November 22, 2013 16:21:43 Timon Gehr wrote:
 On 11/22/2013 04:14 PM, Jonathan M Davis wrote:
 On Friday, November 22, 2013 15:20:30 Timon Gehr wrote:
 On 11/22/2013 02:50 PM, Dicebot wrote:
 On Friday, 22 November 2013 at 13:43:49 UTC, Andrea Fontana wrote:
 I assumed that it knows - when is trying to instatiate s.value
 template - that "s.value" is part of an assignment and that it will be
 assigned to an int.
This is somewhat wrong part. "s.value" is distinct separate expression that must be evaluated by compiler on its own before proceeding. The fact that it is later used in assignment expression is not know at that moment.
Lambda parameter type deduction needs to know this too.
I believe that the only cases where the compiler uses the left-hand side of of an assignment or initialization to determine anything about the type of the right-hand side is when the right-hand side is a literal (be it a lambda literal, array literal, or some other kind of literal). - Jonathan M Davis
int delegate(int) dg = b?x=>x:x=>2*x;
Yeah. The result of the right-hand side is a lambda literal. - Jonathan M Davis
Nov 22 2013
prev sibling parent =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 11/22/2013 07:14 AM, Jonathan M Davis wrote:

 I believe that the only cases where the compiler uses the left-hand 
side of of
 an assignment or initialization to determine anything about the type 
of the
 right-hand side is when the right-hand side is a literal (be it a lambda
 literal, array literal, or some other kind of literal).
Any implicit conversion needs that too, e.g. alias this. struct S { double d; alias d this; } void main() { auto s = S(); double d = s; } The example will be more impressive when multiple 'alias this' is supported. Ali
Nov 22 2013