www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Tuple IFTI with implicit conversions

reply Reiner Pope <some address.com> writes:
(A follow-up from "Any ideas for lazy evaluation on varaible argument 
functions?")

Daniel Keep wrote:
 Sadly, I don't think there's any way to fix this.  The problem is that
 if you've got IFTI (which is what allows you to omit the explicit
 template instantiation), the arguments can't be very complex.  What you
 would need is something like this:
 
 void infoF(A...)(lazy DecayStaticArrays!(A) a) { ... }
 
 Where "DecayStaticArrays" turns all statically-sized arrays into
 dynamically-sized ones: so (char[2u], char[6u]) would become (char[],
 char[]).
This kind of problem with IFTI seems to come up from time to time. I think the general case of this problem is when you want the parameter types[1] to be something which the argument types[1] are implicitly convertable to. This could be like the example above (where char[6u] is implicitly convertable to lazy char[]) or perhaps some function which takes the biggest integer type of the parameters: BiggestIntType!(A) max(A...)(BiggestIntType!(A)[] params...) { ... } Unfortunately, the above function won't work with IFTI. Normally, the "standard" solution to this would be to have a wrapper template: BiggestIntType!(A) max(A...)(A a) { max_impl!(A)(a); } BiggestIntType!(A) max_impl(A...)(BiggestIntType!(A)[] params...) { ... } so that the wrapper ensures IFTI still works, and it simply passes on the argument types to the wrapper. Although this solution works here, it doesn't solve Daniel's problem, and it ends up being repeated whereas I think the language should essentially support doing it for you. More valuable would be the existence of some tuple in a function template which was a tuple of the argument types[1] for a function. I'm not fussed about the syntax, but the point is that argtypes is simple to evaluate: just take typeof() of each of the argument expressions at the call-site, and stick them together as a tuple. Since there's no pattern matching involved, IFTI can't fail. So, the examples above would become: BiggestIntType!(A) max (argtypes A)(BiggestIntType!(A)[] params...) { ... } and void infoF(argtypes A)(lazy DecayStaticArrays!(A) a) { ... } They look almost exactly the same, but the argtypes syntax says to the compiler, "don't try to pattern-match the parameters against lazy DecayStaticArrays!(A), instead just tell me what they are." Supposing you used the above syntax, then it would have the rules: 1. The argtypes parameter must always go last, even after variadic parameters 2. It is impossible to manually instantiate an argtypes parameter. What do you think? --- [1]: Here's what I mean when I distinguish parameter and argument types: void foo(long x) { ... } void main() { int y; foo(y); } Here, I call long the parameter type, and int the argument type. The distinction is that the argument type is the type of the expression at the call site. -- Reiner
Oct 19 2007
next sibling parent reply BCS <ao pathlink.com> writes:
Reply to Reiner,

 (A follow-up from "Any ideas for lazy evaluation on varaible argument
 functions?")
 
 Daniel Keep wrote:
 
 Sadly, I don't think there's any way to fix this.  The problem is
 that if you've got IFTI (which is what allows you to omit the
 explicit template instantiation), the arguments can't be very
 complex.  What you would need is something like this:
 
 void infoF(A...)(lazy DecayStaticArrays!(A) a) { ... }
 
 Where "DecayStaticArrays" turns all statically-sized arrays into
 dynamically-sized ones: so (char[2u], char[6u]) would become (char[],
 char[]).
 
[...] LOL Take a look at my replay to Daniel Keep's post in short, I just suggested a slightly more general form of the exact same idea.
 What do you think?
I'm for it. (clearly)
Oct 19 2007
parent Reiner Pope <some address.com> writes:
BCS wrote:
 Reply to Reiner,
 
 (A follow-up from "Any ideas for lazy evaluation on varaible argument
 functions?")

 Daniel Keep wrote:

 Sadly, I don't think there's any way to fix this.  The problem is
 that if you've got IFTI (which is what allows you to omit the
 explicit template instantiation), the arguments can't be very
 complex.  What you would need is something like this:

 void infoF(A...)(lazy DecayStaticArrays!(A) a) { ... }

 Where "DecayStaticArrays" turns all statically-sized arrays into
 dynamically-sized ones: so (char[2u], char[6u]) would become (char[],
 char[]).
