July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
> [..] Perhaps Andrei could tell us what that quote means. [..]
No need for that. I'll explain what that quote means.
Here's that quote again:
"However, having the language attempt combinatorially at the same time implicit conversions and type deduction is a dicey proposition in the general case, so D does not attempt to do all that."
Here's a simpler paraphrasing of that quote:
"D doesn't attempt to do implicit conversion and type deduction at the same time."
In this context, "type deduction" means the process of trying to figure out what kind of a signature a function template should instantiate to, in other words, what the template parameters of the instantiated function should be.
For example, given a function template:
void foo(T)(Array!T a) { }
...and a call which forces that function template to instantiate:
Array!int arr;
foo(arr); // OK
Type deduction is able to figure out that T must be int, and instantiates the function template to:
void foo(Array!int a) { }
But, if you try to pass to foo a variable of some user defined type MyType which merely implicitly converts Array!int, then the type deduction fails, because "D doesn't attempt to do implicit conversion and type deduction at the same time".
MyType mt;
foo(mt); // Error
Now, we can't test this because D doesn't have an implicit conversion operator (like C++ does), but it could have it in the future, and that's not really even relevant.
|
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote:
>
> Yes, you are right in that the quote from TDPL refers to that specific function signature and is saying that the base type of T[] and T must match. But more generally, that quote is also saying that D doesn't try to do implicit conversion and type deduction at the same time for type-parameterized runtime arguments. In other words, the quote is saying: the type of the argument you pass to a templated function must match exactly to the templated type that the function template is expecting for that particular argument. That is: no implicit conversion happens to your variable before it is passed to the function as a runtime argument whose type is parameterized by the function.
>
>
You heavily misunderstoond the topic. The issue here is that in
T[] find(T)(T[] haystack, T needle)
type of neendle should correspond to base type of haystack. If there is a contradiction ("at the same time implicit conversions and type deduction"), dmd issues error. But here
void foo(T)(T[] slice) { }
base type of slice is not tied to any other parameters. Actually if you pass integer static array, dmd deduces T to be int, then template instantiation part is done. After that static array is casted to slice as what happens with non-template function (internally dmd treats it like foo(cast(int[])arr)). Other your comments are based on this misunderstanding.
|
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote: >> >> Yes, you are right in that the quote from TDPL refers to that specific function signature and is saying that the base type of T[] and T must match. But more generally, that quote is also saying that D doesn't try to do implicit conversion and type deduction at the same time for type-parameterized runtime arguments. In other words, the quote is saying: the type of the argument you pass to a templated function must match exactly to the templated type that the function template is expecting for that particular argument. That is: no implicit conversion happens to your variable before it is passed to the function as a runtime argument whose type is parameterized by the function. >> >> > > You heavily misunderstoond the topic. No I didn't. > The issue here is that in > > T[] find(T)(T[] haystack, T needle) > > type of neendle should correspond to base type of haystack. If there is a contradiction ("at the same time implicit conversions and type deduction"), dmd issues error. Yes I understand this perfectly. No problem. This works exactly like it does in C++ and I know C++. Trust me. > Actually if you pass integer static array, dmd deduces T to be int, [..] And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? DMD does the implicit conversion int[10] -> int[] while it is doing type deduction, which according to TDPL shouldn't happen. |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: > On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote: >> On Thursday, 4 July 2013 at 09:24:53 UTC, TommiT wrote: >>> >>> Yes, you are right in that the quote from TDPL refers to that specific function signature and is saying that the base type of T[] and T must match. But more generally, that quote is also saying that D doesn't try to do implicit conversion and type deduction at the same time for type-parameterized runtime arguments. In other words, the quote is saying: the type of the argument you pass to a templated function must match exactly to the templated type that the function template is expecting for that particular argument. That is: no implicit conversion happens to your variable before it is passed to the function as a runtime argument whose type is parameterized by the function. >>> >>> >> >> You heavily misunderstoond the topic. > > No I didn't. There should be some conversion about how to make judgments based on arguments, otherwise loop of "No ..." can be indefinitely long. You argue that in code void foo(T)(T[] array) {} if static array is passed, there is violation of one quote from TDPL. You are wrong, because: 1) You incorrectly applied quote regarding code void foo(T)(T[] ar, T t){} to a different situation. In this case t is tied to some type, in case we discussed, it doesn't. 2) In case of one parameter, a variable isn't tied to any type and usual implicit conversions are applied. >> The issue here is that in >> >> T[] find(T)(T[] haystack, T needle) >> >> type of neendle should correspond to base type of haystack. If there is a contradiction ("at the same time implicit conversions and type deduction"), dmd issues error. > > Yes I understand this perfectly. No problem. This works exactly like it does in C++ and I know C++. Trust me. Style in which you are arguing is an argument to do not. >> Actually if you pass integer static array, dmd deduces T to be int, [..] > > And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union? > DMD does the implicit conversion int[10] -> int[] while it is doing type deduction, which according to TDPL shouldn't happen. This is flawed since you can: int[10] arr; foo!int(arr). Here there is no deduction puzzle, as parameter was passed explicitly and usual conversion was applied as in case of non-template function. And if you argue, that this should not happen, than you argue to make explicit "!int" typing which is absolutely redundant in this case. |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: > [..] > 2) In case of one parameter, a variable isn't tied to any type and usual implicit conversions are applied. Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword '@implicit'), then the following would compile? struct Wrap(T) { T t; } struct Toy { alias Wrapped = Wrap!Toy; @implicit Wrapped opCast(T : Wrapped)() { return Wrapped.init; } } void foo(T)(Wrap!T) { } void main() { foo(Toy.init); } On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: >> On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote: >>> Actually if you pass integer static array, dmd deduces T to be int, [..] >> >> And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? > > DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union? So you admit that DMD does implicit conversion during type deduction? On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: >> DMD does the implicit conversion int[10] -> int[] while it is doing type deduction, which according to TDPL shouldn't happen. > > This is flawed since you can: > > int[10] arr; > foo!int(arr). What exactly in what I said there is flawed? Your example foo!int(arr) has nothing to do with what I said, because there's no type deduction in your example. On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: > [..] And if you argue, that this should not happen, than you argue to make explicit "!int" typing which is absolutely redundant in this case. What on earth made you think that I would argue for such idiocy. |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote:
>
> struct Toy
> {
> alias Wrapped = Wrap!Toy;
>
> @implicit Wrapped opCast(T : Wrapped)()
> {
> return Wrapped.init;
> }
> }
Let me clarify that arguably silly looking syntax. The @implicit opCast operator simply means that any instance of Toy is implicitly convertible to an instance of Wrapped!Toy, exactly like any instance of type int[10] is implicitly convertible to an instance of type int[].
|
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote: > On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: >> [..] >> 2) In case of one parameter, a variable isn't tied to any type and usual implicit conversions are applied. > > Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword '@implicit'), then the following would compile? > > struct Wrap(T) > { > T t; > } > > struct Toy > { > alias Wrapped = Wrap!Toy; > > @implicit Wrapped opCast(T : Wrapped)() > { > return Wrapped.init; > } > } > > void foo(T)(Wrap!T) { } > > void main() > { > foo(Toy.init); > } > Your implicit syntax is redundant. What C++ does is irrelevant. D has alias this. import std.stdio; struct Wrap(T) { T t; } struct Toy { alias get this; Wrap!Toy get() { return Wrap!Toy.init; } } void foo(T)(Wrap!T t) { writeln(T.stringof); } void main() { foo(Toy.init); } Note, that T is Toy, so there were no type conversion during template instantiation. There was argument conversion after instantiation, as it happens usually. Back to foo function accepting slice - dmd does the same thing. > On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: >> On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: >>> On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote: >>>> Actually if you pass integer static array, dmd deduces T to be int, [..] >>> >>> And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? >> >> DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union? > > So you admit that DMD does implicit conversion during type deduction? See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int? > On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: >> On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: >>> DMD does the implicit conversion int[10] -> int[] while it is doing type deduction, which according to TDPL shouldn't happen. >> >> This is flawed since you can: >> >> int[10] arr; >> foo!int(arr). > > What exactly in what I said there is flawed? Your example foo!int(arr) has nothing to do with what I said, because there's no type deduction in your example. > Example shows that implicit conversion on argument in this case has nothing with type deduction (there is no type deduction, yet argument was converted). |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On Thursday, 4 July 2013 at 18:07:00 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 15:59:21 UTC, TommiT wrote: >> On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: >>> [..] >>> 2) In case of one parameter, a variable isn't tied to any type and usual implicit conversions are applied. >> >> Do you mean that if D had the C++ style implicit conversion operator (using let's say the keyword '@implicit'), then the following would compile? >> >> struct Wrap(T) >> { >> T t; >> } >> >> struct Toy >> { >> alias Wrapped = Wrap!Toy; >> >> @implicit Wrapped opCast(T : Wrapped)() >> { >> return Wrapped.init; >> } >> } >> >> void foo(T)(Wrap!T) { } >> >> void main() >> { >> foo(Toy.init); >> } >> > > Your implicit syntax is redundant. What C++ does is irrelevant. D has alias this. My implicit cast operator syntax is not redundant because it's not the same thing as your alias this example. My example would not and should not ever compile. The difference between alias this relationship and a mere implicit conversion operator is that alias this creates an is-a relationship, whereas implicit conversion operator does not. On Thursday, 4 July 2013 at 18:07:00 UTC, Maxim Fomin wrote: > import std.stdio; > > struct Wrap(T) > { > T t; > } > > struct Toy > { > alias get this; > > Wrap!Toy get() > { > return Wrap!Toy.init; > } > } > > void foo(T)(Wrap!T t) > { > writeln(T.stringof); > } > > void main() > { > foo(Toy.init); > } > > Note, that T is Toy, so there were no type conversion during template instantiation. There was argument conversion after instantiation, as it happens usually. Back to foo function accepting slice - dmd does the same thing. DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double. >> On Thursday, 4 July 2013 at 15:03:54 UTC, Maxim Fomin wrote: >>> On Thursday, 4 July 2013 at 13:55:17 UTC, TommiT wrote: >>>> On Thursday, 4 July 2013 at 13:45:07 UTC, Maxim Fomin wrote: >>>>> Actually if you pass integer static array, dmd deduces T to be int, [..] >>>> >>>> And that right there, "dmd deduces T to be int", is the crux of the matter. How on earth is DMD able to deduce T to be int, without using the implicit conversion from int[10] to int[] ? >>> >>> DMD is stupid, but not that. If it has T[] parameter, and int[10] static array which is convertible to int[] is passed, T is deduced to be int. What other types T can be? A float, object, or pointer to union? >> >> So you admit that DMD does implicit conversion during type deduction? > > See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int? Here's what the compiler does during type deduction when it sees the following function template and its instantiation: void foo(T)(T[] da) { } int[10] sa; foo(sa); Step 1: The compiler tries to figure out the type T such as the parameter da would have the same type as the argument sa which was passed to the function. The complier comes to the conclusion that there is not type which would make the type of da the same as the type of sa. For example, if T were int, then the type of da would be int[] which is not the same as the type of sa which is int[10]. Step 2: The compiler tries to see if the type of sa could be implicitly converted to something. The compiler realises that sa could be converted to an instance of type int[]. Then the compiler checks if this implicitly converted type could be passed to foo. The compiler figures out that if T is int, then the implicitly converted type int[] matches exactly with the type of the parameter da. Thus, the compiler declares that T must be int. Step 3: If the compiler hasn't managed to figure out what T is, it gives an error. If it has found out what T is, the compiler declares that the type deduction has been successfully accomplished. To answer your question "What implicit conversion did dmd do in the case of int[10] and int[]", see Step 2. At Step 2 the compiler does, or rather checks that it's possible to do, the implicit conversion from int[10] to int[]. That Step 2 right there is what no-one coming from C++ would expect to happen, and according to that quote from TDPL, that Step 2 should not happen in D. |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to TommiT | On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote: >> Note, that T is Toy, so there were no type conversion during template instantiation. There was argument conversion after instantiation, as it happens usually. Back to foo function accepting slice - dmd does the same thing. > > DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double. The idea that situation with alias this and arrays is functionally different is simply defeacted by static assert (is(int[1] : int[])); static assert (is(Toy : Wrap!Toy)); static assert (!is(int[1] == int[])); static assert (!is(Toy == Wrap!Toy)); >> See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int? > > Here's what the compiler does during type deduction when it sees the following function template and its instantiation: > > void foo(T)(T[] da) { } > > int[10] sa; > foo(sa); > > <steps follow ...> This is nice and interesting to read. Please provide references to dmd code which support the description of dmd compiling process you provided (judging by how confident in this topic you are, you might have studied cast.c, expression.c and template.c wery well). Without such references any text pretending to tell something about what compiler does is cheap. |
July 04, 2013 Re: Function templates do implicit conversions for their arguments | ||||
---|---|---|---|---|
| ||||
Posted in reply to Maxim Fomin | On Thursday, 4 July 2013 at 19:49:59 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote: >>> Note, that T is Toy, so there were no type conversion during template instantiation. There was argument conversion after instantiation, as it happens usually. Back to foo function accepting slice - dmd does the same thing. >> >> DMD doesn't do the same thing for static arrays. Due to alias this, your Toy is a Wrap!Toy for all intents and purposes. There's no is-a relationship between a static array type and the dynamic array type which it implicitly converts to. What happens when static array implicitly converts to dynamic array is the same type of implicit conversion which happens when long converts to double. There's no is-a relationship between long and double. > > The idea that situation with alias this and arrays is functionally different is simply defeacted by > > static assert (is(int[1] : int[])); > static assert (is(Toy : Wrap!Toy)); > static assert (!is(int[1] == int[])); > static assert (!is(Toy == Wrap!Toy)); Is-a relationship between types means this type of thing: class Human { } class Baby : Human { } void main() { static assert(!is(Baby == Human)); } Baby is-a Human, but the type Baby is not same as Human. If static arrays used an alias this to convert to a dynamic array, then you'd able to append to a static array: struct DynamicArray(T) { void opOpAssign(string op : "+")(int n) { } } struct StaticArray(T, int size) { alias get this; DynamicArray!T get() @property { return DynamicArray!T.init; } } void main() { StaticArray!(int, 4) sa; sa += 42; // Can't do this with static arrays } On Thursday, 4 July 2013 at 19:49:59 UTC, Maxim Fomin wrote: > On Thursday, 4 July 2013 at 19:00:51 UTC, TommiT wrote: >>> See above. What type implicit converision did dmd in case of int[10] and int[]. Conversion from int to int? >> >> Here's what the compiler does during type deduction when it sees the following function template and its instantiation: >> >> void foo(T)(T[] da) { } >> >> int[10] sa; >> foo(sa); >> >> <steps follow ...> > > This is nice and interesting to read. Please provide references to dmd code which support the description of dmd compiling process you provided (judging by how confident in this topic you are, you might have studied cast.c, expression.c and template.c wery well). Without such references any text pretending to tell something about what compiler does is cheap. I have never seen any DMD code. My deduction of how DMD _must_ work is based on: 1) my understanding of how template instantiation works in C++ 2) the fact that the call to foo(sa) does compile The compiler simply must know about implicit conversion of static to dynamic arrays during template instantiation, or otherwise it wouldn't be able to do the instantiation of foo for the call to foo(sa). And not only that, the compiler must accept a non-exact match between parameter and argument types. C++ never accepts anything but an exact match between the parameter types of the instantiated template function and the types of the arguments passed in to the function at the call site which caused the instantiation. That's pretty simple logic, which is why I'm confident that I'm right even though, like I said, I don't know anything about how DMD is written. |
Copyright © 1999-2021 by the D Language Foundation