April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to guslay | I understant Steven proposal, and it is a nice proposal and it could work, and indeed it is nice to have a way out of the const, but I am terribly afraid of this. Maybe I am wong (in this case please point out a pratical example where this is not the case), but what is the use of such a data? The only use that I can imagine (if the object is really constant) are cache or performance related, so informations not really needed, but useful for improving the performance. So naturally also "pure" functions would like to take advantage of it, or the usefulness of the whole is very diminished. But then the cache has to be done *very* carefully to avoid curruptions due to multiple threads accessing the object... It is possible but really not easy (and needs locking or atomic operations that probably will not be good for performance), so very likely it will be done badly and will lead to very subtle bugs. I really think that the controlled and already studied ways to relax the const are "suspended evaluation", and "undefined settable value blocking on read". I also think that indeed what walter wants is as guslay said > A = B = C because you never know (and for performance reasons do not want to know) if another thread is executing a g_not_pure on the same data on which a pure function is working. You do not want to aquire locks before executing a pure function. Fawzi <guslay@gmail.com> said: > Walter Bright Wrote: > >> >> Logical const just utterly breaks the whole system. > > > I think we had a different expectations about invariant and pure. > > Do you believe that (A), > > invariant A a; > int x = f_pure(a); > g_not_pure(a); // potentially mutating mutable members of A > int y = f_pure(a); > int z = f_pure(a); > > which is can be transformed without problem to (B), > > invariant A a; > int x = f_pure(a); > g_not_pure(a); > int y = f_pure(a); > int z = y; > > MUST also be equivalent to (C) ? > > invariant A a; > int x = f_pure(a); > int y = x; > int z = x; > g_not_pure(a); > > > A = B = C is only possible if invariant A as no mutable part. I agree. Is this your position? > > I was expecting a weaker pure model, where only A = B is true (optimization based on purity only possible inside a contiguous block of pure functions). > > If this is correct then I understand your concerns against logical const. |
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Leandro Lucarella | "Leandro Lucarella" wrote > Steven Schveighoffer, el 4 de abril a las 09:24 me escribiste: >> class C >> { >> // this is like a struct passed as an argument to all methods, and is >> always mutable >> nonstate >> { >> int c; >> } >> >> // is passed an implicit nonstate pointer to the nonstate values >> int f(int x) const >> { >> return nonstate.c++; >> } >> } > > Why don't you just do something like this: > > static int[C] nonstate; > class C > { > // is passed an implicit nonstate pointer to the nonstate values > int f(int x) const > { > return nonstate[this] += 1; > } > } because this solution requires a non-trivial lookup time, whereas my solution does not require a lookup time. Plus you have more synchronization to worry about (all instances must have a global mutex to the cache). The point is, yes, you can solve it that way. But why can't we have a way that has higher performance and less threading issues? There is no reason I can see. > If nonstate is not part of the object, why to put it in it? I this the cache-problem is a very specific problem, which is OK to have a specific solution. Even more, I find a lot more clear if the cache it's outside the class, because is not part of it (if it's not part of the state of the object, it's not part of the object at all!). I agree it is confusing, but it's just a tacked-on piece that isn't part of the class, although it lives in the same allocated memory space. What if I put it like: class C { nonstate { int c; } body { int f(int x) const { return nonstate.c++; } } } similar to how function contracts split the body from the contract. Does this help your understanding? what I don't get is how people can understand that static is not part of an instance, yet that goes into the class declaration, but they can't understand this. > So C.f() is clearly not pure, but it's const, it doesn't change the state > of the object. Agreed. -Steve |
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 04/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> what I don't get is how people ... can't
> understand this.
It's because you (...and now me...) are describing a totally new concept which does not yet exist. Even the "mutable" keyword in C++ doesn't come close to this, because C++ doesn't have either transitive const or invariant. This really is something new.
(Everyone else, see the "unpaintable..." thread for a first stab at an
explanation).
|
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Fawzi Mohamed | "Fawzi Mohamed" wrote > So naturally also "pure" functions would like to take advantage of it, or > the usefulness of the whole is very diminished. > But then the cache has to be done *very* carefully to avoid curruptions > due to multiple threads accessing the object... In fact, if pure does memoization, it must too be aware of threading implications, unless the memoization is per-thread (which I have no idea why it would be). > It is possible but really not easy (and needs locking or atomic operations that probably will not be good for performance), so very likely it will be done badly and will lead to very subtle bugs. Anytime you run 2 non-pure functions that access mutable data, be it global or as a logical const portion of the object, you will need synchronization. Adding logical const does not add to or diminish that requirement. For pure to be statically verifiable by the compiler, it must not be allowed to access any mutable data that is at the same time accessible through another thread, so it should not be able to access the data at the same time, and no synchronization is necessary. > I really think that the controlled and already studied ways to relax the const are "suspended evaluation", and "undefined settable value blocking on read". I'm not familiar with these concepts. Frankly, I'm not sure they are relevant to this discussion (although they might be pertinent to making multiprogramming easier). I'm trying to take an already existing possibility, and make it easier to implement and perform better. > I also think that indeed what walter wants is as guslay said > >> A = B = C > > because you never know (and for performance reasons do not want to know) if another thread is executing a g_not_pure on the same data on which a pure function is working. But if A is invariant, it cannot be changed, so there are no thread concerns. Think of the mutable portion of a logical-const class as being global data (or as I like to think, not part of the class state). A pure function cannot access global data, and so likewise it cannot access the mutable portions of the class. There is no need for synchronization because the pure function will not change it's output if the mutable portion changes. -Steve |
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to guslay | "guslay" wrote
> Walter Bright Wrote:
>
>>
>> Logical const just utterly breaks the whole system.
>
>
> I think we had a different expectations about invariant and pure.
>
> Do you believe that (A),
>
> invariant A a;
> int x = f_pure(a);
> g_not_pure(a); // potentially mutating mutable members of A
> int y = f_pure(a);
> int z = f_pure(a);
>
> which is can be transformed without problem to (B),
>
> invariant A a;
> int x = f_pure(a);
> g_not_pure(a);
> int y = f_pure(a);
> int z = y;
>
> MUST also be equivalent to (C) ?
>
> invariant A a;
> int x = f_pure(a);
> int y = x;
> int z = x;
> g_not_pure(a);
>
>
> A = B = C is only possible if invariant A as no mutable part. I agree. Is this your position?
>
> I was expecting a weaker pure model, where only A = B is true (optimization based on purity only possible inside a contiguous block of pure functions).
>
> If this is correct then I understand your concerns against logical const.
If we say that the mutable part of invariant A is not accessible during pure functions, then (A = B = C) holds true. In fact, I would argue that for logical const, the mutable part is not part of the invariant A instance, but is an 'associated' or 'tacked-on' piece that happens to be carried around with A. It is used by A to help for various tools such as syncrhonization, memoization, etc, but it is not part of A's state. Think of it as living in A's namespace, but not in the instance, and so it is inaccessible to pure functions.
-Steve
|
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Janice Caron | Janice Caron, el 4 de abril a las 17:46 me escribiste: > On 04/04/2008, Leandro Lucarella <llucax@gmail.com> wrote: > > Why don't you just do something like this: > > <snip> > > If nonstate is not part of the object, why to put it in it? > > You're having the same problem with Steven's jargon as I had. I found that terminology confusing. Rest assured, we /are/ talking about part of the object. If we come up with a better way of describing it, we'll tell the world. > > What we're talking about here is a member whose constancy cannot be changed (but whose value maybe can). What about a real example? -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- CAROZO CON FARINGITIS -- Crónica TV |
April 04, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Janice Caron | Janice Caron, el 4 de abril a las 17:53 me escribiste: > On 04/04/2008, Janice Caron <caron800@googlemail.com> wrote: > > On 04/04/2008, Leandro Lucarella <llucax@gmail.com> wrote: > > > Why don't you just do something like this: > > > > > <snip> > > > > > If nonstate is not part of the object, why to put it in it? > > > > > > You're having the same problem with Steven's jargon as I had. I found > > that terminology confusing. Rest assured, we /are/ talking about part > > of the object. If we come up with a better way of describing it, we'll > > tell the world. > > > > What we're talking about here is a member whose constancy cannot be > > changed (but whose value maybe can). > > See the thread "unpaintable..." for an alternative description of the solution. I saw it and says nothing new to me. -- Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/ ---------------------------------------------------------------------------- GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145 104C 949E BFB6 5F5A 8D05) ---------------------------------------------------------------------------- This homeless guy asked me for some money the other day. And I was gonna give it to him but then I thought you're just gonna use it on drugs or alcohol. And then I thought, that's what I'm gonna use it on. Why am I judging this poor bastard. |
April 05, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer wrote: > "Georg Wrede" wrote >>Walter Bright wrote: >>>Bill Baxter wrote: >>> >>>>So does that mean >>>> >>>> pure int f(const Class c) >>>> { ... >>>> } >>>> >>>>will *not* be allowed? Because some part of c *could* change, even if it's not f() doing the changing. I.e. another thread could be changing c concurrently. >>> >>>Right. That declaration of f is erroneous. >> >>I'd disagree. >> >>Whether a function is pure or not, has nothing to do with whether c could change or not. > > The question is, should pure functions be protected from other threads changing c? I think Walter wants this to be true, and for that, c must be invariant. I think Walter is trying to promote a different definition for pure than what you are thinking of. Hmm. (There ought to be a law against stealing other people's words!!! And jail time for changing them subtly and inconspicuously!) Hundreds of (otherwise unnecessary) posts even in this NG are written every month, just because two people argue on something, that in the end turns out to be just them having different notions of what a word means. And reading those posts wastes everyones time! And, in that case, it would have been prudent to clarify this as a comment to "What is pure and what is not". Not only for my sake, but for all the readers who've seen this post stay undisputed or uncommented. >>Take, for example sin(x). We all know that it's pure, period. Now, whether x changes between two consecutive invocations or not, is irrelevant. Of course you get a different result. And you should. > > sin(x) is a completely different animal, because x is not a reference type, it is pushed on the stack, and the pure function has the only reference to it. Nothing can change x while sin is running. So you mean that Walter's pure implies that the argument has to be Petrified? (Dammit, I don't even dare to use the normal words for stuff anymore, so I make my own words! Petrified here simply means "we know it won't change". Vague, I know.) Does it also mean that the pure function itself causes this Petrified thing, or is it the programmer's responsibility to Petrify the object before passing it? >>But pure, IMHO, means (1) the function is guaranteed to give the same result every time /if/ the input is the same (like sin(x) does), (2) it does not change anything at all anywhere, it's only way of affecting life is by its return value, (3) it doesn't access info from non-constants anywhere. >> >>Now, to the issue between >> >> (1)pure int f(const Class c) >> (2)pure int f(Class c) >> >>I'd say that, since a Pure function promises not to change /anything/ (other than its return value, of course) anywhere, this means a pure function /simply has to implicitly treat its arguments as unchangeable/! >> >>Therefore, depending on what A/W want, either: >> >> - a pure function has to have const decorated parameters only (1) >> - and (2) would be a syntax error >> >>or >> >> - a pure function's parameters are implicitly const, and (1) is then a >>syntax error > > If the arguments must be invariant, then they cannot be implicitly cast. I understand what you are saying, but I think A/W want something different. Arrgh. You lost me here. (I'm getting paranoid: did you write this because it says "implicitly const" above? What if it had been just "petrified", as in "somehow just known not to change while the pure function is running"?) >>(Not making the latter a syntax error isn't technically impossible, but for those learning the language and the casual readers of ex. magazine articles, this would wreak havoc with their understanding D.) >> >>*Back to concurrency*, if someone changes c in mid-run, then it's the programmer's fault. It's his headache. The function f only guarantees not to change c /by itself/. > > I think this is what A/W want to prevent. Otherwise, all this touting about pure functions being the way to multiprogram in D is not completely true, you still have to deal with multithreadded issues. Heh, I always thought everybody already knew and agreed on that. Running pure (as in "my" definition ;-( ) functions on atomic data would be nice and fast. No concurrency issues, IMHO. But obviously, with any reference type one has to have some kind of mechanism that guarantees atomicity of access. > Walter has said before he wants multithreaded programming to "just work" without having to do any locking, just like FP languages have. That'd be excellent. Problem is, then the data would have to be Petrified all the FP time. This would probably result in a common programming pattern, where things are first set up using non-functional programming, then the data is Petrified (what do I know, explicitly, implicitly?), and then a lot of functional stuff is run, most likely in concurrent processes. Then some non-functional code is run to prepare for the next functional bout, etc. Hmm, might not be too bad a programming pattern. At least it could ease the use of both functional and non-functional code in the same program. |
April 05, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Georg Wrede | "Georg Wrede" wrote > Steven Schveighoffer wrote: >> "Georg Wrede" wrote >>>Walter Bright wrote: >>>>Bill Baxter wrote: >>>> >>>>>So does that mean >>>>> >>>>> pure int f(const Class c) >>>>> { ... >>>>> } >>>>> >>>>>will *not* be allowed? Because some part of c *could* change, even if it's not f() doing the changing. I.e. another thread could be changing c concurrently. >>>> >>>>Right. That declaration of f is erroneous. >>> >>>I'd disagree. >>> >>>Whether a function is pure or not, has nothing to do with whether c could change or not. >> >> The question is, should pure functions be protected from other threads changing c? I think Walter wants this to be true, and for that, c must be invariant. I think Walter is trying to promote a different definition for pure than what you are thinking of. > > Hmm. (There ought to be a law against stealing other people's words!!! And jail time for changing them subtly and inconspicuously!) > > Hundreds of (otherwise unnecessary) posts even in this NG are written every month, just because two people argue on something, that in the end turns out to be just them having different notions of what a word means. And reading those posts wastes everyones time! Sorry. Usually, to convince "the world" of an opinion, you must argue continuously with the single doubter. Otherwise, it looks like you give in :) > And, in that case, it would have been prudent to clarify this as a comment to "What is pure and what is not". Not only for my sake, but for all the readers who've seen this post stay undisputed or uncommented. I still don't know the answer to this, as I've never used 'pure' and I'm pretty sure Walter intends to use pure in a different way than it has been used before :) >>>Take, for example sin(x). We all know that it's pure, period. Now, whether x changes between two consecutive invocations or not, is irrelevant. Of course you get a different result. And you should. >> >> sin(x) is a completely different animal, because x is not a reference type, it is pushed on the stack, and the pure function has the only reference to it. Nothing can change x while sin is running. > > So you mean that Walter's pure implies that the argument has to be Petrified? (Dammit, I don't even dare to use the normal words for stuff anymore, so I make my own words! Petrified here simply means "we know it won't change". Vague, I know.) Only for reference types. For value types (such as structs without pointers/references and basic types like int), they are copied on the stack every time they are passed to a function, and so they are guaranteed to be unique. That is why sin(x) doesn't need 'Petrified' data, because a guaranteed unique copy of x (presumably a double?) is made. > > Does it also mean that the pure function itself causes this Petrified thing, or is it the programmer's responsibility to Petrify the object before passing it? I would guess that the caller has to ensure this for reference/pointer types, otherwise, the compiler can't tell whether the data pointed to is not going to change during the function with a different thread. BTW, the keyword that means 'Petrified' is invariant. > >>>But pure, IMHO, means (1) the function is guaranteed to give the same result every time /if/ the input is the same (like sin(x) does), (2) it does not change anything at all anywhere, it's only way of affecting life is by its return value, (3) it doesn't access info from non-constants anywhere. >>> >>>Now, to the issue between >>> >>> (1)pure int f(const Class c) >>> (2)pure int f(Class c) >>> >>>I'd say that, since a Pure function promises not to change /anything/ (other than its return value, of course) anywhere, this means a pure function /simply has to implicitly treat its arguments as unchangeable/! >>> >>>Therefore, depending on what A/W want, either: >>> >>> - a pure function has to have const decorated parameters only (1) >>> - and (2) would be a syntax error >>> >>>or >>> >>> - a pure function's parameters are implicitly const, and (1) is then a syntax error >> >> If the arguments must be invariant, then they cannot be implicitly cast. I understand what you are saying, but I think A/W want something different. > > Arrgh. You lost me here. (I'm getting paranoid: did you write this because it says "implicitly const" above? What if it had been just "petrified", as in "somehow just known not to change while the pure function is running"?) I'm saying, if Walter and Andrei define pure functions as ONLY taking invariant data, then 2 must be the syntax error. In fact, to get technical, 1 is a syntax error also, it should be: pure int f(invariant Class c); as const does not guarantee that c will not change, it just guarantees that f will not change c. const in D means 'constant through this view' Of course, I could also be wrong, and they do not intend to implement pure functions as requiring invariant reference parameters. In that case, I've totally misunderstood the whole point of having pure functions, and I'd hazard to guess it negates the reason for having invariant in the first place :) >>>(Not making the latter a syntax error isn't technically impossible, but for those learning the language and the casual readers of ex. magazine articles, this would wreak havoc with their understanding D.) >>> >>>*Back to concurrency*, if someone changes c in mid-run, then it's the programmer's fault. It's his headache. The function f only guarantees not to change c /by itself/. >> >> I think this is what A/W want to prevent. Otherwise, all this touting about pure functions being the way to multiprogram in D is not completely true, you still have to deal with multithreadded issues. > > Heh, I always thought everybody already knew and agreed on that. > > Running pure (as in "my" definition ;-( ) functions on atomic data would be nice and fast. No concurrency issues, IMHO. > > But obviously, with any reference type one has to have some kind of mechanism that guarantees atomicity of access. No need to guarantee atomicity for data that will not change. That is why I think invariant reference parameters are necessary. > >> Walter has said before he wants multithreaded programming to "just work" without having to do any locking, just like FP languages have. > > That'd be excellent. Problem is, then the data would have to be Petrified all the FP time. Not only all FP time, but all time. If you are executing a pure function in one thread and a non-pure function in another thread, and both have references to the same data, this won't work without synchronization issues if the non-pure function has a writable reference, but the FP function has a 'Petrified' reference. Sorry to be so confusing. It's really tough to explain things that I'm not totally sure of, but I have no choice, as Walter is never specific about what the rules will be :) We have to imply them from his statements of how pure functions will work. -Steve |
April 05, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On 05/04/2008, Steven Schveighoffer <schveiguy@yahoo.com> wrote:
> In fact, to get technical,
> it should be:
> pure int f(invariant Class c);
I wondered about that.
It seems to me that if /all/ of the parameters, /and/ the return value /must/ be invariant (or implicitly castable to invariant, e.g. int) then couldn't
pure R f(A a, B b, C c, D d)
be syntactic sugar for
pure invariant(R) f(invariant(A) a, invariant(B) b, invariant(C)
c, invariant(D) d)
? (Especially what with "invariant" being a nine-letter keyword and all!)
|
Copyright © 1999-2021 by the D Language Foundation