[...] LOL Take a look at my replay to Daniel Keep's post in short, I just suggested a slightly more general form of the exact same idea.
Yes, I think what you suggest is nicer, since it works equally well for the non-variadic case. See my reply there for more. -- Reiner
Oct 20 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
What does IFTI stand for?
(Even Google doesn't seem to know)
Oct 19 2007
parent BCS <ao pathlink.com> writes:
Reply to Janice,

 What does IFTI stand for?
 (Even Google doesn't seem to know)
implicit function itemplate instantiatein it's what lets you skip the template args sometimes. int foo(T)(T t){...} // full form foo!(int)(5); // with ITFI foo(5);
Oct 19 2007
prev sibling parent reply Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Reiner Pope wrote:

 (A follow-up from "Any ideas for lazy evaluation on varaible argument
 functions?")
 
 Daniel Keep wrote:
 Sadly, I don't think there's any way to fix this.  The problem is that
 if you've got IFTI (which is what allows you to omit the explicit
 template instantiation), the arguments can't be very complex.  What you
 would need is something like this:
 
 void infoF(A...)(lazy DecayStaticArrays!(A) a) { ... }
 
 Where "DecayStaticArrays" turns all statically-sized arrays into
 dynamically-sized ones: so (char[2u], char[6u]) would become (char[],
 char[]).
This kind of problem with IFTI seems to come up from time to time. I think the general case of this problem is when you want the parameter types[1] to be something which the argument types[1] are implicitly convertable to. This could be like the example above (where char[6u] is implicitly convertable to lazy char[]) or perhaps some function which takes the biggest integer type of the parameters: BiggestIntType!(A) max(A...)(BiggestIntType!(A)[] params...) { ... } Unfortunately, the above function won't work with IFTI. Normally, the "standard" solution to this would be to have a wrapper template: BiggestIntType!(A) max(A...)(A a) { max_impl!(A)(a); } BiggestIntType!(A) max_impl(A...)(BiggestIntType!(A)[] params...) { ... } so that the wrapper ensures IFTI still works, and it simply passes on the argument types to the wrapper. Although this solution works here, it doesn't solve Daniel's problem, and it ends up being repeated whereas I think the language should essentially support doing it for you. More valuable would be the existence of some tuple in a function template which was a tuple of the argument types[1] for a function. I'm not fussed about the syntax, but the point is that argtypes is simple to evaluate: just take typeof() of each of the argument expressions at the call-site, and stick them together as a tuple. Since there's no pattern matching involved, IFTI can't fail. So, the examples above would become: BiggestIntType!(A) max (argtypes A)(BiggestIntType!(A)[] params...) { ... } and void infoF(argtypes A)(lazy DecayStaticArrays!(A) a) { ... } They look almost exactly the same, but the argtypes syntax says to the compiler, "don't try to pattern-match the parameters against lazy DecayStaticArrays!(A), instead just tell me what they are." Supposing you used the above syntax, then it would have the rules: 1. The argtypes parameter must always go last, even after variadic parameters 2. It is impossible to manually instantiate an argtypes parameter. What do you think?
Couldn't the polysemous values be used here? E.g. caller side calls the function with a set of possible type tuples. In the end the compiler could choose the best match(es) or throw an error on ambiguity. The char[] <-> char[n] conversion is particularly annoying even in very trivial code - a template doesn't sound like a good idea. Approach with polysemous values could be extended in other ways. IFTI functions could be overloaded. One thing that has caused me some headache is the IsExpression - it seems to sweep under the carpet the fact that template parameters cannot do proper static predicate dispatching. I'd like to see a way to specify a compile time boolean function that defines whether the pattern matches or not (arithmetics with types should have proper semantics then - this could be extended elsewhere too, but I won't touch that here) - a general case could look like: void myFun(T1 : predicate_1, ..., Tn : predicate_n)(type_1 param_1, ..., type_n param_n) I don't think moveing the predicates to the fn parameter side (BCS's proposal) is a good idea since it's something different that D does now. Here's some related discussion http://lambda-the-ultimate.org/node/1655
Oct 20 2007
next sibling parent BCS <ao pathlink.com> writes:
Reply to Jari-Matti Mäkelä,

 Couldn't the polysemous values be used here? E.g. caller side calls
 the function with a set of possible type tuples. In the end the
 compiler could choose the best match(es) or throw an error on
 ambiguity. The char[] <-> char[n] conversion is particularly annoying
 even in very trivial code - a template doesn't sound like a good idea.
 
 Approach with polysemous values could be extended in other ways. IFTI
 functions could be overloaded. One thing that has caused me some
 headache is the IsExpression - it seems to sweep under the carpet the
 fact that template parameters cannot do proper static predicate
 dispatching. I'd like to see a way to specify a compile time boolean
 function that defines whether the pattern matches or not (arithmetics
 with types should have proper semantics then - this could be extended
 elsewhere too, but I won't touch that here) - a general case could
 look like:
 
 void myFun(T1 : predicate_1, ..., Tn : predicate_n)(type_1 param_1,
 ..., type_n param_n)
 
 I don't think moveing the predicates to the fn parameter side (BCS's
 proposal) is a good idea since it's something different that D does
 now.
