digitalmars.D - challenge #2: implement the varargs_reduce metafunction
- Andrei Alexandrescu (See Website for Email) (10/10) Jan 23 2007 My previous post defines max by availing itself of a metafunction called
- Bruno Medeiros (105/119) Jan 23 2007 Here is my try. I assume the reduce type must be exactly the same in all...
- Christian Kamm (50/52) Jan 23 2007 Your implementation doesn't work with template functions. Here's a rathe...
- Andrei Alexandrescu (See Website For Email) (15/64) Jan 23 2007 This is a good solution. Ideally, you would combine it with Bruno
- Lutger (20/22) Jan 23 2007 I suspect the same since I have just a few lines. This is what I came up...
- janderson (2/29) Jan 23 2007 What about maxtype support?
- Lutger (3/4) Jan 23 2007 I see, it doesn't do that, not good. In fact it demands all arguments
- Andrei Alexandrescu (See Website For Email) (10/35) Jan 23 2007 That is not the case, and is actually an important point in implementing...
- janderson (4/18) Jan 23 2007 While your at it. What about a multi-threaded version? ie Each
- Andrei Alexandrescu (See Website For Email) (16/35) Jan 23 2007 That's a good point, and goes right to the core of my solution, which
- Frits van Bommel (15/45) Jan 23 2007 Assumes the operation is associative. That may not always be the case;
- Andrei Alexandrescu (See Website For Email) (5/44) Jan 23 2007 I think this ia a good point. Probably varargs_reduce must be linear to
- Chris Nicholson-Sauls (5/54) Jan 23 2007 I think that would be best (having a semi-seperate _parellel version). ...
- janderson (5/12) Jan 24 2007 My thought exactly. It would be nice to have a vargags_reduce and
- Kevin Bealer (45/55) Jan 23 2007 There is a really simple solution, but I'm not sure if it does the right...
- Andrei Alexandrescu (See Website For Email) (8/40) Jan 23 2007 Interesting point. I think varargs_reduce can safely assume that linear
- Jarrett Billingsley (13/23) Jan 23 2007 "Andrei Alexandrescu (See Website for Email)"
- Andrei Alexandrescu (See Website For Email) (21/49) Jan 23 2007 It's really easy. I can't right now disclose all of my agenda for
- kris (24/36) Jan 23 2007 That's great, Andrei. And I, for one, truly want to see some of those
- Andrei Alexandrescu (See Website For Email) (13/54) Jan 23 2007 I'm afraid I am not in the position to help here. All I'm really doin'
- kris (12/75) Jan 23 2007 Aye, and that is certainly appreciated. Some of the items you've
- Andrei Alexandrescu (See Website For Email) (3/17) Jan 24 2007 Could you give more detail?
- kris (2/25) Jan 24 2007 Yep -- will get back to you with examples (probably tomorrow)
- Bill Baxter (11/37) Jan 24 2007 Well there is this issue that &foo where foo is an overloaded will give
- Andrei Alexandrescu (See Website For Email) (4/17) Jan 24 2007 Ouch. This warrants a bug report. It sure has ripples in lots of
- Bill Baxter (4/23) Jan 25 2007 It's in there.
- John Reimer (13/32) Jan 25 2007 That's not considered a bug, however. There have been many complaints
- Walter Bright (4/34) Jan 25 2007 I didn't invalidate it because it is a bug. The lookup thing was a
- BCS (3/7) Jan 26 2007 Is it? VS complains if there is any question, I haven't checked GCC. Or ...
- Walter Bright (2/11) Jan 26 2007 Looks like something different.
- James Dennett (9/21) Jan 26 2007 Could you maybe explain what you mean by "C++-style
- Walter Bright (4/7) Jan 26 2007 In C++, overriding one function in a base class prevents use of any of
- James Dennett (21/29) Jan 26 2007 OK; I'd say this is a name lookup difference, not a
- kris (122/146) Jan 25 2007 Please pardon the delay, and the length of this post ... here's a test:
- Andrei Alexandrescu (See Website For Email) (18/178) Jan 26 2007 Yup. So far so good.
- kris (41/242) Jan 26 2007 You'll have noticed that the constant 'c' defaults to /char/, and that
- Frits van Bommel (31/88) Jan 27 2007 It's a bit more complicated with character literals than just defaulting...
- kris (3/101) Jan 27 2007 Yes, that would seem eminently reasonable.
- Bill Baxter (6/18) Jan 27 2007 And while you're at it do the same for integer and float literals -- use...
- Oskar Linde (26/45) Jan 29 2007 A slight inconsistency here is that:
- Frits van Bommel (7/33) Jan 29 2007 Yes, that probably warrants a bug report. Either an error should be
- Sean Kelly (7/21) Jan 27 2007 I suppose this is where the difference of opinion comes in, but why do
- Kevin Bealer (47/60) Jan 26 2007 ...
- Andrei Alexandrescu (See Website For Email) (22/47) Jan 26 2007 [snip]
- Frits van Bommel (5/28) Jan 26 2007 Would it also be possible to 'cherry-pick' attributes?
- Sean Kelly (43/73) Jan 26 2007 This gets into something that would be nice to have in D: some sort of
- Andrei Alexandrescu (See Website For Email) (4/34) Jan 26 2007 You will be able to cherry-pick with "is" tests. Walter is opposed to
- Bill Baxter (5/40) Jan 26 2007 If you have to cherry pick with "is" tests to separate the bits out of
- Frits van Bommel (2/9) Jan 27 2007 Because just 'S' is much shorter when you don't want to cherry-pick?
- Bill Baxter (20/30) Jan 27 2007 Just wondering if there are that many cases where all want to do is pick...
- Sean Kelly (7/46) Jan 26 2007 Interesting. So I suppose I could explicitly instantiate the template a...
- Kevin Bealer (5/64) Jan 26 2007 I like this; does this mean that when declaring an instance, you would
- Andrei Alexandrescu (See Website For Email) (23/55) Jan 26 2007 The intent is to deduce the storage, but specifying it explicitly works
- Sean Kelly (5/66) Jan 27 2007 So how would one declare such a type? typeof(T) perhaps? I imagine
- Lionello Lunesu (7/9) Jan 23 2007 I'm often running into problems with the (old!) GC, and the only fix is ...
- kris (28/39) Jan 23 2007 Yeah, I agree. There's things that each of us would like resolved. For
- Sean Kelly (13/34) Jan 24 2007 Could you explain further? Is this simply a problem with false
- Lionello Lunesu (9/11) Jan 24 2007 I'm afraid I'm not 100% sure. The memory use was definitely higher than ...
- Sean Kelly (7/18) Jan 24 2007 Ah, that is because all those strings are being scanned for pointers.
- Thomas Kuehne (11/14) Jan 24 2007 -----BEGIN PGP SIGNED MESSAGE-----
- Sean Kelly (3/13) Jan 24 2007 No. Would it work for a deadlock situation?
- Thomas Kuehne (10/19) Jan 24 2007 -----BEGIN PGP SIGNED MESSAGE-----
- Thomas Kuehne (29/35) Jan 24 2007 -----BEGIN PGP SIGNED MESSAGE-----
- Sean Kelly (3/35) Jan 24 2007 Very nice! I may just start doing all my D debugging on Linux.
- Thomas Kuehne (29/35) Jan 24 2007 -----BEGIN PGP SIGNED MESSAGE-----
- Frits van Bommel (4/4) Jan 24 2007 Thomas Kuehne wrote:
My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. Andrei
Jan 23 2007
Andrei Alexandrescu (See Website for Email) wrote:My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiHere is my try. I assume the reduce type must be exactly the same in all arguments. Should that not be the case? As for efficiency, I'm not sure what the requirements are. Is there to be any parallelization/vectorization considerations? This one, instead of linear iteration, uses binary recursion to calculate the result, but for *no particular reason* since its not actually any faster than linear recursion. This one seems much more easy that the fist challenge, so I suspect I'm missing something. ----------------------------------- //import stdext.stdio; import std.stdio : writeln = writefln; static import meta.Util; // Gets the typeof if not a type already. template TypeOf(alias ALIAS) { static if(is(ALIAS)) { alias ALIAS TypeOf; } else { alias typeof(ALIAS) TypeOf; } } // See if the types are the same template TypeEquals(alias A1, alias A2) { static if(is(TypeOf!(A1) == TypeOf!(A2))) const bool TypeEquals = true; else const bool TypeEquals = false; } // Duplicated template because primitive types do not match aliases :( template TypeEquals(T1, T2) { static if(is(T1 == T2)) const bool TypeEquals = true; else const bool TypeEquals = false; } /***************** varargsReduce ***************/ // Template implicit property workaround template varargsReduce(alias FN) { alias varargsReduce_Impl!(FN).varargsReduce_Impl varargsReduce; } template varargsReduce_Impl(alias FN) { static import std.traits; static assert(is(typeof(FN) == function) || is(typeof(FN) == delegate)); alias std.traits.ParameterTypeTuple!(FN) Params; alias std.traits.ReturnType!(FN) ReduceType; static assert(Params.length == 2); //static assert(typeof(FNarg1) == typeof(FNarg2)); static assert(TypeEquals!(Params[0], Params[1])); //static assert(typeof(FNarg1) == ReduceType); static assert(TypeEquals!(Params[0], ReduceType)); ReduceType varargsReduce_Impl(ARGS...) (ARGS args) { pragma(msg, "varargsReduce_Impl! length:" ~ meta.Util.itoa!(ARGS.length)); //static assert(ARGS.length != 0, "Error: no args"); static if(ARGS.length == 1) { static assert(TypeEquals!(typeof(args[0]), ReduceType)); return args[0]; } else static if(ARGS.length == 2) { static assert(TypeEquals!(typeof(args[0]), ReduceType)); static assert(TypeEquals!(typeof(args[1]), ReduceType)); return FN(args[0], args[1]); } else static if(ARGS.length >= 3) { ReduceType val1 = varargsReduce_Impl(args[0 .. ARGS.length/2]); ReduceType val2 = varargsReduce_Impl(args[ARGS.length/2 .. ARGS.length]); return FN(val1, val2); } else { static assert(false, "Error: no args"); } } } /***************** Usage ***************/ int sum(int a, int b) { return a + b; } char[] cat(char[] a, char[] b) { return a ~ b; } void main(char[][] args) { auto result = varargsReduce!(sum)(1, 3, 3, 7); writeln(result); auto result2 = varargsReduce!(cat)("H"[], "e"[], "l"[], "l"[], "o"[]); writeln(result2); } ----------------- itoa: toStrings an int -------------------- module meta.Util; template itoa(int i) { static if (i < 0) { static const char[] itoa = "-" ~ itoa!(-i); } else { static if (i / 10 > 0) { static const char[] itoa = itoa(i / 10) ~ "0123456789"[i % 10]; } else { static const char[] itoa = "0123456789"[i % 10 .. i % 10 + 1]; } } } -- Bruno Medeiros - MSc in CS/E student http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
Jan 23 2007
This one seems much more easy that the fist challenge, so I suspect I'=m =missing something.Your implementation doesn't work with template functions. Here's a rathe= r = straightforward implementation that seems to work: template varargs_reduce(alias reduce2) { private: template ReduceType(ARGS...) { static assert(ARGS.length >=3D 2, "requires >=3D 2 arguments"); static if(ARGS.length =3D=3D 2) alias typeof(reduce2(ARGS[0], ARGS[1])) ReduceType; else static if(ARGS.length > 2) alias typeof(reduce2(ARGS[0], ReduceType!(ARGS[1..$]))) ReduceTyp= e; } public: // I'd have prefered a typeof(result) or typeof(return) as return typ= e = here. ReduceType!(ARGS) varargs_reduce(ARGS...)(ARGS args) { static assert(args.length >=3D 2, "requires >=3D 2 arguments"); = static if(args.length =3D=3D 2) auto result =3D reduce2(args[0], args[1]); else static if(args.length > 2) auto result =3D reduce2(args[0], varargs_reduce(args[1..$])); = return result; } } typeof(T+Q) plus(T,Q)(T a, Q b) { return a + b; } void main() { alias varargs_reduce!(plus).varargs_reduce plus_va; plus_va(1, 2.1, 3i, 4u); } While it compiles and seems to behave correctly, I'm suspicious of my us= e = of typeof in ReduceType - does the spec guarantee it does what I want? I= = tried using is and std.traits.ReturnType but typeof lead to the simplest= = solution. Cheers, Christian
Jan 23 2007
Christian Kamm wrote:This is a good solution. Ideally, you would combine it with Bruno Medeiros' idea to expand the template in a binary manner so as to spread dependencies. One subtle point is that you don't want to use auto result = ...; We have not yet defined the semantics of transporting storage class entirely. It is possible that for backward compatibility reasons, auto will eliminate the 'inout' storage class that max2 so laboriously transported out. If that happens, your code will not compile. So the best bet is to simply return the reduce2 expression directly. (Now I guess it's self-evident why I said earlier that D's handling of storage classes is a source of radioactive waste in the type system. It insidiously hits everywhere and when least expected.) Great work, thanks! AndreiThis one seems much more easy that the fist challenge, so I suspect I'm missing something.Your implementation doesn't work with template functions. Here's a rather straightforward implementation that seems to work: template varargs_reduce(alias reduce2) { private: template ReduceType(ARGS...) { static assert(ARGS.length >= 2, "requires >= 2 arguments"); static if(ARGS.length == 2) alias typeof(reduce2(ARGS[0], ARGS[1])) ReduceType; else static if(ARGS.length > 2) alias typeof(reduce2(ARGS[0], ReduceType!(ARGS[1..$]))) ReduceType; } public: // I'd have prefered a typeof(result) or typeof(return) as return type here. ReduceType!(ARGS) varargs_reduce(ARGS...)(ARGS args) { static assert(args.length >= 2, "requires >= 2 arguments"); static if(args.length == 2) auto result = reduce2(args[0], args[1]); else static if(args.length > 2) auto result = reduce2(args[0], varargs_reduce(args[1..$])); return result; } } typeof(T+Q) plus(T,Q)(T a, Q b) { return a + b; } void main() { alias varargs_reduce!(plus).varargs_reduce plus_va; plus_va(1, 2.1, 3i, 4u); } While it compiles and seems to behave correctly, I'm suspicious of my use of typeof in ReduceType - does the spec guarantee it does what I want? I tried using is and std.traits.ReturnType but typeof lead to the simplest solution.
Jan 23 2007
Bruno Medeiros wrote:This one seems much more easy that the fist challenge, so I suspect I'm missing something.I suspect the same since I have just a few lines. This is what I came up with, does it meet the requirements? There must be something wrong. template reduce(alias fn) { typeof(T[0]) reduce(T...)(T args) { static assert( is(T[0] == T[1]) && is( T[0] == typeof( fn(T[0], T[1]) ) ), "try again" ); static assert( args.length > 1, "nothing to reduce here"); auto result = args[0]; foreach(arg; args[1..$]) result = fn(result, arg); return result; } } example: alias reduce!(function(int a, int b) { return (a > b) ? a : b; }) max; assert( max(2,55,787) == 787 );
Jan 23 2007
Lutger wrote:Bruno Medeiros wrote:What about maxtype support?This one seems much more easy that the fist challenge, so I suspect I'm missing something.I suspect the same since I have just a few lines. This is what I came up with, does it meet the requirements? There must be something wrong. template reduce(alias fn) { typeof(T[0]) reduce(T...)(T args) { static assert( is(T[0] == T[1]) && is( T[0] == typeof( fn(T[0], T[1]) ) ), "try again" ); static assert( args.length > 1, "nothing to reduce here"); auto result = args[0]; foreach(arg; args[1..$]) result = fn(result, arg); return result; } } example: alias reduce!(function(int a, int b) { return (a > b) ? a : b; }) max; assert( max(2,55,787) == 787 );
Jan 23 2007
janderson wrote:What about maxtype support?I see, it doesn't do that, not good. In fact it demands all arguments and return value to be of the same type.
Jan 23 2007
Bruno Medeiros wrote:Andrei Alexandrescu (See Website for Email) wrote:That is not the case, and is actually an important point in implementing varargs_reduce.My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiHere is my try. I assume the reduce type must be exactly the same in all arguments. Should that not be the case?As for efficiency, I'm not sure what the requirements are. Is there to be any parallelization/vectorization considerations? This one, instead of linear iteration, uses binary recursion to calculate the result, but for *no particular reason* since its not actually any faster than linear recursion.It could well be faster. The dependency chain in your expansion has logarithmic depth; in linear expansion it has linear depth. Superscalar machines have the ability to execute independent operations literally in parallel (they have multiple ALUs).This one seems much more easy that the fist challenge, so I suspect I'm missing something.As Christian Kamm mentioned, your solution does not work with templates, and min2 actually is one. Andrei
Jan 23 2007
Andrei Alexandrescu (See Website for Email) wrote:My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiWhile your at it. What about a multi-threaded version? ie Each function that is called is placed on one of 4 threads. -Joel
Jan 23 2007
janderson wrote:Andrei Alexandrescu (See Website for Email) wrote:That's a good point, and goes right to the core of my solution, which (similarly to Bruno Medeiros') arranges operations in an optimal way for superscalar evaluation, e.g. max(a, b, c, d) is expanded not to: max2(max2(max2(a, b), c), d) but instead to: max2(max2(a, b), max2(c, d)) The number of operations is the same but in the latter case there is logaritmically less dependency between partial results. When max2 expands into a primitive comparison, the code is easy prey for a superscalar machine to execute in parallel. This won't count in a great deal of real code, but the fact that this will go in the standard library warrants the thought. Besides, the fact that the language makes it so easy, we can actually think of such subtlety, is very encouraging. AndreiMy previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiWhile your at it. What about a multi-threaded version? ie Each function that is called is placed on one of 4 threads.
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:janderson wrote:Assumes the operation is associative. That may not always be the case; it's not even necessarily true that the function takes arguments of identical types. Silly example: char[] accumulate(char[] arr, char element) { return arr ~= element; } Of course, you could *require* that the operation is associative, but I didn't see that anywhere in the specification. And as far as I can tell, that generally isn't assumed for reduce. For instance in Lisp, REDUCE is either left-to-right or right-to-left (depending on a flag)[1]. In Python, it's always left-to-right[2]. (those were the ones I could most easily find with a quick Google search) [1]: http://www.lisp.org/HyperSpec/Body/fun_reduce.html [2]: http://docs.python.org/lib/built-in-funcs.htmlAndrei Alexandrescu (See Website for Email) wrote:That's a good point, and goes right to the core of my solution, which (similarly to Bruno Medeiros') arranges operations in an optimal way for superscalar evaluation, e.g. max(a, b, c, d) is expanded not to: max2(max2(max2(a, b), c), d) but instead to: max2(max2(a, b), max2(c, d))My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiWhile your at it. What about a multi-threaded version? ie Each function that is called is placed on one of 4 threads.
Jan 23 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote:I think this ia a good point. Probably varargs_reduce must be linear to make the most conservative assumptions. There is always the chance of defining varargs_reduce_parallel. Thanks! Andreijanderson wrote:Assumes the operation is associative. That may not always be the case; it's not even necessarily true that the function takes arguments of identical types. Silly example: char[] accumulate(char[] arr, char element) { return arr ~= element; }Andrei Alexandrescu (See Website for Email) wrote:That's a good point, and goes right to the core of my solution, which (similarly to Bruno Medeiros') arranges operations in an optimal way for superscalar evaluation, e.g. max(a, b, c, d) is expanded not to: max2(max2(max2(a, b), c), d) but instead to: max2(max2(a, b), max2(c, d))My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiWhile your at it. What about a multi-threaded version? ie Each function that is called is placed on one of 4 threads.
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote:I think that would be best (having a semi-seperate _parellel version). I also wonder about applying this same "feature" to functions of 3 or 4 arguments (or even arbitrary! so long as none are themselves variadic), but that could come later. -- Chris Nicholson-SaulsAndrei Alexandrescu (See Website For Email) wrote:I think this ia a good point. Probably varargs_reduce must be linear to make the most conservative assumptions. There is always the chance of defining varargs_reduce_parallel. Thanks! Andreijanderson wrote:Assumes the operation is associative. That may not always be the case; it's not even necessarily true that the function takes arguments of identical types. Silly example: char[] accumulate(char[] arr, char element) { return arr ~= element; }Andrei Alexandrescu (See Website for Email) wrote:That's a good point, and goes right to the core of my solution, which (similarly to Bruno Medeiros') arranges operations in an optimal way for superscalar evaluation, e.g. max(a, b, c, d) is expanded not to: max2(max2(max2(a, b), c), d) but instead to: max2(max2(a, b), max2(c, d))My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiWhile your at it. What about a multi-threaded version? ie Each function that is called is placed on one of 4 threads.
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:I think this ia a good point. Probably varargs_reduce must be linear to make the most conservative assumptions. There is always the chance of defining varargs_reduce_parallel. Thanks! AndreiMy thought exactly. It would be nice to have a vargags_reduce and simply add _parallel to it for instant speed gains. I also think that there will be other functions that could go the same way. Perhaps _parallel (or whatever we decide to call it) should be standardized.
Jan 24 2007
== Quote from Andrei Alexandrescu (See Website for Email) (SeeWebsiteForEmail erdani.org)'s articleMy previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiThere is a really simple solution, but I'm not sure if it does the right thing with respect to your wording above. What exactly is meant by deducing the result type properly? Specifically, if the input types are different kinds of objects, is the result expected to have the same type as a linear sequence of operations? 1. Can we assume the type conversions are non-cyclic? 2. Should we assume that the delegate looks like: T dg(T, T), or like: T dg(U, V) 3. If the signature is T dg(U, V), can we assume that type conversions are non-cyclic? Cyclic types: (requires more template logic than actually shown here) Rock add(Paper x, Scissors y); Paper add(Scissors x, Rock x); Scissors add(Rock x, Paper y); If this kind of stuff is permitted, the solution is possible but a lot messier. // Simple solution: import std.stdio; import std.traits; template reduce(D, E...) { ReturnType!(D) reduce(D dg, E v) { ReturnType!(D) rv; static if (E.length >= 1) { rv = v[0]; foreach(i, a; v[1..$]) { rv = dg(rv, a); } } return rv; } } int main(char[][] args) { int add(int a, int b) { return a + b; } double mul(double a, double b) { return a * b; } int x = reduce(& add, 101, 102, 103, 104); double y = reduce(& mul, .1, .2, .3, .4, .5); writefln("sum=%s prod=%s", x, y); return 0; }
Jan 23 2007
Kevin Bealer wrote:== Quote from Andrei Alexandrescu (See Website for Email) (SeeWebsiteForEmail erdani.org)'s articleInteresting point. I think varargs_reduce can safely assume that linear reduction always works f(f(f(f(...f(a1, a2), a3), a4), ...). My solution optimizes more aggressively by assuming that reduction can be performed in any order. Your solution is less optimal because it uses a delegate. Ideally you just use an alias and deduce the result type by simply using the alias. AndreiMy previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiThere is a really simple solution, but I'm not sure if it does the right thing with respect to your wording above. What exactly is meant by deducing the result type properly? Specifically, if the input types are different kinds of objects, is the result expected to have the same type as a linear sequence of operations? 1. Can we assume the type conversions are non-cyclic? 2. Should we assume that the delegate looks like: T dg(T, T), or like: T dg(U, V) 3. If the signature is T dg(U, V), can we assume that type conversions are non-cyclic? Cyclic types: (requires more template logic than actually shown here) Rock add(Paper x, Scissors y); Paper add(Scissors x, Rock x); Scissors add(Rock x, Paper y); If this kind of stuff is permitted, the solution is possible but a lot messier.
Jan 23 2007
"Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:ep4i7p$2n1t$4 digitaldaemon.com...My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiThis post is not meant to be inflammatory. Answer me this: are you just toying with us, tempting us with things which are unimplementable without new features which are secretly being developed for D? Or are you just surreptitiously trying to get the community to write a bunch of standard library metacode for you? ;) How about this: start a thread in which you disclose all the fancy features you (you plural, you and Walter, since you now seem to be the _team_ designing D) have been planning. For that matter, is coming up with crazy new features like inout returns and opImplicitCast _all_ Walter's doing? Or is he fixing bugs?
Jan 23 2007
Jarrett Billingsley wrote:"Andrei Alexandrescu (See Website for Email)" <SeeWebsiteForEmail erdani.org> wrote in message news:ep4i7p$2n1t$4 digitaldaemon.com...Definitely not taken as such.My previous post defines max by availing itself of a metafunction called varargs_reduce. Your challenge is to define it. varargs_reduce takes an alias which it assumes is a function of two arguments, and it defines a vararg function that applies that alias repeatedly, similarly to the reduce primitive that is present in functional languages. Again, for background on functional reduce, see e.g.: http://www.joelonsoftware.com/items/2006/08/01.html Define varargs_reduce to deduce the result type properly, and to generate code as efficient as possible. AndreiThis post is not meant to be inflammatory.Answer me this: are you just toying with us, tempting us with things which are unimplementable without new features which are secretly being developed for D? Or are you just surreptitiously trying to get the community to write a bunch of standard library metacode for you? ;)It's really easy. I can't right now disclose all of my agenda for objective reasons (I will do it later), but the gist of the matter is simple: 1. There are some simple tests for a language gaging very precisely the power of that language 2. I want D to pass those tests 3. I want to increase community's awareness of the importance of those tests for larger use cases, as well as gather ideas from the community about how to overcome D's shortcomings in passing the tests.How about this: start a thread in which you disclose all the fancy features you (you plural, you and Walter, since you now seem to be the _team_ designing D) have been planning.We are doing it already, e.g. by describing my suggested max implementation. But I don't want to do that preemptively, because I'd bias future responses. I wished, for example, that somebody could suggest a better design instead of "storageof(T) T".For that matter, is coming up with crazy new features like inout returns and opImplicitCast _all_ Walter's doing? Or is he fixing bugs?Walter and I have been discussing these issues and we have agreement on the necessity of certain features (of which indeed better inout handling and opImplicitCast are two). At this point, we don't have much more of a design than what I've already shown. For all I know, Walter hasn't started implementing any of them. Andrei
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote: [snip]It's really easy. I can't right now disclose all of my agenda for objective reasons (I will do it later), but the gist of the matter is simple: 1. There are some simple tests for a language gaging very precisely the power of that language 2. I want D to pass those tests 3. I want to increase community's awareness of the importance of those tests for larger use cases, as well as gather ideas from the community about how to overcome D's shortcomings in passing the tests.That's great, Andrei. And I, for one, truly want to see some of those changes happen (such as read-only arrays, covered by the const you talk of). However, there are also some rather pressing matters of fixing stuff that is simply broken, and is hampering attempts to build real, live, useful apps at this time. Yeah, new features are sexy cool, and fixing long-standing issues ain't. How about taking a long hard look at what sucks in D right now, and making those a primary priority? Having read many of your posts since yesterday, it seems you've identified a number of those. Great! Yet, you guys are apparently pressing ahead with wonderful new stuff instead? Like, for instance, an updated GC? When did that ever top the list of real-world issues for D? To anticipate a question: There's some serious issues with templates in libs (you really can't put them there successfully), some serious issues with debuggers not being able to locate source-files for templates; lots of problems related to real-world, end-user, /usability/ of the language on a daily basis. I submit that such issues are just as important a test to pass than the ones you allude to. Dare I say, perhaps more so for people actually using the language right now? Regards; - Kris
Jan 23 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote: [snip]I'm afraid I am not in the position to help here. All I'm really doin' is bitchin'. Walter does the actual work, and prioritizing and implementing is entirely his charter. That being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.It's really easy. I can't right now disclose all of my agenda for objective reasons (I will do it later), but the gist of the matter is simple: 1. There are some simple tests for a language gaging very precisely the power of that language 2. I want D to pass those tests 3. I want to increase community's awareness of the importance of those tests for larger use cases, as well as gather ideas from the community about how to overcome D's shortcomings in passing the tests.That's great, Andrei. And I, for one, truly want to see some of those changes happen (such as read-only arrays, covered by the const you talk of). However, there are also some rather pressing matters of fixing stuff that is simply broken, and is hampering attempts to build real, live, useful apps at this time. Yeah, new features are sexy cool, and fixing long-standing issues ain't. How about taking a long hard look at what sucks in D right now, and making those a primary priority? Having read many of your posts since yesterday, it seems you've identified a number of those. Great! Yet, you guys are apparently pressing ahead with wonderful new stuff instead? Like, for instance, an updated GC? When did that ever top the list of real-world issues for D?To anticipate a question: There's some serious issues with templates in libs (you really can't put them there successfully), some serious issues with debuggers not being able to locate source-files for templates; lots of problems related to real-world, end-user, /usability/ of the language on a daily basis. I submit that such issues are just as important a test to pass than the ones you allude to. Dare I say, perhaps more so for people actually using the language right now?I entirely agree. Figuring out priorities is Walter's call. Let's keep our fingers crossed :o). Andrei
Jan 23 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:Drat ... that's apparently all I'm achieving too :(Andrei Alexandrescu (See Website For Email) wrote: [snip]I'm afraid I am not in the position to help here. All I'm really doin' is bitchin'.It's really easy. I can't right now disclose all of my agenda for objective reasons (I will do it later), but the gist of the matter is simple: 1. There are some simple tests for a language gaging very precisely the power of that language 2. I want D to pass those tests 3. I want to increase community's awareness of the importance of those tests for larger use cases, as well as gather ideas from the community about how to overcome D's shortcomings in passing the tests.That's great, Andrei. And I, for one, truly want to see some of those changes happen (such as read-only arrays, covered by the const you talk of). However, there are also some rather pressing matters of fixing stuff that is simply broken, and is hampering attempts to build real, live, useful apps at this time. Yeah, new features are sexy cool, and fixing long-standing issues ain't. How about taking a long hard look at what sucks in D right now, and making those a primary priority? Having read many of your posts since yesterday, it seems you've identified a number of those. Great! Yet, you guys are apparently pressing ahead with wonderful new stuff instead? Like, for instance, an updated GC? When did that ever top the list of real-world issues for D?Walter does the actual work, and prioritizing and implementing is entirely his charter. That being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.Aye, and that is certainly appreciated. Some of the items you've mentioned recently have long been festering sores for many. It'll certainly be refreshing to see some attention focused upon those. Regarding implicit conversion rules -- are you including the "issues" currently surrounding signature-matching when using constants?Hehe; superstition :-p Well, thanks for replying. The clarification is much appreciated, though I'm honestly sad to hear you're not more deeply involved. Cheers; - KrisTo anticipate a question: There's some serious issues with templates in libs (you really can't put them there successfully), some serious issues with debuggers not being able to locate source-files for templates; lots of problems related to real-world, end-user, /usability/ of the language on a daily basis. I submit that such issues are just as important a test to pass than the ones you allude to. Dare I say, perhaps more so for people actually using the language right now?I entirely agree. Figuring out priorities is Walter's call. Let's keep our fingers crossed :o).
Jan 23 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote:Could you give more detail? AndreiThat being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.Aye, and that is certainly appreciated. Some of the items you've mentioned recently have long been festering sores for many. It'll certainly be refreshing to see some attention focused upon those. Regarding implicit conversion rules -- are you including the "issues" currently surrounding signature-matching when using constants?
Jan 24 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:Yep -- will get back to you with examples (probably tomorrow)Andrei Alexandrescu (See Website For Email) wrote:Could you give more detail? AndreiThat being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.Aye, and that is certainly appreciated. Some of the items you've mentioned recently have long been festering sores for many. It'll certainly be refreshing to see some attention focused upon those. Regarding implicit conversion rules -- are you including the "issues" currently surrounding signature-matching when using constants?
Jan 24 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote:Well there is this issue that &foo where foo is an overloaded will give you pointer to the first version of foo in the source file. There's no way to disambiguate as far as I know. Particularly frustrating for property methods void prop(float x) { ... } float prop() { ... } &prop just gives you the first one no matter what. Maybe not what you're talking about with "matching using constants" but it is related to signature matching (or lack thereof). --bbkris wrote:Yep -- will get back to you with examples (probably tomorrow)Andrei Alexandrescu (See Website For Email) wrote:Could you give more detail? AndreiThat being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.Aye, and that is certainly appreciated. Some of the items you've mentioned recently have long been festering sores for many. It'll certainly be refreshing to see some attention focused upon those. Regarding implicit conversion rules -- are you including the "issues" currently surrounding signature-matching when using constants?
Jan 24 2007
Bill Baxter wrote:Well there is this issue that &foo where foo is an overloaded will give you pointer to the first version of foo in the source file. There's no way to disambiguate as far as I know. Particularly frustrating for property methods void prop(float x) { ... } float prop() { ... } &prop just gives you the first one no matter what. Maybe not what you're talking about with "matching using constants" but it is related to signature matching (or lack thereof).Ouch. This warrants a bug report. It sure has ripples in lots of template code, too. Andrei
Jan 24 2007
Andrei Alexandrescu (See Website For Email) wrote:Bill Baxter wrote:It's in there. http://d.puremagic.com/issues/show_bug.cgi?id=52 --bbWell there is this issue that &foo where foo is an overloaded will give you pointer to the first version of foo in the source file. There's no way to disambiguate as far as I know. Particularly frustrating for property methods void prop(float x) { ... } float prop() { ... } &prop just gives you the first one no matter what. Maybe not what you're talking about with "matching using constants" but it is related to signature matching (or lack thereof).Ouch. This warrants a bug report. It sure has ripples in lots of template code, too. Andrei
Jan 25 2007
On Wed, 24 Jan 2007 23:12:21 -0800, Andrei Alexandrescu (See Website For Email) wrote:Bill Baxter wrote:That's not considered a bug, however. There have been many complaints about D's signature lookup "protocol" over the last 2 to 3 years. Walter has many times stated that it's done that way on purpose, I think to avoid compiler complexity. Maybe this example shows yet another reason why it hurts, but we've had no success convincing him otherwise. That's just one example of "silent acception" of the symbol. I'm actually surprised that Walter didn't invalidate that bug report. I guess this is an example of the slow aggregation of evidence against certain language/implementation mechanisms. Template issues related to this may further this realization. -JJRWell there is this issue that &foo where foo is an overloaded will give you pointer to the first version of foo in the source file. There's no way to disambiguate as far as I know. Particularly frustrating for property methods void prop(float x) { ... } float prop() { ... } &prop just gives you the first one no matter what. Maybe not what you're talking about with "matching using constants" but it is related to signature matching (or lack thereof).Ouch. This warrants a bug report. It sure has ripples in lots of template code, too. Andrei
Jan 25 2007
John Reimer wrote:On Wed, 24 Jan 2007 23:12:21 -0800, Andrei Alexandrescu (See Website For Email) wrote:I didn't invalidate it because it is a bug. The lookup thing was a debate about whether Java-style or C++-style overriding is done. D does C++ style overriding.Bill Baxter wrote:That's not considered a bug, however. There have been many complaints about D's signature lookup "protocol" over the last 2 to 3 years. Walter has many times stated that it's done that way on purpose, I think to avoid compiler complexity. Maybe this example shows yet another reason why it hurts, but we've had no success convincing him otherwise. That's just one example of "silent acception" of the symbol. I'm actually surprised that Walter didn't invalidate that bug report.Well there is this issue that &foo where foo is an overloaded will give you pointer to the first version of foo in the source file. There's no way to disambiguate as far as I know. Particularly frustrating for property methods void prop(float x) { ... } float prop() { ... } &prop just gives you the first one no matter what. Maybe not what you're talking about with "matching using constants" but it is related to signature matching (or lack thereof).Ouch. This warrants a bug report. It sure has ripples in lots of template code, too. Andrei
Jan 25 2007
Reply to Walter,I didn't invalidate it because it is a bug. The lookup thing was a debate about whether Java-style or C++-style overriding is done. D does C++ style overriding.Is it? VS complains if there is any question, I haven't checked GCC. Or are you talking about something different.
Jan 26 2007
BCS wrote:Reply to Walter,Looks like something different.I didn't invalidate it because it is a bug. The lookup thing was a debate about whether Java-style or C++-style overriding is done. D does C++ style overriding.Is it? VS complains if there is any question, I haven't checked GCC. Or are you talking about something different.
Jan 26 2007
Walter Bright wrote:BCS wrote:Could you maybe explain what you mean by "C++-style overriding" as opposed to Java-style overriding? In what way is D more like C++ here than like Java? The overriding rules seem similar in C++ and in Java, though the name/function lookup rules are very different, and there are restrictions on Java related to changing visibility when overriding. -- JamesReply to Walter,Looks like something different.I didn't invalidate it because it is a bug. The lookup thing was a debate about whether Java-style or C++-style overriding is done. D does C++ style overriding.Is it? VS complains if there is any question, I haven't checked GCC. Or are you talking about something different.
Jan 26 2007
James Dennett wrote:Could you maybe explain what you mean by "C++-style overriding" as opposed to Java-style overriding? In what way is D more like C++ here than like Java?In C++, overriding one function in a base class prevents use of any of the base class function's overloads. In Java, you can override one overload and inherit the rest.
Jan 26 2007
Walter Bright wrote:James Dennett wrote:OK; I'd say this is a name lookup difference, not a difference in how overriding is done. In C++ it doesn't prevent the use of the base class function's overloads: they can still be used if you name their scope, or if you bring them into the scope of the derived class with a using declaration. (It certainly does prevent certain idiomatic uses of those base class functions unless you add a using declaration. It turns out to generally be a bad idea to overload virtual functions for design reasons unrelated to the specifics of a particular language though, so this isn't much of an issue in practice.) In Java, lookup is done by signature, not just name, and there's no such thing as "hiding" of a name in a base class by a name in a derived class. Both systems have their pros and cons, and D could reasonably resemble either, so long as the choice "feels" consistent with other decisions in the design of D. -- JamesCould you maybe explain what you mean by "C++-style overriding" as opposed to Java-style overriding? In what way is D more like C++ here than like Java?In C++, overriding one function in a base class prevents use of any of the base class function's overloads. In Java, you can override one overload and inherit the rest.
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:Please pardon the delay, and the length of this post ... here's a test: extern (C) int printf (char*, ...); class Foo { void write (int x) {printf("int\n");} void write (uint x) {printf("uint\n");} void write (long x) {printf("long\n");} void write (char x) {printf("char\n");} void write (wchar x) {printf("wchar\n");} void write (double x) {printf("double\n");} void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} } void main() { auto foo = new Foo; foo.write ('c'); foo.write (1); foo.write (1u); foo.write (3.14); //foo.write ("asa"); } prints: char int uint double DMD has actually become smarter than the last time I tried something like this: it manages to select the correct overload for 'c' whereas before it couldn't decide whether int or uint was a better match for the char instead. This is good. It seems clear from the above that D /defaults/ the type of character, and undecorated integers, to something appropriate? In the above case 'c' is defaulted to char, rather than wchar, for example. The undecorated int constant is defaulted to int, rather than uint or long. This is good. Now for the broken part. When you uncomment the string constant, the compiler gets all confused about whether it's a char[] or wchar[]. There is no defaulting to one type, as there is for other constants (such as char). It /is/ possible to decorate the string constant in a similar manner to decorating integer constants: foo.write ("qwe"c); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants. Things start to go south when using templates with string constants. For example, take this template sig: uint locate(T) (T[] source, T match, uint start=0) This is intended to handle types of char[], wchar[] and dchar[]. There's a uint on the end, as opposed to an int. Suppose I call it like this: locate ("abc", "ab", 1); we get a compile error, since the int-constant does not match a uint in the sig (IFTI currently needs exact sig matches). In order to get around this, we wrap the template with a few functions: uint locate (char[] source, char[] match, uint start=0) { return locateT!(char) (source, match, start); } uint locate (wchar[] source, wchar[] match, uint start=0) { return locateT!(wchar) (source, match, start); } and dchar too. Now we call it: locate ("abc", "ab", 1); Well, the int/uint error goes away (since function matching operates differently than IFTI matching), but we've now got our old friend back again -- the constant char[], wchar[], dchar[] mismatch problem. How about another type of template? Here's one that does some simply text processing: T[] layout(T) (T[] output, T[] format, T[][] subs...) This has an output buffer, a format string, and a set of optional args; all of the same type. If I call it like so: char[128] tmp; char[] world = "world"; layout (tmp, "hello %1", world); that compiles ok. If I use wchar[] instead, it doesn't compile: wchar[128] tmp; wchar[] world = "world"; layout (tmp, "hello %1", world); In this case, the constant string used for formatting remains as a char[], so the template match fails (args: wchar[], char[], wchar[]) However, if I change the template signature to this instead: T[] layout(T) (T[] output, T[][] subs...) then everything works the way I want it to, but the design is actually wrong (the format string is now not required). String constants can be a royal PITA. inout ----- Since you're working on inout also, I'd like to ask what the plan is relating to a couple of examples. Tango uses this style of call quite regularly: get (inout int x); get (inout char[] x); etc. This is a clean way to pass-by-reference, instead of dealing with all the pointer syntax. I sure hope D will retain reference semantics like this, in some form? One current problem with inout, which you might not be aware of, is with regard to const structs. I need to pass structs by reference, because I don't want to pass them by value. Applying inout is the mechanism for describing this: struct Bar {int a, b;} Bar b = {1, 2}; void parf (inout Bar x) {} void main() { parf (b); } That all works fine. However, when I want to make those structs /const/ instead, I cannot use inout since it has mutating semantics: I get a compile error to that effect: const Bar b = {1, 2};Andrei Alexandrescu (See Website For Email) wrote:Could you give more detail? AndreiThat being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on.Aye, and that is certainly appreciated. Some of the items you've mentioned recently have long been festering sores for many. It'll certainly be refreshing to see some attention focused upon those. Regarding implicit conversion rules -- are you including the "issues" currently surrounding signature-matching when using constants?That is, there's no supported way to pass a const struct by reference. The response from Walter in the past has been "just use a pointer instead" ... well, yes I could do that. But it appears to be indicative of a problem with the language design? Why do I want to use const? Well, the data held therein is for reference only, and (via some future D vendor) I want that reference data placed into a ROM segment. I do a lot of work with MCUs, and this sort of thing is a common requirement. Cheers; - KrisError: cannot modify const variable 'b'
Jan 25 2007
kris wrote: [about implicit conversion rules]extern (C) int printf (char*, ...); class Foo { void write (int x) {printf("int\n");} void write (uint x) {printf("uint\n");} void write (long x) {printf("long\n");} void write (char x) {printf("char\n");} void write (wchar x) {printf("wchar\n");} void write (double x) {printf("double\n");} void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} } void main() { auto foo = new Foo; foo.write ('c'); foo.write (1); foo.write (1u); foo.write (3.14); //foo.write ("asa"); } prints: char int uint double DMD has actually become smarter than the last time I tried something like this: it manages to select the correct overload for 'c' whereas before it couldn't decide whether int or uint was a better match for the char instead. This is good. It seems clear from the above that D /defaults/ the type of character, and undecorated integers, to something appropriate? In the above case 'c' is defaulted to char, rather than wchar, for example. The undecorated int constant is defaulted to int, rather than uint or long. This is good.Yup. So far so good.Now for the broken part. When you uncomment the string constant, the compiler gets all confused about whether it's a char[] or wchar[]. There is no defaulting to one type, as there is for other constants (such as char). It /is/ possible to decorate the string constant in a similar manner to decorating integer constants: foo.write ("qwe"c); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants.I talked to Walter about this and it's not a bug, it's a feature :o). Basically it's hard to decide what to do with an unadorned string when both wchar[] and char[] would want to "attract" it. I understand you're leaning towards defaulting to char[]? Then probably others will be unhappy.Things start to go south when using templates with string constants. For example, take this template sig: uint locate(T) (T[] source, T match, uint start=0) This is intended to handle types of char[], wchar[] and dchar[]. There's a uint on the end, as opposed to an int. Suppose I call it like this: locate ("abc", "ab", 1); we get a compile error, since the int-constant does not match a uint in the sig (IFTI currently needs exact sig matches). In order to get around this, we wrap the template with a few functions: uint locate (char[] source, char[] match, uint start=0) { return locateT!(char) (source, match, start); } uint locate (wchar[] source, wchar[] match, uint start=0) { return locateT!(wchar) (source, match, start); } and dchar too. Now we call it: locate ("abc", "ab", 1); Well, the int/uint error goes away (since function matching operates differently than IFTI matching), but we've now got our old friend back again -- the constant char[], wchar[], dchar[] mismatch problem.I think a sound solution to this should be found. It's kind of hard, because char[] is the worst match but also probably the most used one. The most generous match is dchar[] but wastes much time and space for the minority of cases in which it's useful.How about another type of template? Here's one that does some simply text processing: T[] layout(T) (T[] output, T[] format, T[][] subs...) This has an output buffer, a format string, and a set of optional args; all of the same type. If I call it like so: char[128] tmp; char[] world = "world"; layout (tmp, "hello %1", world); that compiles ok. If I use wchar[] instead, it doesn't compile: wchar[128] tmp; wchar[] world = "world"; layout (tmp, "hello %1", world); In this case, the constant string used for formatting remains as a char[], so the template match fails (args: wchar[], char[], wchar[]) However, if I change the template signature to this instead: T[] layout(T) (T[] output, T[][] subs...) then everything works the way I want it to, but the design is actually wrong (the format string is now not required). String constants can be a royal PITA.Color me convinced. :o) I have no bright ideas on solving it though.inout ----- Since you're working on inout also, I'd like to ask what the plan is relating to a couple of examples. Tango uses this style of call quite regularly: get (inout int x); get (inout char[] x); etc. This is a clean way to pass-by-reference, instead of dealing with all the pointer syntax. I sure hope D will retain reference semantics like this, in some form?It will, and in the same form.One current problem with inout, which you might not be aware of, is with regard to const structs. I need to pass structs by reference, because I don't want to pass them by value. Applying inout is the mechanism for describing this: struct Bar {int a, b;} Bar b = {1, 2}; void parf (inout Bar x) {} void main() { parf (b); } That all works fine. However, when I want to make those structs /const/ instead, I cannot use inout since it has mutating semantics: I get a compile error to that effect: const Bar b = {1, 2}; >> Error: cannot modify const variable 'b' That is, there's no supported way to pass a const struct by reference. The response from Walter in the past has been "just use a pointer instead" ... well, yes I could do that. But it appears to be indicative of a problem with the language design?This case is on the list. You will definitely have a sane and simple way to pass const structs by reference, while having a guarantee that they can't be changed by the callee.Why do I want to use const? Well, the data held therein is for reference only, and (via some future D vendor) I want that reference data placed into a ROM segment. I do a lot of work with MCUs, and this sort of thing is a common requirement.I agree. Andrei
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote: [about implicit conversion rules]You'll have noticed that the constant 'c' defaults to /char/, and that there's no compile-time conflict between the write(char) & write(wchar)? Are people unhappy about that too? Perhaps defaulting of char constants and int constants should be abolished also? I just want the compiler to be consistent, and it's quite unlikely that I'm alone in that regard -- consistency is a very powerful tool. Besides, aren't so-called 'features' actually bugs, coyly renamed by the marketing department? :) BTW: if you remove the write(char) overload, the compiler says it doesn't know which of int/uint overloads to select for 'c', and completely ignores write(wchar) as an viable option. That seems reasonable, but it clearly shows that a char constant is being defaulted to type char; and it's vaguely amusing in a twisted manner <g>extern (C) int printf (char*, ...); class Foo { void write (int x) {printf("int\n");} void write (uint x) {printf("uint\n");} void write (long x) {printf("long\n");} void write (char x) {printf("char\n");} void write (wchar x) {printf("wchar\n");} void write (double x) {printf("double\n");} void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} } void main() { auto foo = new Foo; foo.write ('c'); foo.write (1); foo.write (1u); foo.write (3.14); //foo.write ("asa"); } prints: char int uint double DMD has actually become smarter than the last time I tried something like this: it manages to select the correct overload for 'c' whereas before it couldn't decide whether int or uint was a better match for the char instead. This is good. It seems clear from the above that D /defaults/ the type of character, and undecorated integers, to something appropriate? In the above case 'c' is defaulted to char, rather than wchar, for example. The undecorated int constant is defaulted to int, rather than uint or long. This is good.Yup. So far so good.Now for the broken part. When you uncomment the string constant, the compiler gets all confused about whether it's a char[] or wchar[]. There is no defaulting to one type, as there is for other constants (such as char). It /is/ possible to decorate the string constant in a similar manner to decorating integer constants: foo.write ("qwe"c); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants.I talked to Walter about this and it's not a bug, it's a feature :o). Basically it's hard to decide what to do with an unadorned string when both wchar[] and char[] would want to "attract" it. I understand you're leaning towards defaulting to char[]? Then probably others will be unhappy.In the FWIW department, after writing several truckloads of text-oriented library code and wrappers for external text-processing libs, I've reached a simple conclusion: utf8 is where it's at for ~80% of code written, IMO. There's probably a ~15% need to go to utf32 for serious text handling (Word Processor, etc), and utf16 is the half-way house that the remaining percentage resort to when compromising (such as when stuffing things into ROM). Before the flames rise up and engulf that claim, Let's consider one major exclusion: certain GUI APIs use utf16 throughout. What the heck does one do in that situation if the compiler defaults strings-constants to char[] instead of wchar[]? Well, it's actually no issue at all since those APIs typically don't have method overloads for char/dchar also. They have only utf16 signatures instead, for any given method name, because they only deal in utf16. Thus, the compiler can happily morph a string constant to a wchar[] instead of the default -- *just as it happily does today* Let's also keep in mind we're talking string constants only, rather than all strings. I'll just try not to harp on about the consistency mismatch between char & char[] constants any more than I have done already.Things start to go south when using templates with string constants. For example, take this template sig: uint locate(T) (T[] source, T match, uint start=0) This is intended to handle types of char[], wchar[] and dchar[]. There's a uint on the end, as opposed to an int. Suppose I call it like this: locate ("abc", "ab", 1); we get a compile error, since the int-constant does not match a uint in the sig (IFTI currently needs exact sig matches). In order to get around this, we wrap the template with a few functions: uint locate (char[] source, char[] match, uint start=0) { return locateT!(char) (source, match, start); } uint locate (wchar[] source, wchar[] match, uint start=0) { return locateT!(wchar) (source, match, start); } and dchar too. Now we call it: locate ("abc", "ab", 1); Well, the int/uint error goes away (since function matching operates differently than IFTI matching), but we've now got our old friend back again -- the constant char[], wchar[], dchar[] mismatch problem.I think a sound solution to this should be found. It's kind of hard, because char[] is the worst match but also probably the most used one. The most generous match is dchar[] but wastes much time and space for the minority of cases in which it's useful.Perhaps a change to a default type might take care of it? After all, this particular issue is specific to string-constants only; not for other types (such as char/int/long/float). Certainly worth a try, one would think?How about another type of template? Here's one that does some simply text processing: T[] layout(T) (T[] output, T[] format, T[][] subs...) This has an output buffer, a format string, and a set of optional args; all of the same type. If I call it like so: char[128] tmp; char[] world = "world"; layout (tmp, "hello %1", world); that compiles ok. If I use wchar[] instead, it doesn't compile: wchar[128] tmp; wchar[] world = "world"; layout (tmp, "hello %1", world); In this case, the constant string used for formatting remains as a char[], so the template match fails (args: wchar[], char[], wchar[]) However, if I change the template signature to this instead: T[] layout(T) (T[] output, T[][] subs...) then everything works the way I want it to, but the design is actually wrong (the format string is now not required). String constants can be a royal PITA.Color me convinced. :o) I have no bright ideas on solving it though.Grand!inout ----- Since you're working on inout also, I'd like to ask what the plan is relating to a couple of examples. Tango uses this style of call quite regularly: get (inout int x); get (inout char[] x); etc. This is a clean way to pass-by-reference, instead of dealing with all the pointer syntax. I sure hope D will retain reference semantics like this, in some form?It will, and in the same form.Praise the lord !One current problem with inout, which you might not be aware of, is with regard to const structs. I need to pass structs by reference, because I don't want to pass them by value. Applying inout is the mechanism for describing this: struct Bar {int a, b;} Bar b = {1, 2}; void parf (inout Bar x) {} void main() { parf (b); } That all works fine. However, when I want to make those structs /const/ instead, I cannot use inout since it has mutating semantics: I get a compile error to that effect: const Bar b = {1, 2}; >> Error: cannot modify const variable 'b' That is, there's no supported way to pass a const struct by reference. The response from Walter in the past has been "just use a pointer instead" ... well, yes I could do that. But it appears to be indicative of a problem with the language design?This case is on the list. You will definitely have a sane and simple way to pass const structs by reference, while having a guarantee that they can't be changed by the callee.Cheers; this kind of detailed reply (above) is very much appreciated - KrisWhy do I want to use const? Well, the data held therein is for reference only, and (via some future D vendor) I want that reference data placed into a ROM segment. I do a lot of work with MCUs, and this sort of thing is a common requirement.I agree. Andrei
Jan 26 2007
kris wrote:Andrei Alexandrescu (See Website For Email) wrote:[snip]kris wrote: [about implicit conversion rules]extern (C) int printf (char*, ...); class Foo { void write (int x) {printf("int\n");} void write (uint x) {printf("uint\n");} void write (long x) {printf("long\n");} void write (char x) {printf("char\n");} void write (wchar x) {printf("wchar\n");} void write (double x) {printf("double\n");} void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} } void main() { auto foo = new Foo; foo.write ('c'); foo.write (1); foo.write (1u); foo.write (3.14); //foo.write ("asa"); } prints: char int uint doubleIt's a bit more complicated with character literals than just defaulting to 'char': ----- import std.stdio; void main() { writefln(typeid(typeof('a'))); // c <= \u007f writefln(typeid(typeof('\uabcd'))); // c <= \uffff writefln(typeid(typeof('\U000abcde'))); // c <= \U0010ffff } ----- outputs: """ char wchar dchar """ So it defaults to the *smallest* character type that can hold it in one element. Pretty cool, actually. This also applies to other types, by the way. If you type an integer literal that won't fit into an 'int', it'll be a 'long' constant (assuming it fits), not an 'int' constant. Perhaps we should do something similar with string literals, defaulting it to use an array of the smallest character type that can hold all of the characters in the string (i.e. the maximum "character type" of the component characters) ? That seems like a reasonable rule. And it has the added benefit that "some string constant".length will always be the number of characters in the string.You'll have noticed that the constant 'c' defaults to /char/, and that there's no compile-time conflict between the write(char) & write(wchar)? Are people unhappy about that too? Perhaps defaulting of char constants and int constants should be abolished also?Now for the broken part. When you uncomment the string constant, the compiler gets all confused about whether it's a char[] or wchar[]. There is no defaulting to one type, as there is for other constants (such as char). It /is/ possible to decorate the string constant in a similar manner to decorating integer constants: foo.write ("qwe"c); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants.I talked to Walter about this and it's not a bug, it's a feature :o). Basically it's hard to decide what to do with an unadorned string when both wchar[] and char[] would want to "attract" it. I understand you're leaning towards defaulting to char[]? Then probably others will be unhappy.
Jan 27 2007
Frits van Bommel wrote:kris wrote:Yes. Kinda glossed over that part, didn't I <g>Andrei Alexandrescu (See Website For Email) wrote:[snip]kris wrote: [about implicit conversion rules]extern (C) int printf (char*, ...); class Foo { void write (int x) {printf("int\n");} void write (uint x) {printf("uint\n");} void write (long x) {printf("long\n");} void write (char x) {printf("char\n");} void write (wchar x) {printf("wchar\n");} void write (double x) {printf("double\n");} void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} } void main() { auto foo = new Foo; foo.write ('c'); foo.write (1); foo.write (1u); foo.write (3.14); //foo.write ("asa"); } prints: char int uint doubleIt's a bit more complicated with character literals than just defaulting to 'char': ----- import std.stdio; void main() { writefln(typeid(typeof('a'))); // c <= \u007f writefln(typeid(typeof('\uabcd'))); // c <= \uffff writefln(typeid(typeof('\U000abcde'))); // c <= \U0010ffff } ----- outputs: """ char wchar dchar """ So it defaults to the *smallest* character type that can hold it in one element. Pretty cool, actually. This also applies to other types, by the way. If you type an integer literal that won't fit into an 'int', it'll be a 'long' constant (assuming it fits), not an 'int' constant.You'll have noticed that the constant 'c' defaults to /char/, and that there's no compile-time conflict between the write(char) & write(wchar)? Are people unhappy about that too? Perhaps defaulting of char constants and int constants should be abolished also?Now for the broken part. When you uncomment the string constant, the compiler gets all confused about whether it's a char[] or wchar[]. There is no defaulting to one type, as there is for other constants (such as char). It /is/ possible to decorate the string constant in a similar manner to decorating integer constants: foo.write ("qwe"c); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants.I talked to Walter about this and it's not a bug, it's a feature :o). Basically it's hard to decide what to do with an unadorned string when both wchar[] and char[] would want to "attract" it. I understand you're leaning towards defaulting to char[]? Then probably others will be unhappy.Perhaps we should do something similar with string literals, defaulting it to use an array of the smallest character type that can hold all of the characters in the string (i.e. the maximum "character type" of the component characters) ? That seems like a reasonable rule. And it has the added benefit that "some string constant".length will always be the number of characters in the string.Yes, that would seem eminently reasonable.
Jan 27 2007
Frits van Bommel wrote:So it defaults to the *smallest* character type that can hold it in one element. Pretty cool, actually. This also applies to other types, by the way. If you type an integer literal that won't fit into an 'int', it'll be a 'long' constant (assuming it fits), not an 'int' constant. Perhaps we should do something similar with string literals, defaulting it to use an array of the smallest character type that can hold all of the characters in the string (i.e. the maximum "character type" of the component characters) ?And while you're at it do the same for integer and float literals -- use the "best" type considering _all_ the elements rather than just the first one so that this doesn't mean an array with two 1 integers. [1, 1.5]; --bb
Jan 27 2007
Frits van Bommel wrote:kris wrote:A slight inconsistency here is that: foo("test"); gives a syntax error, while: auto t = "test"; foo(t); compiles and selects the char[] overload. Also: void bar(T)(T x) { foo(x); } ... bar("test"); compiles and selects the char[] overload.Andrei Alexandrescu (See Website For Email) wrote:kris wrote: [about implicit conversion rules]void write (char[] x) {printf("char[]\n");} void write (wchar[] x) {printf("wchar[]\n");} //foo.write ("asa"); And this, of course, compiles. It's a PITA though, and differs from the rules for other constants.I talked to Walter about this and it's not a bug, it's a feature :o). Basically it's hard to decide what to do with an unadorned string when both wchar[] and char[] would want to "attract" it. I understand you're leaning towards defaulting to char[]? Then probably others will be unhappy.It's a bit more complicated with character literals than just defaulting to 'char':[snip typeof('e') == char, typeof('é') == wchar, ...]]So it defaults to the *smallest* character type that can hold it in one element. Pretty cool, actually.A problem related to this, that might actually warrant a bug report, is that given: char[] str = "abcd"; str ~= 'e'; writefln(str); Is OK, while str ~= 'é'; writefln(str); Generates an invalid UTF-8 string ant gives the runtime "Error: 4invalid UTF-8 sequence" It seems like the wchar 'é' gets int-promoted to 0xE9 that is different from the char[] (utf-8) representation of "é", that would be x"C3 A9". -- /Oskar
Jan 29 2007
Oskar Linde wrote:Frits van Bommel wrote:Yes, that probably warrants a bug report. Either an error should be produced (something like "Invalid operation: str ~= 'é', cannot concatenate wchar to char[]"), or (preferably) it should just work. The latter can (for example) be implemented by doing the equivalent of std.utf.encode(str, 'é'), or the equivalent of str ~= "é" (note the quotes, implicitly converting it to char[] instead of wchar).It's a bit more complicated with character literals than just defaulting to 'char':[snip typeof('e') == char, typeof('é') == wchar, ...]]So it defaults to the *smallest* character type that can hold it in one element. Pretty cool, actually.A problem related to this, that might actually warrant a bug report, is that given: char[] str = "abcd"; str ~= 'e'; writefln(str); Is OK, while str ~= 'é'; writefln(str); Generates an invalid UTF-8 string ant gives the runtime "Error: 4invalid UTF-8 sequence" It seems like the wchar 'é' gets int-promoted to 0xE9 that is different from the char[] (utf-8) representation of "é", that would be x"C3 A9".
Jan 29 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:I suppose this is where the difference of opinion comes in, but why do you think char[] is the worst match? From what I understand, UTF-8 seems preferable to UTF-16 in most cases. The algorithm for dealing with such strings must be essentially the same in both cases, and UTF-8 tends to be more compact on average. SeanNow we call it: locate ("abc", "ab", 1); Well, the int/uint error goes away (since function matching operates differently than IFTI matching), but we've now got our old friend back again -- the constant char[], wchar[], dchar[] mismatch problem.I think a sound solution to this should be found. It's kind of hard, because char[] is the worst match but also probably the most used one. The most generous match is dchar[] but wastes much time and space for the minority of cases in which it's useful.
Jan 27 2007
Andrei Alexandrescu (See Website For Email) wrote:kris wrote:...Andrei Alexandrescu (See Website For Email) wrote: [snip]...3. I want to increase community's awareness of the importance of those tests for larger use cases, as well as gather ideas from the community about how to overcome D's shortcomings in passing the tests.That being said, my view on what should be fixed in D is a bit different. For example, as I said, one of my main foci is putting a lead sarcophagus around "inout" before its radioactive waste affects too much code that will become broken later. Also, implicit conversion rules will change, and again I want to get that in ASAP so not too much code suffers. And so on....AndreiWell, since you mention it... I'm not positive that I'm thinking of the same type problem that you are, but when writing templates in D, I've wanted something like storageof(T) capabilities, but the way I imagine it, the ideal solution would be a bit different. (Actually, I'm not sure exactly what problem inout causes that you are referring to as requiring lead, but moving on..) I would prefer for the types passed to a template to carry a lot more info across, and let the user decide which parts to throw away. You could call this a "strict" template, if you want backward compatibility (but it's early in D's template design so maybe we don't care). I'm thinking that this (current) syntax: template foo(T) { T plus(T x, T y) { return x + y; } } would be equivalent to: strict template foo(T) { T.value plus(T.value x, T.value y) { return x + y; } } In a 'strict' template the type "T" would carry a lot of baggage. It would know if T was constant (as in a local "const int x = 10"), or a constant view (maybe) whether it is an lvalue, rvalue, literal, known-at-compile-time, static array, result of arithmetic, etc. I couldn't tell you the actual names and definitions of all of these attributes -- I imagine they could be added one at a time as the need arose, starting with an equivalent for "storageof". [OT: I personally don't like the name storageof() because 'storage class' doesn't connote 'inout' versus 'in' to me, I don't really think of those as storage. I guess it comes from the idea of storing const items in R/O memory?) What we normally think of as the "type" is called T.value in a strict template. If you use "T" you get a whole lot more -- in fact, we might want to force the user to say "T.all" in this case. In my view, what we have now, is a leaky abstraction - you get some type info but not a lot. When you pass T into a template, you lose bits and pieces of what makes T work. There would be two ways to manage the template definition - one is to get out each piece of 'type modification' data that you want to copy to the new type, using something like: If T is "const long" (but not lazy), then: int foo(T.constness T.lazyness T.value x) (becomes: int foo(const long x)) Alternately, you could let users specify things like "in T" and the "in" overrides the "inout" if there was one. Kevin
Jan 26 2007
Kevin Bealer wrote:I would prefer for the types passed to a template to carry a lot more info across, and let the user decide which parts to throw away. You could call this a "strict" template, if you want backward compatibility (but it's early in D's template design so maybe we don't care). I'm thinking that this (current) syntax: template foo(T) { T plus(T x, T y) { return x + y; } } would be equivalent to: strict template foo(T) { T.value plus(T.value x, T.value y) { return x + y; } } In a 'strict' template the type "T" would carry a lot of baggage. It would know if T was constant (as in a local "const int x = 10"), or a constant view (maybe) whether it is an lvalue, rvalue, literal, known-at-compile-time, static array, result of arithmetic, etc. I couldn't tell you the actual names and definitions of all of these attributes -- I imagine they could be added one at a time as the need arose, starting with an equivalent for "storageof".[snip] These are great points. The direction I hope to steer things in would be to (backward-compatibly) continue matching types "lossily", but to also give you the ability to pattern-match the extra info when you want. The syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage". Andrei
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:The syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage".Would it also be possible to 'cherry-pick' attributes? So that e.g something like S.constness expands to either 'const' or ''? And would this mean 'raw' storage attributes would be valid template parameters? So that something like foo!(const) would be valid syntax?
Jan 26 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote:This gets into something that would be nice to have in D: some sort of concept checking. The easiest method would be something like this: alias Tuple!(int,char) A; void fnA( T in A )( T val ) {} void fnB( T : is( T == int ) || is( T == char ) )( T val ) {} The ':' currently suggests type matching so perhaps the operator would need to be changes. An alternate for the above which would work is: template fnC( T ) in { this = is( T == int ) || is( T == char ); } body { } Which would be equivalent to: template fnC( T, bool match : true = Test!(T) ) { } template Test( T ) { const Test = is( T == int ) || is( T == char ); } The problem is that it's awkward to overload templates on concepts now--the C++ approach is just about the only way. However, since templates can not be overloaded across module boundaries, this is also currently an option: template fnD( T ) { static if( is( T == int ) || is( T == char ) ) { ... } else { static assert( false ); } } It works just fine, but requires all overloads to exist within the same template block, and this isn't terribly readable with a large number of conditions. SeanThe syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage".Would it also be possible to 'cherry-pick' attributes? So that e.g something like S.constness expands to either 'const' or ''? And would this mean 'raw' storage attributes would be valid template parameters? So that something like foo!(const) would be valid syntax?
Jan 26 2007
Frits van Bommel wrote:Andrei Alexandrescu (See Website For Email) wrote:You will be able to cherry-pick with "is" tests. Walter is opposed to manipulating the raw storage attributes. AndreiThe syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage".Would it also be possible to 'cherry-pick' attributes? So that e.g something like S.constness expands to either 'const' or ''? And would this mean 'raw' storage attributes would be valid template parameters? So that something like foo!(const) would be valid syntax?
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:Frits van Bommel wrote:If you have to cherry pick with "is" tests to separate the bits out of the S above, then why not just use "is" tests to pick the S info off the type to begin with? --bbAndrei Alexandrescu (See Website For Email) wrote:You will be able to cherry-pick with "is" tests. Walter is opposed to manipulating the raw storage attributes.The syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage".Would it also be possible to 'cherry-pick' attributes? So that e.g something like S.constness expands to either 'const' or ''? And would this mean 'raw' storage attributes would be valid template parameters? So that something like foo!(const) would be valid syntax?
Jan 26 2007
Bill Baxter wrote:Andrei Alexandrescu (See Website For Email) wrote:Because just 'S' is much shorter when you don't want to cherry-pick?You will be able to cherry-pick with "is" tests. Walter is opposed to manipulating the raw storage attributes.If you have to cherry pick with "is" tests to separate the bits out of the S above, then why not just use "is" tests to pick the S info off the type to begin with?
Jan 27 2007
Frits van Bommel wrote:Bill Baxter wrote:Just wondering if there are that many cases where all want to do is pick off S without 'deconstructing' it further. Especially given Andrei's statement "there are lots of things that could go under the vague heading of storage class", it seems like it may be rare that you can actually just use S as-is without picking out the particular attributes you care about -- const or inout or any one of the 'lots of other things'. And if that's the case then maybe the benefits of having the "template foo(S T)(S T x)" syntax are not so huge. You could get S using alias storageof(T) S in the first line of the template instead. Or lets say you just want to use the constness part of S, which seems like a fairly common thing to want. Then you'll still need something like: constof(S) template foo(S T)(S T v) { ... } But if you have to say constof(S) you might as well just make constof operate on T directly instead. constof(T) template foo(T)(T v) { ... } I'm willing to be convinced, but so far it's not clear to me that "template xx(S T)" would be all that useful in practice. --bbAndrei Alexandrescu (See Website For Email) wrote:Because just 'S' is much shorter when you don't want to cherry-pick?You will be able to cherry-pick with "is" tests. Walter is opposed to manipulating the raw storage attributes.If you have to cherry pick with "is" tests to separate the bits out of the S above, then why not just use "is" tests to pick the S info off the type to begin with?
Jan 27 2007
Andrei Alexandrescu (See Website For Email) wrote:Kevin Bealer wrote:Interesting. So I suppose I could explicitly instantiate the template as: int i; foo!(const)( i ); If so, this seems great.I would prefer for the types passed to a template to carry a lot more info across, and let the user decide which parts to throw away. You could call this a "strict" template, if you want backward compatibility (but it's early in D's template design so maybe we don't care). I'm thinking that this (current) syntax: template foo(T) { T plus(T x, T y) { return x + y; } } would be equivalent to: strict template foo(T) { T.value plus(T.value x, T.value y) { return x + y; } } In a 'strict' template the type "T" would carry a lot of baggage. It would know if T was constant (as in a local "const int x = 10"), or a constant view (maybe) whether it is an lvalue, rvalue, literal, known-at-compile-time, static array, result of arithmetic, etc. I couldn't tell you the actual names and definitions of all of these attributes -- I imagine they could be added one at a time as the need arose, starting with an equivalent for "storageof".[snip] These are great points. The direction I hope to steer things in would be to (backward-compatibly) continue matching types "lossily", but to also give you the ability to pattern-match the extra info when you want. The syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { }So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage".Definitely. Sean
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:Kevin Bealer wrote:I like this; does this mean that when declaring an instance, you would do something like this? Or do the 'const' parts get deduced? alias Foo!(const, int) TheFoo; KevinI would prefer for the types passed to a template to carry a lot more info across, and let the user decide which parts to throw away. You could call this a "strict" template, if you want backward compatibility (but it's early in D's template design so maybe we don't care). I'm thinking that this (current) syntax: template foo(T) { T plus(T x, T y) { return x + y; } } would be equivalent to: strict template foo(T) { T.value plus(T.value x, T.value y) { return x + y; } } In a 'strict' template the type "T" would carry a lot of baggage. It would know if T was constant (as in a local "const int x = 10"), or a constant view (maybe) whether it is an lvalue, rvalue, literal, known-at-compile-time, static array, result of arithmetic, etc. I couldn't tell you the actual names and definitions of all of these attributes -- I imagine they could be added one at a time as the need arose, starting with an equivalent for "storageof".[snip] These are great points. The direction I hope to steer things in would be to (backward-compatibly) continue matching types "lossily", but to also give you the ability to pattern-match the extra info when you want. The syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage". Andrei
Jan 26 2007
Kevin Bealer wrote:Andrei Alexandrescu (See Website For Email) wrote:[about deducing storage types]The intent is to deduce the storage, but specifying it explicitly works just as well. There is strong resistance against my notation because manipulating the storage class separately is something entirely new, which means a lot of work in the compiler implementation. Also, S is not a type but a (new kind of) alias, which might confuse people who read: template Foo(S, T) { ... } and expect S and T to be types, just to discover in the body of the template that S is actually a qualifier. The strawman we're discussing now looks like this: void foo(auto T)(T t) { } meaning, T will match whatever you throw at foo, including storage: int a = 5; foo(a); // T == inout int foo(a + 1); // T == int Either way, one thing is clear - defining storage classes properly is a focal area. AndreiThe syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage". AndreiI like this; does this mean that when declaring an instance, you would do something like this? Or do the 'const' parts get deduced? alias Foo!(const, int) TheFoo;
Jan 26 2007
Andrei Alexandrescu (See Website For Email) wrote:Kevin Bealer wrote:So how would one declare such a type? typeof(T) perhaps? I imagine there must be some way to separate the storage class from the type for this to work. SeanAndrei Alexandrescu (See Website For Email) wrote:[about deducing storage types]The intent is to deduce the storage, but specifying it explicitly works just as well. There is strong resistance against my notation because manipulating the storage class separately is something entirely new, which means a lot of work in the compiler implementation. Also, S is not a type but a (new kind of) alias, which might confuse people who read: template Foo(S, T) { ... } and expect S and T to be types, just to discover in the body of the template that S is actually a qualifier. The strawman we're discussing now looks like this: void foo(auto T)(T t) { } meaning, T will match whatever you throw at foo, including storage: int a = 5; foo(a); // T == inout int foo(a + 1); // T == intThe syntax that I am proposing does away with storageof and is very much in spirit with the current D: S int foo(S, T)(S T t) { } It does exactly what it says it does, in a clear and terse manner, and is 100% within the spirit of existing D: * It's customized on symbols S and T * It's matching (by sheer position of S and T) the storage (i.e., all of the gooey fuzzy nice information about the argument passed) and the type of the incoming argument * In this example it uses the storage in the result type (e.g. const goes to const) * Inside the function can easily use either T separately or S T as a group that transports the storage around. You have total flexibility (and don't forget that juxtaposition is always easier than extraction). So far we're only thinking of storage-like attributes, but a good deal of information can be encoded under the loose declaration of "storage". AndreiI like this; does this mean that when declaring an instance, you would do something like this? Or do the 'const' parts get deduced? alias Foo!(const, int) TheFoo;
Jan 27 2007
"kris" <foo bar.com> wrote in message news:ep6c92$2ti3$1 digitaldaemon.com...Like, for instance, an updated GC? When did that ever top the list of real-world issues for D?I'm often running into problems with the (old!) GC, and the only fix is to manage memory manually here-n-there, until you end up with some mixed GC, malloc/free frankenstein code. I surely hope the new GC will solve my problems :) L.
Jan 23 2007
Lionello Lunesu wrote:"kris" <foo bar.com> wrote in message news:ep6c92$2ti3$1 digitaldaemon.com...Yeah, I agree. There's things that each of us would like resolved. For example, phobos deadlocks in the gc when using multiple threads - sometimes only beyond a day or two - and it's been that way since before last April. But, that affects only a select few people. In fact, it seems I'm the only one who managed to trip it (though Sean fixed it in both Tango & Ares). From another viewpoint, lib issues and debugger issues tend to affect the public in general; especially those new to the language, who may be used to the relative luxury of functional debuggers and libs :-D On one hand, I was bitchin' that priorities seem as crazy as the recent weather up north. On the other, I was relaying a subtle but serious concern about the direction of D, and its general daily usability. While I often bitch and gripe about the language and its various flaws, and it's clear that Walter and I can't agree on anything notable these days, I'll happily stick by the assertion that D has awesome potential (and will keep putting serious effort into improving the few areas that I /think/ I can contribute to). All the same, I suspect that now it's more a case of the "managment" of how D progresses which will be the deciding factor whether it lives or dies by the corporate hand (phew! There's a run-on). That's just an opinion, but it's what drove the response to Andrei's post, along with my mistaken belief he was tied in somewhat more deeply to the progression of D. My apologies for placing /that/ selection of real-world usability issues above the troubles you've been having with the GC ;) It was silly of me to even make mention of it. - KrisLike, for instance, an updated GC? When did that ever top the list of real-world issues for D?I'm often running into problems with the (old!) GC, and the only fix is to manage memory manually here-n-there, until you end up with some mixed GC, malloc/free frankenstein code. I surely hope the new GC will solve my problems :)
Jan 23 2007
kris wrote:Lionello Lunesu wrote:Could you explain further? Is this simply a problem with false positives or is there something else going on?"kris" <foo bar.com> wrote in message news:ep6c92$2ti3$1 digitaldaemon.com...Like, for instance, an updated GC? When did that ever top the list of real-world issues for D?I'm often running into problems with the (old!) GC, and the only fix is to manage memory manually here-n-there, until you end up with some mixed GC, malloc/free frankenstein code. I surely hope the new GC will solve my problems :)Yeah, I agree. There's things that each of us would like resolved. For example, phobos deadlocks in the gc when using multiple threads - sometimes only beyond a day or two - and it's been that way since before last April. But, that affects only a select few people. In fact, it seems I'm the only one who managed to trip it (though Sean fixed it in both Tango & Ares).For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much). And the Tango/Ares Thread code is a complete rewrite so I couldn't simply submit my version to Walter for inclusion in Phobos.From another viewpoint, lib issues and debugger issues tend to affect the public in general; especially those new to the language, who may be used to the relative luxury of functional debuggers and libs :-DI've gotten pretty used to debugging via code inspection and an occasional well-placed printf since discovering D, but the library issues are obviously a major point of concern with Tango in development. There are certainly far fewer of them then there were a year ago, but a few major stumbling blocks still exist. Sean
Jan 24 2007
"Sean Kelly" <sean f4.ca> wrote in message news:ep8a08$2t4o$1 digitaldaemon.com...Could you explain further? Is this simply a problem with false positives or is there something else going on?I'm afraid I'm not 100% sure. The memory use was definitely higher than it should be and there were quite a few memory leaks (false positives). The biggest problem however was not the memory use, but the fact that the GC would get slower and slower. This particular application was using mostly strings (big ones), so apart from the pointers to those strings, there werent' many pointers. L.
Jan 24 2007
Lionello Lunesu wrote:"Sean Kelly" <sean f4.ca> wrote in message news:ep8a08$2t4o$1 digitaldaemon.com...Ah, that is because all those strings are being scanned for pointers. The Tango GC that keys on element size addresses this issue, and the new Phobos GC that uses TypeInfo does as well. For the record, Tango will be using TypeInfo just like Phobos as soon as the next DMD release is out (we're holding with 1.0 until the bugs are fixed). SeanCould you explain further? Is this simply a problem with false positives or is there something else going on?I'm afraid I'm not 100% sure. The memory use was definitely higher than it should be and there were quite a few memory leaks (false positives). The biggest problem however was not the memory use, but the fact that the GC would get slower and slower. This particular application was using mostly strings (big ones), so apart from the pointers to those strings, there werent' many pointers.
Jan 24 2007
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Sean Kelly schrieb am 2007-01-24: [...]For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ? Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFt7+fLK5blCcjpWoRAgKcAJ48NLoOIYlBQJr3ZiCMsCGJ2sggGwCgkNEZ pLO502XddDa5Rcmo9ZCmCHc= =gXEq -----END PGP SIGNATURE-----
Jan 24 2007
Thomas Kuehne wrote:-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Sean Kelly schrieb am 2007-01-24: [...]No. Would it work for a deadlock situation? SeanFor the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ?
Jan 24 2007
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Sean Kelly schrieb am 2007-01-24:Thomas Kuehne wrote:I think it should. Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFt+t1LK5blCcjpWoRAveqAJ9QPCTrhW0hinZvZc0YRUb7LS+FlACgrZrd g6XbkivwRylFQG09S5+HQWM= =MUkv -----END PGP SIGNATURE-----Sean Kelly schrieb am 2007-01-24: [...]No. Would it work for a deadlock situation?For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ?
Jan 24 2007
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thomas Kuehne schrieb am 2007-01-24:Sean Kelly schrieb am 2007-01-24: [...]The latest version contains a convenince function for gdb. Simply issue the gdb command "call print_trace ($ebp)" to print a stack trace. Sample session: [...] Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFt+kwLK5blCcjpWoRAuMEAJwJRj4bDV0iOT3AEPVc+iPECYLiMwCfeiFq qssaJ0ke1hFWvMCn6laWMco= =D9Ih -----END PGP SIGNATURE-----For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ?
Jan 24 2007
Thomas Kuehne wrote:-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thomas Kuehne schrieb am 2007-01-24:Very nice! I may just start doing all my D debugging on Linux. SeanSean Kelly schrieb am 2007-01-24: [...]The latest version contains a convenince function for gdb. Simply issue the gdb command "call print_trace ($ebp)" to print a stack trace. Sample session: [...]For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ?
Jan 24 2007
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thomas Kuehne schrieb am 2007-01-24:Sean Kelly schrieb am 2007-01-24: [...]The latest version contains a convenience function for gdb. Simply issue the gdb command "call print_trace ($ebp)" to print a stack trace. Sample session: [...] Thomas -----BEGIN PGP SIGNATURE----- iD8DBQFFt+odLK5blCcjpWoRApXOAKCm9jDp8HH0nCzUlLhaqQczcBYntACgrovA L0LcNxr4ciRizu9V21JPL+I= =/PIb -----END PGP SIGNATURE-----For the record, I read through the Phobos code looking for the problem but nothing jumped out at me (and the lack of a usable stack trace didn't help much).Have you tried Flectioned's Trace.getTrace() ?
Jan 24 2007
Thomas Kuehne wrote: [snip] Hey, another post from the future... (Please check your computer's clock)
Jan 24 2007