digitalmars.D - Generic code: autoconst, autopure, autonothrow
- dsimcha (23/23) Aug 28 2010 An issue that comes up very frequently when trying to use const, pure or
- Jonathan M Davis (5/33) Aug 28 2010 It certainly sounds good to me, though I'm probably not well enough vers...
- Andrei Alexandrescu (16/39) Aug 28 2010 Yah, with the growing interest in applying qualifiers at a larger scale
- dsimcha (14/28) Aug 28 2010 This may work well in Phobos because it's the standard library, so a lot...
- Rainer Deyke (11/18) Aug 28 2010 On one hand, this addresses a real need. On the other hand, D is
- dsimcha (9/25) Aug 28 2010 Two reasons:
- Rainer Deyke (10/24) Aug 28 2010 "It is incompatible with the archaic compilation model chosen by one D
- Nick Sabalausky (4/18) Aug 28 2010 I assume you mean "This special treatment of binary-only libraries has g...
- Jonathan M Davis (8/23) Aug 28 2010 Templates are instantiated when you use them. They can't work any other ...
- Rainer Deyke (16/21) Aug 29 2010 Why would that be a bad idea?
- Pelle (8/18) Aug 29 2010 I'm not sure I understand what you mean, but if you put pure on a
- Robert Jacques (5/20) Aug 29 2010 So... dynamic libraries (DLL's, etc.) are an archaic compilation model? ...
- dsimcha (11/33) Aug 29 2010 Sometimes I wonder if, once higher priority issues have been taken care ...
- Rainer Deyke (8/11) Aug 29 2010 Not necessarily, but it /is/ a special case. Dlls are best treated as
- dsimcha (6/8) Aug 28 2010 With regard to featuritis, I see your point, but IMHO without something ...
- Brad Roberts (13/40) Aug 28 2010 This really feels like a work-around rather than addressing the underlyi...
- bearophile (4/6) Aug 29 2010 I agree. On the other hand if you use multiprecision GNU numbers, your l...
- Justin Johansson (3/4) Aug 29 2010 Is the reason for this due to some optimization strategy to
- bearophile (4/6) Aug 29 2010 If each of your numbers need 500 MB of RAM, copying a number just to cha...
- =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= (20/78) Aug 30 2010 or
- =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= (5/7) Aug 30 2010 Eh, nevermind. popFront() must mutate the range so it can't be pure. Nee...
- Andrei Alexandrescu (8/13) Aug 30 2010 It can still be nothrow depending on input, which makes a solid point.
- Philippe Sigaud (40/56) Aug 30 2010 Could something like this be a possible building block for pure?
- =?iso-8859-2?B?VG9tZWsgU293afFza2k=?= (17/22) Aug 30 2010 But wait! It can be auto(nothrow). So my sleepy-head argument didn't suc...
- Peter Alexander (12/20) Aug 29 2010 I think the problem here is *not* that it's difficult to tell whether a
- dsimcha (6/28) Aug 29 2010 Part of the point of purity is to provide guarantees useful for multithr...
-
Simen kjaeraas
(17/17)
Aug 29 2010
dsimcha
wrote: - Andrej Mitrovic (16/22) Aug 29 2010 Couldn't you reuse the version statement for that? At least for a
- Simen kjaeraas (7/9) Aug 29 2010 Static if, at least. Still, that is the road to extreme code bloat.
- bearophile (4/7) Aug 29 2010 That's bad. I prefer a more flexible semantics, so @auto becomes one cas...
- Simen kjaeraas (5/11) Aug 29 2010 Please do elucidate. Does the second part of my post perhaps help define
- bearophile (13/16) Aug 29 2010 I am not able to design user-defined attributes in a short time all alon...
- Pillsy (13/36) Aug 30 2010 [...]
- dsimcha (10/46) Aug 30 2010 My concern with proposals that require you to explicitly check specific ...
An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num < 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions. A pure function would be allowed to call an autopure function instantiated with a type that makes it pure, and similarly for const and nothrow. Taking the address of an instantiation of an autopure function would return a pure function pointer iff the instantiation was pure. If an autopure function tried to do anything impure other than call an impure function, ideally the result would not compile, since the function is falsely asserting that everything it does itself is pure. This is not an absolute requirement, though. I believe that this would massively simplify correctly using const/pure/nothrow in generic code, which currently is near impossible. Does this sound like it could feasibly be implemented and would work well?
Aug 28 2010
On Saturday 28 August 2010 18:29:02 dsimcha wrote:An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num < 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions. A pure function would be allowed to call an autopure function instantiated with a type that makes it pure, and similarly for const and nothrow. Taking the address of an instantiation of an autopure function would return a pure function pointer iff the instantiation was pure. If an autopure function tried to do anything impure other than call an impure function, ideally the result would not compile, since the function is falsely asserting that everything it does itself is pure. This is not an absolute requirement, though. I believe that this would massively simplify correctly using const/pure/nothrow in generic code, which currently is near impossible. Does this sound like it could feasibly be implemented and would work well?It certainly sounds good to me, though I'm probably not well enough versed on all the implications of const, nothrow, and pure to know exactly what effects this will have. Still, from where I sit, it sounds like a good idea. - Jonathan M Davis
Aug 28 2010
On 08/28/2010 08:29 PM, dsimcha wrote:An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num< 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions. A pure function would be allowed to call an autopure function instantiated with a type that makes it pure, and similarly for const and nothrow. Taking the address of an instantiation of an autopure function would return a pure function pointer iff the instantiation was pure. If an autopure function tried to do anything impure other than call an impure function, ideally the result would not compile, since the function is falsely asserting that everything it does itself is pure. This is not an absolute requirement, though. I believe that this would massively simplify correctly using const/pure/nothrow in generic code, which currently is near impossible. Does this sound like it could feasibly be implemented and would work well?Yah, with the growing interest in applying qualifiers at a larger scale (and making Phobos a good example of such) this is quite timely. I've been mulling myself over a similar proposal. What I had in mind is a bit more structured - it would allow selecting a type or an expression and assessing its constness/purity. That would be done just like .sizeof and .stringof work - by defining two more special members .constof, .pureof, .nothrowof. For example: T abs(T num) (-1 * num).pureof (-1 * num).nothrowof { return (num < 0) ? -1 * num : num; } The autoxxx stuff arguably makes this example and probably many others easier to write. What I fear in the case of autoxxx is that the compiler will be unable to decide cases in which there are cycles of autoxxx. Andrei
Aug 28 2010
== Quote from Andrei Alexandrescu (SeeWebsiteForEmail erdani.org)'s articleYah, with the growing interest in applying qualifiers at a larger scale (and making Phobos a good example of such) this is quite timely. I've been mulling myself over a similar proposal. What I had in mind is a bit more structured - it would allow selecting a type or an expression and assessing its constness/purity. That would be done just like .sizeof and .stringof work - by defining two more special members .constof, .pureof, .nothrowof. For example: T abs(T num) (-1 * num).pureof (-1 * num).nothrowof { return (num < 0) ? -1 * num : num; }This may work well in Phobos because it's the standard library, so a lot of effort could reasonably be put into making stuff like this right. For run of the mill generic code, though, this just seems too verbose. If this is what I had to write, about the only time I would use it is when hacking Phobos. I probably would even be too lazy to use it for my more specialized libraries like dstats. Also, how does it scale to the case where you need to evaluate the purity of multiple expressions to decide how to qualify a single function?The autoxxx stuff arguably makes this example and probably many others easier to write. What I fear in the case of autoxxx is that the compiler will be unable to decide cases in which there are cycles of autoxxx.Good point, though in practice I can't think of any time I've written or seen mutually recursive templated code. I guess the cycle-breaking logic could just treat the mutually recursive functions as effectively being one function. If all would compile when qualified with x, then all are x. Otherwise, none are x. Is it more complex than this for some reason I'm not thinking of? BTW, now that I think of it, I guess these proposals also apply to safe.
Aug 28 2010
On 8/28/2010 19:29, dsimcha wrote:Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions.On one hand, this addresses a real need. On the other hand, D is already has a serious case featuritis. Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".) Failing that, are the arguments for the inclusion of pure/nothrow/const really strong enough to justify all this extra cruft in the language? -- Rainer Deyke - rainerd eldwood.com
Aug 28 2010
== Quote from Rainer Deyke (rainerd eldwood.com)'s articleOn 8/28/2010 19:29, dsimcha wrote:Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library? 2. The modifiers are part of a contract and part of the public API. What if some function just happens to be pure now, but you consider that an implementation detail, not part of its specification? Client code may rely on this, not realizing it's an implementation detail. Then, when you make the function impure, your client code will break.Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions.On one hand, this addresses a real need. On the other hand, D is already has a serious case featuritis. Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".) Failing that, are the arguments for the inclusion of pure/nothrow/const really strong enough to justify all this extra cruft in the language?
Aug 28 2010
On 8/28/2010 22:33, dsimcha wrote:"It is incompatible with the archaic compilation model chosen by one D implementation." This special treatment of templates has got to end.Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".)Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library?2. The modifiers are part of a contract and part of the public API. What if some function just happens to be pure now, but you consider that an implementation detail, not part of its specification? Client code may rely on this, not realizing it's an implementation detail. Then, when you make the function impure, your client code will break.That may or may not be a compelling argument against always auto-detecting pure. It seems stronger as an argument against having pure as a language feature at all. (How can you know ahead of time that a logically pure function will remain physically pure?) -- Rainer Deyke - rainerd eldwood.com
Aug 28 2010
"Rainer Deyke" <rainerd eldwood.com> wrote in message news:i5cogl$31j9$1 digitalmars.com...On 8/28/2010 22:33, dsimcha wrote:I assume you mean "This special treatment of binary-only libraries has got to end"? If so, I agree."It is incompatible with the archaic compilation model chosen by one D implementation." This special treatment of templates has got to end.Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".)Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library?
Aug 28 2010
On Saturday 28 August 2010 21:42:49 Rainer Deyke wrote:On 8/28/2010 22:33, dsimcha wrote:Templates are instantiated when you use them. They can't work any other way. Normal functions are instantiated where they are declared. Unless you want to try and make it so that _all_ functions are instantiated where they are used (which IMHO seems like a really _bad_ idea), templates are and must be treated differently. There's no way around it. It was tried with exported templates in C++ and that idea was shown to be horribly broken. You just can't do it. - Jonathan M Davis"It is incompatible with the archaic compilation model chosen by one D implementation." This special treatment of templates has got to end.Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".)Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library?
Aug 28 2010
On 8/29/2010 00:32, Jonathan M Davis wrote:Templates are instantiated when you use them. They can't work any other way. Normal functions are instantiated where they are declared. Unless you want to try and make it so that _all_ functions are instantiated where they are used (which IMHO seems like a really _bad_ idea), templates are and must be treated differently.Why would that be a bad idea? You gain: - Consistency. - The ability to treat all libraries as "header-only", with no separate compilation. - Because there is no separate compilation, templated virtual functions. (Funny how that works out.) - Better global optimizations. You lose: - Binary libraries. (Which barely work anyway, because most of my functions are already templated.) - Potentially some (or even a lot of) compilation speed, and potentially not. Separate compilation introduces its own slow-downs. -- Rainer Deyke - rainerd eldwood.com
Aug 29 2010
On 08/29/2010 06:42 AM, Rainer Deyke wrote:On 8/28/2010 22:33, dsimcha wrote:I'm not sure I understand what you mean, but if you put pure on a function in your library, you're saying to whoever using it that it is and probably will always be pure. Also, if you put autopure on it implies that if the template input is pure in the right places, the function will be pure. If we remove pure, or make everything always autopure, this benefit disappears. Which is why I don't know if I get your point.2. The modifiers are part of a contract and part of the public API. What if some function just happens to be pure now, but you consider that an implementation detail, not part of its specification? Client code may rely on this, not realizing it's an implementation detail. Then, when you make the function impure, your client code will break.That may or may not be a compelling argument against always auto-detecting pure. It seems stronger as an argument against having pure as a language feature at all. (How can you know ahead of time that a logically pure function will remain physically pure?)
Aug 29 2010
On Sun, 29 Aug 2010 00:42:49 -0400, Rainer Deyke <rainerd eldwood.com> wrote:On 8/28/2010 22:33, dsimcha wrote:So... dynamic libraries (DLL's, etc.) are an archaic compilation model? Might you be able to suggest an newer alternative? (that doesn't involve a JIT, of course)"It is incompatible with the archaic compilation model chosen by one D implementation." This special treatment of templates has got to end.Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".)Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library?
Aug 29 2010
== Quote from Robert Jacques (sandford jhu.edu)'s articleOn Sun, 29 Aug 2010 00:42:49 -0400, Rainer Deyke <rainerd eldwood.com> wrote:Sometimes I wonder if, once higher priority issues have been taken care of and D is mainstream enough to support multiple dialects, we should create a D dialect for standalone programs. From a language specification point of view this would be basically a superset of D, but would disallow separate compilation, i.e. all non-extern(C) code for the entire project must be passed to the compiler in a single invocation. This would allow: 1. Using templates to add virtual functions to classes. 2. Compile-time introspection to get all descendants of a class. 3. Better whole-program optimization, because no ABI for this dialect of D would have to be specified, so the compiler could just do whatever is most efficient.On 8/28/2010 22:33, dsimcha wrote:So... dynamic libraries (DLL's, etc.) are an archaic compilation model? Might you be able to suggest an newer alternative? (that doesn't involve a JIT, of course)"It is incompatible with the archaic compilation model chosen by one D implementation." This special treatment of templates has got to end.Is there any real reason why we can't apply these modifiers automatically to all functions? (And by "real" I don't mean "it would be hard to do" or "it is incompatible with the archaic compilation model chosen by one D implementation".)Two reasons: 1. Unless the function is a template, the compiler isn't guaranteed to have the source available. What if it's a binary-only library?
Aug 29 2010
On 8/29/2010 08:44, Robert Jacques wrote:So... dynamic libraries (DLL's, etc.) are an archaic compilation model? Might you be able to suggest an newer alternative? (that doesn't involve a JIT, of course)Not necessarily, but it /is/ a special case. Dlls are best treated as separate programs communicating through C function calls. C function calls are used to achieve language neutrality. C function calls don't support pure and D-const, so the purity and constness of functions at the dll boundary is a moot point. -- Rainer Deyke - rainerd eldwood.com
Aug 29 2010
== Quote from Rainer Deyke (rainerd eldwood.com)'s articleOn one hand, this addresses a real need. On the other hand, D is already has a serious case featuritis.With regard to featuritis, I see your point, but IMHO without something like this pure and nothrow are the worst kind of extra features: Dead weight features. If they clash so severely with generic programming, then to a large extent they're useless. Very few people will use them if it means that they can't use D's metaprogramming capabilities or half of Phobos in the same code.
Aug 28 2010
On 8/28/2010 6:29 PM, dsimcha wrote:An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num < 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure. A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions. These would mean "everything I do is const/pure/nothrow as long as all of the functions I call are const/pure/nothrow". As far as I can tell, this would be reasonably implementable because the compiler always has the source code to template functions, unlike non-template functions. A pure function would be allowed to call an autopure function instantiated with a type that makes it pure, and similarly for const and nothrow. Taking the address of an instantiation of an autopure function would return a pure function pointer iff the instantiation was pure. If an autopure function tried to do anything impure other than call an impure function, ideally the result would not compile, since the function is falsely asserting that everything it does itself is pure. This is not an absolute requirement, though. I believe that this would massively simplify correctly using const/pure/nothrow in generic code, which currently is near impossible. Does this sound like it could feasibly be implemented and would work well?This really feels like a work-around rather than addressing the underlying problem. These annotations are defining the contract for the function. The fact that it's generic just means it's being flexible with the types, but that flexibility shouldn't extend to allowing the contract to be violated. Abs _should_ be pure. If the type being passed to it is incapable performing the algorithm purely, then the type isn't valid to be used with abs. Trying to say that abs should be pure sometimes and not others.. why bother having the pure requirement at all? Maybe it's a bad example, but let's not loose site of the reason pure exists. It's not just for the side effects, it's for the contract it defines as well. Later, Brad
Aug 28 2010
Brad Roberts:Abs _should_ be pure. If the type being passed to it is incapable performing the algorithm purely, then the type isn't valid to be used with abs.I agree. On the other hand if you use multiprecision GNU numbers, your large numbers aren't immutable, you are able to change their sign in-place. Is it an abs() or something different like inplaceAbs()? Bye, bearophile
Aug 29 2010
On 29/08/10 21:36, bearophile wrote:... if you use multiprecision GNU numbers, your large numbers aren't immutable, you are able to change their sign in-place.Is the reason for this due to some optimization strategy to avoid copying and changing the sign on the copy?
Aug 29 2010
Justin Johansson:Is the reason for this due to some optimization strategy to avoid copying and changing the sign on the copy?If each of your numbers need 500 MB of RAM, copying a number just to change its sign, and then free the original number is a significant waste of time. So it's an optimization offered by GMP. Bye, bearophile
Aug 29 2010
Dnia 29-08-2010 o 06:57:17 Brad Roberts <braddr puremagic.com> napisa=B3= (a):On 8/28/2010 6:29 PM, dsimcha wrote:orAn issue that comes up very frequently when trying to use const, pure==nothrow in generic code is lack of knowledge of whether the functions==you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num < 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't=pure and can't practically be made pure. A solution I propose is to allow the=annotations autoconst, autopure and autonothrow for template =l =functions. These would mean "everything I do is const/pure/nothrow as long as al==of the functions I call are const/pure/nothrow". As far as I can tell, this=would be reasonably implementable because the compiler always has the source =code to template functions, unlike non-template functions. A pure function would be allowed to call an autopure function ==instantiated with a type that makes it pure, and similarly for const and nothrow. =a =Taking the address of an instantiation of an autopure function would return=pure function pointer iff the instantiation was pure. If an autopure =ly =function tried to do anything impure other than call an impure function, ideal=tthe result would not compile, since the function is falsely asserting tha=everything it does itself is pure. This is not an absolute =requirement, though. I believe that this would massively simplify correctly using const/pure/nothrow in generic code, which currently is near =impossible. Does this sound like it could feasibly be implemented and would work well?=This really feels like a work-around rather than addressing the =underlying problem. These annotations are defining the contract for the function=. =The fact that it's generic just means it's being flexible with the types, ==but that flexibility shouldn't extend to allowing the contract to be violated. Abs _should_ be pure. If the type being passed to it is incapable =performing the algorithm purely, then the type isn't valid to be used with abs. Trying to say that abs should be pure sometimes and not others.. why =bother having the pure requirement at all? Maybe it's a bad example, but let's not loose site of the reason pure ==exists. It's not just for the side effects, it's for the contract it defines a=s =well.How about reduce!fun(range)? It's pure/nothrow when fun is pure/nothrow.= = Plenty of std.algorithm would benefit. BTW, small suggestion: autopure -> auto(pure) Tomek
Aug 30 2010
Dnia 30-08-2010 o 21:10:10 Tomek Sowi=F1ski <just ask.me> napisa=B3(a):How about reduce!fun(range)? It's pure/nothrow when fun is pure/nothro=w. =Plenty of std.algorithm would benefit.Eh, nevermind. popFront() must mutate the range so it can't be pure. Nee= d = to get some sleep...
Aug 30 2010
On 8/30/10 14:16 PDT, Tomek Sowiński wrote:Dnia 30-08-2010 o 21:10:10 Tomek Sowiński <just ask.me> napisał(a):It can still be nothrow depending on input, which makes a solid point. Arguments against qualifier/attribute propagation based on sheer semantics ("it's abs so it must be pure") break badly in the face of higher-order functions. It's pretty clear we need an attribute propagation mechanism if we want e.g. to make Phobos const-aware. AndreiHow about reduce!fun(range)? It's pure/nothrow when fun is pure/nothrow. Plenty of std.algorithm would benefit.Eh, nevermind. popFront() must mutate the range so it can't be pure. Need to get some sleep...
Aug 30 2010
2010/8/30 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>On 8/30/10 14:16 PDT, Tomek Sowi=F1ski wrote:sDnia 30-08-2010 o 21:10:10 Tomek Sowi=F1ski <just ask.me> napisa=B3(a): How about reduce!fun(range)? It's pure/nothrow when fun isIt can still be nothrow depending on input, which makes a solid point. Arguments against qualifier/attribute propagation based on sheer semantic=pure/nothrow. Plenty of std.algorithm would benefit.Eh, nevermind. popFront() must mutate the range so it can't be pure. Need to get some sleep...("it's abs so it must be pure") break badly in the face of higher-order functions. It's pretty clear we need an attribute propagation mechanism if we want e.g. to make Phobos const-aware.Could something like this be a possible building block for pure? import std.stdio, std.bigint; /** Test whether expression expr is pure for type Type. */ template isPure(Type, string expr) { enum isPure =3D isPureImpl!(Type, expr); } bool isPureImpl(Type, string expr)() { mixin("auto pureTester(T)(T a) pure { return " ~ expr ~ ";}"); return is(typeof( pureTester(Type.init) )); } int foo(int i) { return i;} // not pure int bar(int i) pure { return i;}// pure void test(T)(T num) if (isPure!(T, "-1*a")) // test that with "-1*a", "-1*foo(a)" and "-1*bar(a)" { writeln("Yes, pure expr for type " ~ T.stringof); } void main() { test(1); auto b =3D BigInt("1"); // test(b); // is -1*b a pure expr for BigInts? } test(1) will compile for expression -1*a or just "a" test(BigInt("1")) will not compile with expression -1*a, because it's not pure for BigInts. It will compile with just "a" as constraint expression. I also tried to instantiate test(1) with -1*bar(a) and -1*foo(a) as constraints. It passes for -1*bar(a), because bar is pure, but not with foo(a). Maybe then it's possible to create a pure version of a function if this passes the test. I'm not sure, it's late in there. Also, this could be done also for nothrow. I didn't think it through for const. Philippe
Aug 30 2010
Dnia 30-08-2010 o 21:16:15 Tomek Sowi=F1ski <just ask.me> napisa=B3(a):Dnia 30-08-2010 o 21:10:10 Tomek Sowi=F1ski <just ask.me> napisa=B3(a)=:How about reduce!fun(range)? It's pure/nothrow when fun is =pure/nothrow. Plenty of std.algorithm would benefit.Eh, nevermind. popFront() must mutate the range so it can't be pure. =Need to get some sleep...But wait! It can be auto(nothrow). So my sleepy-head argument didn't suc= k = after all:) And I do like the auto(qual) syntax. BTW, isn't it surprising? Think of sum of squares, I mean reduce!"a + b = * = b"(range) -- it should be pure, it's natural. Come to think of it, if pu= re = functions required arguments to be tail immutable**, not from-head = immutable as it is now, popFront() being also tail immutable would = advance the range and it would just work... Damn, now I can't sleep. ** tail immutable doesn't exist in the language. It was proposed in the= = great const debate not too long ago. Tomek
Aug 30 2010
On 29/08/10 2:29 AM, dsimcha wrote:An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num< 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure.I think the problem here is *not* that it's difficult to tell whether a function can be pure, but the fact that BigInt's binary product should be pure (because it is, in the mathematical sense). This is just the whole const without mutable thing all over again i.e. there exist logically const member functions that change the state of their members (caching is the canonical example of this). This is why mutable exists. Similarly, there exist logically pure function that change the global state, or allocate memory (such as BigInt's op* here). There needs to be some way around that. If there cannot be any way around it then pure should be dropped altogether because the language simply cannot support it.
Aug 29 2010
== Quote from Peter Alexander (peter.alexander.au gmail.com)'s articleOn 29/08/10 2:29 AM, dsimcha wrote:Part of the point of purity is to provide guarantees useful for multithreading. For these cases logical purity isn't what's important. Physical purity is. I agree that D's purity system is a bit conservative and should ideally be made less conservative, but it doesn't change the fact physical purity is what matters to threading.An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num< 0) ? -1 * num : num; } Looks pretty good. Won't work with BigInt because opBinary!"*" isn't pure and can't practically be made pure.I think the problem here is *not* that it's difficult to tell whether a function can be pure, but the fact that BigInt's binary product should be pure (because it is, in the mathematical sense). This is just the whole const without mutable thing all over again i.e. there exist logically const member functions that change the state of their members (caching is the canonical example of this). This is why mutable exists. Similarly, there exist logically pure function that change the global state, or allocate memory (such as BigInt's op* here). There needs to be some way around that. If there cannot be any way around it then pure should be dropped altogether because the language simply cannot support it.
Aug 29 2010
dsimcha <dsimcha yahoo.com> wrote: [Interesting stuff] This problem is much bigger that auto<foo>. When we get a new ribute that affects code in a similar way, are we to add autofnord autogrok autogazillions to the language as well? If anything, this system should be made extensible. I suggest the syntax auto[ <value> ] where <value> is a comma-separated list of ributes. This also leads to another (somewhat unrelated, I admit) idea of mine. Occasionally, you want attributes to be determined by template parameters. For this, I suggest the following: attribute( condition )[ <value> ] Where <condition> follows the system of template constraints. <value> is as above, a comma-separated list of ributes. If <condition> evaluates to true, the ributes in <value> are applied to whatever follows. -- Simen
Aug 29 2010
Couldn't you reuse the version statement for that? At least for a group of attribute-related functions this might work (but I haven't tried): version(pure) { pure void foo() {...} pure void bar() {...} } else { void foo() {...} void bar() {...} } For this, I suggest the following:=A0 =A0 attribute( condition )[ <value> ] Where <condition> follows the system of template constraints. <value> is as above, a comma-separated list of ributes. If <condition> evaluates to true, the ributes in <value> are applied to whatever follows. -- Simen
Aug 29 2010
Andrej Mitrovic <andrej.mitrovich gmail.com> wrote:Couldn't you reuse the version statement for that? At least for a group of attribute-related functions this might workStatic if, at least. Still, that is the road to extreme code bloat. Imagine having to write those for all combinations of pure, nothrow, const/immutable, override, protected/public/package, final, and synchronized (arguably a constructed situation, but still). -- Simen
Aug 29 2010
Simen kjaeraas:I suggest the syntax auto[ <value> ] where <value> is a comma-separated list of ributes.That's bad. I prefer a more flexible semantics, so auto becomes one case of user-defined scoped properties. Bye, bearophile
Aug 29 2010
bearophile <bearophileHUGS lycos.com> wrote:Simen kjaeraas:Please do elucidate. Does the second part of my post perhaps help define something akin to what you envision? -- SimenI suggest the syntax auto[ <value> ] where <value> is a comma-separated list of ributes.That's bad. I prefer a more flexible semantics, so auto becomes one case of user-defined scoped properties.
Aug 29 2010
Simen kjaeraas:Please do elucidate.I am not able to design user-defined attributes in a short time all alone :-) I'd like to be able to define scoped attributes in normal D code, so you can extend a bit the D type system in user-code, using static introspection (using __traits and other things). ("scoped" means that you are able to use them only in the scope where you have defined them, and you are able to import them).Does the second part of my post perhaps help define something akin to what you envision?It's a starting point, but in my opinion quite more design work is needed. First of all I'd like to define one attribute, for example using alias: alias attribute(SomeCondition!A)[A...] autopure; You may even use a template-like syntax, but it's a bit overkill: attribute autopure(A...) { You are surely able to do better. Anyway, I think all this is for D3. For D2 I think it's better to fix the features already present or planned before creating new ones. The bug reports I've suggested to Don (among my ones) in my opinion comes before truly new features. Bye, bearophile
Aug 29 2010
Andrei Alexandrescu Wrote:On 08/28/2010 08:29 PM, dsimcha wrote:An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example:[...]T abs(T num) pure nothrow { return (num< 0) ? -1 * num : num; }[...]A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions.Does this sound like it could feasibly be implemented and would work well?Yah, with the growing interest in applying qualifiers at a larger scale (and making Phobos a good example of such) this is quite timely. I've been mulling myself over a similar proposal.What I had in mind is a bit more structured - it would allow selecting a type or an expression and assessing its constness/purity. That would be done just like .sizeof and .stringof work - by defining two more special members .constof, .pureof, .nothrowof. For example:T abs(T num) (-1 * num).pureof (-1 * num).nothrowof { return (num < 0) ? -1 * num : num; }Could something along the lines of a .qualof member work, for those times where you want to duplicate all relevant qualifiers? That seems likely to be the general case, and could considerably reduce the verbosity involved. T abs(T num) (-1 * num).qualof { return (num < 0) ? -1 * num : num; } is even shorter, and would have the possibility of also handling concerns of constness and sharedness and the like properly. Cheers, Pillsy
Aug 30 2010
== Quote from Pillsy (pillsbury gmail.com)'s articleAndrei Alexandrescu Wrote:constness and sharedness and the like properly.On 08/28/2010 08:29 PM, dsimcha wrote:[...]An issue that comes up very frequently when trying to use const, pure or nothrow in generic code is lack of knowledge of whether the functions you're calling are const/pure/nothrow. For example: T abs(T num) pure nothrow { return (num< 0) ? -1 * num : num; }[...]A solution I propose is to allow the annotations autoconst, autopure and autonothrow for template functions.Could something along the lines of a .qualof member work, for those times where you want to duplicate all relevant qualifiers? That seems likely to be the general case, and could considerably reduce the verbosity involved. T abs(T num) (-1 * num).qualof { return (num < 0) ? -1 * num : num; } is even shorter, and would have the possibility of also handling concerns ofDoes this sound like it could feasibly be implemented and would work well?Yah, with the growing interest in applying qualifiers at a larger scale (and making Phobos a good example of such) this is quite timely. I've been mulling myself over a similar proposal. What I had in mind is a bit more structured - it would allow selecting a type or an expression and assessing its constness/purity. That would be done just like .sizeof and .stringof work - by defining two more special members .constof, .pureof, .nothrowof. For example: T abs(T num) (-1 * num).pureof (-1 * num).nothrowof { return (num < 0) ? -1 * num : num; }Cheers, PillsyMy concern with proposals that require you to explicitly check specific statements is that they won't scale to larger functions where there are a lot of things that need to be checked. I'm worried some signatures might become so complicated with qualification checking as to make protected virtual abstract base pure virtual private destructors look tame. Until someone can explain how this proposal would scale at all, let alone gracefully, to needing to check the purity/constness/nothrow-ness/safety of a lot of things, I don't think we can seriously consider it.
Aug 30 2010