Yeah, I like that syntax a little better than mine (I never did like mine, to many T's) but I'm not clear on the semantics. How would it map the "used" types to the "accepted" types? what I want is to work: void TFn(T)(T a, T b){...} // 'A' converts to 'B', 'B' doesn't convert to 'A' A a; B b; TFn(a, b); TFn(b, a); the first though is to map by position: void TFn(T, U)(TypeFn!(T, U) a, TypeFn!(T, U) b){...} but what if there a different number of template args than function args? void TFn(V, T, U)(V v1, F!(T,U, V) a, V v2, F!(T,U) b){...} now what? what is needed is a way to get at explicitly bind something to the incoming type. An interesting note here is that the this is close to a system where the actual template args can be derived from what is given, this can result in fewer instances of the template. Av get(Av : (uint N = Av.length, T = typeof(Av[0])))(T[] text) { if(text.length < N) return null; return text[0..N]; } TestFor!("hello")(str); // same function TestFor!("world")(str); // in both cases I'm not sure where this is all going, but I see a huge amount of power in letting the programer get ahold of the types and value that the user gives and play with them, particularly if this can work with the ITFI.
Oct 20 2007
prev sibling parent reply Reiner Pope <some address.com> writes:
Jari-Matti Mäkelä wrote:
 Reiner Pope wrote:
 
 (A follow-up from "Any ideas for lazy evaluation on varaible argument
 functions?")

 Daniel Keep wrote:
 Sadly, I don't think there's any way to fix this.  The problem is that
 if you've got IFTI (which is what allows you to omit the explicit
 template instantiation), the arguments can't be very complex.  What you
 would need is something like this:

 void infoF(A...)(lazy DecayStaticArrays!(A) a) { ... }

 Where "DecayStaticArrays" turns all statically-sized arrays into
 dynamically-sized ones: so (char[2u], char[6u]) would become (char[],
 char[]).
This kind of problem with IFTI seems to come up from time to time. I think the general case of this problem is when you want the parameter types[1] to be something which the argument types[1] are implicitly convertable to. This could be like the example above (where char[6u] is implicitly convertable to lazy char[]) or perhaps some function which takes the biggest integer type of the parameters: BiggestIntType!(A) max(A...)(BiggestIntType!(A)[] params...) { ... } Unfortunately, the above function won't work with IFTI. Normally, the "standard" solution to this would be to have a wrapper template: BiggestIntType!(A) max(A...)(A a) { max_impl!(A)(a); } BiggestIntType!(A) max_impl(A...)(BiggestIntType!(A)[] params...) { ... } so that the wrapper ensures IFTI still works, and it simply passes on the argument types to the wrapper. Although this solution works here, it doesn't solve Daniel's problem, and it ends up being repeated whereas I think the language should essentially support doing it for you. More valuable would be the existence of some tuple in a function template which was a tuple of the argument types[1] for a function. I'm not fussed about the syntax, but the point is that argtypes is simple to evaluate: just take typeof() of each of the argument expressions at the call-site, and stick them together as a tuple. Since there's no pattern matching involved, IFTI can't fail. So, the examples above would become: BiggestIntType!(A) max (argtypes A)(BiggestIntType!(A)[] params...) { ... } and void infoF(argtypes A)(lazy DecayStaticArrays!(A) a) { ... } They look almost exactly the same, but the argtypes syntax says to the compiler, "don't try to pattern-match the parameters against lazy DecayStaticArrays!(A), instead just tell me what they are." Supposing you used the above syntax, then it would have the rules: 1. The argtypes parameter must always go last, even after variadic parameters 2. It is impossible to manually instantiate an argtypes parameter. What do you think?
Couldn't the polysemous values be used here? E.g. caller side calls the function with a set of possible type tuples. In the end the compiler could choose the best match(es) or throw an error on ambiguity. The char[] <-> char[n] conversion is particularly annoying even in very trivial code - a template doesn't sound like a good idea. Approach with polysemous values could be extended in other ways. IFTI functions could be overloaded. One thing that has caused me some headache is the IsExpression - it seems to sweep under the carpet the fact that template parameters cannot do proper static predicate dispatching. I'd like to see a way to specify a compile time boolean function that defines whether the pattern matches or not (arithmetics with types should have proper semantics then - this could be extended elsewhere too, but I won't touch that here) - a general case could look like: void myFun(T1 : predicate_1, ..., Tn : predicate_n)(type_1 param_1, ..., type_n param_n) I don't think moveing the predicates to the fn parameter side (BCS's proposal) is a good idea since it's something different that D does now. Here's some related discussion http://lambda-the-ultimate.org/node/1655
In their simplest form, predicates solve a different problem. To give a concrete example of what I'm talking about, suppose I have a struct: struct Wrapper(T) { const bool IsWrapper = true; ... static Wrapper!(T) opImplicitCastFrom(T t) {...} } and I have a function which requires that it is called with arguments always wrapped in Wrapper (Wrapper!(T) might, for instance, be lazy(T), or a reference to T, so that it has to be wrapped on the calling side). Suppose you had a predicate which said if some type, T, is a Wrapper of something else. It might look like: template WrapperPredicate(T) { const bool WrapperPredicate = is(T.IsWrapper == bool); } If you then wrote a templated function using this predicate, it wouldn't work: void foo(T : WrapperPredicate!(T)) (T t) {} void main() { foo(5); // error, int is not a Wrapper, so no template found } This seems to be where you say polysemous values help, ie when instantiating the template, you come up with a list of possible types for T, and see if any match the predicate. The problem is that this list can be infinitely long, if you have something like this: struct Foo(T) { static Foo!(T) opImplicitCastFrom(int i) {...} } While that might be a straw man, it still highlights the difficulty in the compiler being able to find that Wrapper!(int) has an opImplicitCastFrom(int). I think templates are too powerful for the compiler to determine all the possible implicitly-convertible types, so for problems like this, you need to say to the compiler, "here's how you find the type". Predicates alone don't do this, as they just say, "this type is (in)valid". Interestingly, C++0x Concepts have concept_maps, which solve this problem by letting you tell the compiler "this is how to make this type match this concept." --- However, I agree with you about the need for predicates. There's a neat trick which allows you to use them already in D, though: void foo(T, bool T_predicate : true = IsValidParam!(T)) (T t) {...} It works well, except that it doesn't allow specialization. -- Reiner
Oct 20 2007
parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Reiner Pope wrote:

 In their simplest form, predicates solve a different problem. To give a
 concrete example of what I'm talking about, suppose I have a struct:
 
 struct Wrapper(T)
 {
      const bool IsWrapper = true;
      ...
      static Wrapper!(T) opImplicitCastFrom(T t) {...}
 }
 
 and I have a function which requires that it is called with arguments
 always wrapped in Wrapper (Wrapper!(T) might, for instance, be lazy(T),
 or a reference to T, so that it has to be wrapped on the calling side).
 
 Suppose you had a predicate which said if some type, T, is a Wrapper of
 something else. It might look like:
 
 template WrapperPredicate(T)
 {
      const bool WrapperPredicate = is(T.IsWrapper == bool);
 }
 
 If you then wrote a templated function using this predicate, it wouldn't
 work:
 
 void foo(T : WrapperPredicate!(T)) (T t) {}
 
 void main() {
      foo(5); // error, int is not a Wrapper, so no template found
 }
 
 This seems to be where you say polysemous values help, ie when
 instantiating the template, you come up with a list of possible types
 for T, and see if any match the predicate. The problem is that this list
 can be infinitely long
Agreed, calculation of the lists might become intractable. On the other hand you could extend the predicate here to accept both, wrappers and primitives. Then the intractability would depend on the search method.
 However, I agree with you about the need for predicates. There's a neat
 trick which allows you to use them already in D, though:
 
 void foo(T, bool T_predicate : true = IsValidParam!(T)) (T t) {...}
Yep, and this syntax seems to make more sense since it allows you to specify predicates independently of the other template parameters.
 It works well, except that it doesn't allow specialization.
True (at least in the case of IFTI), but I meant the predicate would allow specialization with and without IFTI. It would also use similar semantics to IsExpression: void foo(T)(T T) { ... } // 1) void foo(T, isValidParam!(T) == true)(T t) { ... } // 2) Now when isValidParam!(T) evaluates to true, 2) would be selected, otherwise 1). Again the syntax isn't probably as good as it could be.
Oct 21 2007