April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | Steven Schveighoffer Wrote:
> I have been reading through the reddit posts about the const faq, and also some posts here by guslay, and I believe Walter needs to re-think his beliefs about how transitive const is necessary for the future.
>
I'm glad to see this issue catch on. I'll try to summarize of the thoughts on const (in reality, invariant) expressed by others and myself.
-= A const method may exhibit side effects =-
The transitivity of const does not extends beyond the scope of the class. You may call static/global functions and modify static/global data, modify mutable objects accessible through static/global pointers, perform I/O, etc.
-= Const != thread-safe =-
A method with potential side effects is not implicitly thread-safe.
-= Const is not the key to functional programming =-
Immutability is required for FP, but const "mathematical" guarantees are too weak to provide that ability.
-= Pure function + invariant data is required for FP =-
As many have pointed out, the expected pure function will enable FP.
This concept is not yet into place, but I will define it has a method that only call other pure functions, and performs read-only operation on its class members and static/global data. In other terms, a method with no side effect, that can only modify its input and output.
A pure method is const, but a const method is not pure.
-= Const is part of the interface, not part of the implementation =-
Const part of the interface: you need to have a const method to call on a const object.
It is also an abstraction. In some sense, D const is already logical const. Sure, the bits of the object cannot change, but since /everything else/ can change, the "purity" of constness is defined by the implementation. In other words, const is a conceptual.
It does not mean that it is useless or without teeth, because D const is enforceable (but only on a member-by member basis).
Defining const as bitwise const for the whole object is simplistic. You can still (imperfectly) fake mutability. Allowing this would be desirable.
class MyMutableClass
{
private int normal_part;
private mutable int mutable_part;
public int const lazyGetter();
}
const MyMutableClass obj = new MyMutableClass;
If not, people will find a way to hack around it with ugly tricks.
int mutable_part;
class MyHackishClass
{
private int normal_part;
public int const lazyGetter();
}
const MyHackishClass obj = new MyHackishClass;
Mutable is an acknowledgment of the current limits of const.
-= Allowing mutable members does not break or weaken the const model =-
D const is powerful because it is enforceable.
What mutable does is allowing finer-grained control over the powerful const system. It does not weaken it, it controls its scope. Those are orthogonal issues (as far as I have yet to see an instance where having half the fields of an object const, instead of all the fields of the object, limits anything in any way).
The internal state of the object will not be modified beyond what the class designer intended and allowed. The extent of const should be an implementation detail.
-= Mutability is required =-
C++ mutable keyword is not strictly required. Given that constness is almost never enforced in practice, one can cast away constness, modify "constant" data, and move on. Mutable offers a cleaner way to do that in the clear, as part of the class definition.
The casting trick cannot be used in D (gladly).
But which is worst?
An object is fully initialized, but some parts are lazyly evaluated and require mutable members. The object as to be sent to an external entity.
a) Mutable members are not allowed. The object cannot be passed as const. The non-const object is passed to the external function, renouncing to any control at all on the immutability of the object.
b) Mutable members are allowed. The object is passed as const. The caller can be confident that the internal state of the object will not be modified beyond the intention of the class designer.
Mutable strengthen the const system.
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tim Burrell | On 01/04/2008, Tim Burrell <tim@timburrell.net> wrote:
> Instead of going ... the way D is
> going now ... why not take the time to find a really
> good, simple and effective solution?
<slaps head> Doh! Why anyone else think of that before!?
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bill Baxter | On 01/04/2008, Bill Baxter <dnewsgroup@billbaxter.com> wrote:
> // Hypothetical pure function with implicit/deduced constness
> pure int foo(Klass a) {
> scope b = new Klass;
> Klass[] x;
> x ~= a;
> x ~= b;
> random_shuffle(x);
> x[0].prop = 10; // is this ok or not?
> }
Just for the record, random_shuffle() is not a pure function, and hence cannot be called from within a pure function.
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tim Burrell | > > If D did move the way of C++ it would be a lot better off than it is now. But, that being said, I agree with you... there's such a huge opportunity here! Instead of going the way C++ did, or the way D is going now (which is even worse), why not take the time to find a really good, simple and effective solution? > The D community has been working on this problem for about a year now. I'm don't know if such a "simple effective" solution exists as you imply. And even if it does, if it takes us another year to figure it out, then that's a year that we could be doing more productive things. > For large parallel projects, I have seen some successful uses of Erlang, but most of the parallel development these days is done in Fortran and C++ because of OpenMP (even some big distributed apps are moving to OpenMP via Intel's clustering OpenMP compiler). C++ also has a nice join calculus module available in Boost, not to mention Boost's [new] MPI support. > > People aren't using C++ and Fortan because they're the best suited languages... and parallel people are always looking for something better because, like you said, in theory there should be a lot better choices available to us. Functional languages promise neat and tidy parallelization with no side affects, etc, but in reality they're too slow and don't produce optimized code on the machines that parallel developers need to write code for. > > What we want is a fast, system level language, that can be targeted by an optimizing compiler, but that is also quick and enjoyable to write code in. Yes, I think the solution to scalable concurrency will probably be a systems programming language that supports multiple paradigms. D is a good candidate here, being very multi-paradigm. I still don't think writing scalable multithreaded apps will ever be easy. > If D did have some support for parallel programming I think it would be a major selling point, and I don't think it would need to complicate the language at all. The whole problem here isn't that adding parallel support automatically makes a language ridiculously complex. > > The real issue is Walter's wacky and complex const system. Somehow he thinks it's going to help with parallel development, so the issues are getting mixed, but in reality they're completely unrelated. I'm not so sure it's any more complex than C++ const, but from a developer perspective, I think it may be considered wacky, because it's more difficult to work with. This is due to the fact that it has a completely different design goal from C++'s const, based on a hypothetical compiler optimization benefit that no one seems to fully understand. My suggestion would be to implement a C++-like "logical const" system using the const keyword, and use the invariant keyword for Walter/Andrei's transitive const. Maybe that would give us the best of both worlds. -Craig |
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Janice Caron | Janice Caron wrote:
> On 01/04/2008, Bill Baxter <dnewsgroup@billbaxter.com> wrote:
>> // Hypothetical pure function with implicit/deduced constness
>> pure int foo(Klass a) {
>> scope b = new Klass;
>> Klass[] x;
>> x ~= a;
>> x ~= b;
>> random_shuffle(x);
>> x[0].prop = 10; // is this ok or not?
>> }
>
> Just for the record, random_shuffle() is not a pure function, and
> hence cannot be called from within a pure function.
Ah, good point. So make it:
x = pure_random_shuffle(x);
Still I think that points to an unnecessary restriction on pure functions.
A function that _only_ modifies its arguments can be safely called from a pure function when the data being passed is local to the pure function. For example this should be fine:
void inc(ref int x) { x++; }
pure int pure_func() { int x=0; inc(x); return x; }
--bb
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Tim Burrell | == Quote from Tim Burrell (tim@timburrell.net)'s article ... > People aren't using C++ and Fortan because they're the best suited languages... and parallel people are always looking for something better because, like you said, in theory there should be a lot better choices available to us. Functional languages promise neat and tidy parallelization with no side affects, etc, but in reality they're too slow and don't produce optimized code on the machines that parallel developers need to write code for. I'm currently of the opinion that the ideal solution is to use a functional language like Erlang for the concurrency aspect and then to use a language like C for the performance-critical or systems bits. For example, from a statistic I saw somewhere, the Ericsson switch code has approximately as many lines of C code as it does Erlang code (a few hundred K of each), with the structure I've described. > What we want is a fast, system level language, that can be targeted by an optimizing compiler, but that is also quick and enjoyable to write code in. I think we have that with D 1.0. > If D did have some support for parallel programming I think it would be a major selling point, and I don't think it would need to complicate the language at all. The whole problem here isn't that adding parallel support automatically makes a language ridiculously complex. I'd modify that by saying that adding concurrency support makes an imperative language ridiculously complex. The problem (as someone in this NG put it) is that while functional languages describe /what/ should happen, imperative languages describe /how/ it should happen. In instances where the programmer knows more about how to optimize the process than the compiler may, using an imperative language is clearly the correct solution... and systems languages are clearly necessary when systems work is to be done (ie. operations involving direct memory access, etc). But it's fairly well accepted these days that concurrent programming is just too difficult for most programmers if they have to worry about the 'how' part. At best, most such programs achieve only a large-scale granularity as tasks with logically distinct divisions are manually separated out into jobs for thread pools and the like. By comparison, because data in most functional languages is immutable and the program structure is inherently function-oriented, it is easy for the environment to spread function invocations across multiple cores, machines, whatever. There is no shared/global data to worry about, no state to synchronize, and no loops to unroll. Combine this with a message passing system for explicit parallelization and the program may take advantage of not only automatic parallelization but explicit parallelization as well. (For the record, I've looked at languages Cilk and think they're a pretty neat idea but still too bogged down in the 'how' aspect. Haven't looked at OpenMP yet though, and I really should do so.) > The real issue is Walter's wacky and complex const system. Somehow he thinks it's going to help with parallel development, so the issues are getting mixed, but in reality they're completely unrelated. I actually think that the const system could help somewhat. Pure functions, for example, offer guarantees that are fairly similar to what a functional programming language offers. However, for such an approach to compete with a true functional language the D programmer would have to use pure functions almost exclusively rather than sparely, which is what I expect. And in the case that the programmer uses pure functions exclusively, what he's doing is mimicking functional programming in a language that isn't naturally suited to it. So in short, I think the pure/const/whatever stuff may actually get D part of the way there, but I also think that it's a bit like using a boat in an automobile race. Sure you can bolt some wheels to it and drop in an engine, but why do that when you could use a car instead? Insofar as concurrency is concerned with D, I'd prefer to focus on tight integration with "control" languages such as Erlang and to add a few simple constructs to make more traditional imperative concurrency easier. Just give me a reason to use D instead of C/C++ for the areas I care about: * Embedded systems programming * General systems programming * Optimization points for programs in less performant languages (Python, Erlang, whatever) In these arenas, the only mainstream competitors for D that I'm aware of are C and C++ (and maybe Java, if you count it as an embedded language). That's it. And this field isn't liklely to expand any time soon. Further, I think we've gotten to the point where there's no longer any compelling reason to write monolithic programs entirely in a single language, so why even try to be the go-to language for every problem? Particularly if doing so risks diluting the strength of one's offering in another area. I could be totally off base here, but I think that D's advantage over C is its support for sensible OOP, optional garbage collection, Unicode, and elegance / understandability (lack of a pre-processor, delegates, etc), with its semantic simplicity as an additional advantage over C++--it's far easier to create a compiler for D than it is for C++. This means that I feel D is more powerful than C, easier to learn than C++, and in my experience is prone to fewer bugs (by virtue of its syntax) than either language. These are its core strengths, and I worry about any feature that threatens to dilute them. On the other hand, I suppose it's difficult to sell a language to a C/C++ shop without a list of bullet points. C++ folks often insist on logical const features, etc. Also, I could just be worrying about nothing and all this stuff will turn out to be the bees knees for D. My initial impression is simply that we're trading away too great an increase in complexity for the benefit offered by the 2.0 rendition of const. All that said, I do think that CTFE is an absolutely fantastic option for certain programs, and pure functions seem to be a great way for extending this feature quite naturally. If the goal is simply blistering end-to-end runtime performance then there may well be something too all this mess. I only wish that it could be done in a way that doesn't bring so much baggage along with it. We went from one way to declare functions in D 1.0 to at least four: unlabeled, const, invariant, and pure, all of which cover the same basic conceptual domain. Sean |
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Janice Caron | Janice Caron wrote:
> On 01/04/2008, Tim Burrell <tim@timburrell.net> wrote:
>> Instead of going ... the way D is
>> going now ... why not take the time to find a really
>> good, simple and effective solution?
>
> <slaps head> Doh! Why anyone else think of that before!?
I get the sarcasm, and it's funny... but it rings a bit of truth too. Nothing has been proposed for D and concurrency thus far! I think it's an important discussion too.
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Craig Black | Craig Black wrote: > The D community has been working on this problem for about a year now. I'm don't know if such a "simple effective" solution exists as you imply. And even if it does, if it takes us another year to figure it out, then that's a year that we could be doing more productive things. Working on const and working on a scheme for doing parallel programming are basically completely unrelated. Yeah you may need one to do the other, but a means for parallel programming isn't born out of proper const support. > Yes, I think the solution to scalable concurrency will probably be a systems programming language that supports multiple paradigms. D is a good candidate here, being very multi-paradigm. I still don't think writing scalable multithreaded apps will ever be easy. Definitely not. But there are ways to make it easier. Currently D employs none of these. Const will not improve the situation. > My suggestion would be to implement a C++-like "logical const" system using the const keyword, and use the invariant keyword for Walter/Andrei's transitive const. Maybe that would give us the best of both worlds. I think I agree with you. I'd just really like to see this debacle sorted out so some real effort can be made toward D3 and the possibility of parallel programming support. |
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Craig Black | "Craig Black" wrote
> One idea that I had recently was that the const keyword could provide logical const (for developers) and the invariant keyword could provide transitive const (for the compiler).
Making D not support logical invariant does not make it impossible to have logically invariant functions as I have already demonstrated. If there is a way to legally implement logical invariant, then I think the language should not impose that restriction. Imposing a restriction that you cannot have logical invariance is just going to be a nuisance for those who want to do it, and it does not provide any benefits for compiler optimization.
The only place where it is important to ban logical const is for pure functions as far as I can tell, and that can be dealt with when pure functions are introduced.
-Steve
|
April 01, 2008 Re: Fully transitive const is not necessary | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | Sean Kelly wrote: > I'm currently of the opinion that the ideal solution is to use a functional > language like Erlang for the concurrency aspect and then to use a language > like C for the performance-critical or systems bits. For example, from a > statistic I saw somewhere, the Ericsson switch code has approximately as > many lines of C code as it does Erlang code (a few hundred K of each), with > the structure I've described. Agreed. But what we really need is a language that's also good at shared memory parallelism (Erlang's really geared toward shared-nothing setups -- good back in the day, but today a modern shared memory compiler can perform suitable optimizations). We really only have Fortran and C++ that currently fit the bill -- but perhaps D could be that language? > I'd modify that by saying that adding concurrency support makes an > imperative language ridiculously complex. The problem (as someone in > this NG put it) is that while functional languages describe /what/ should > happen, imperative languages describe /how/ it should happen. In > instances where the programmer knows more about how to optimize > the process than the compiler may, using an imperative language is > clearly the correct solution... and systems languages are clearly necessary > when systems work is to be done (ie. operations involving direct memory > access, etc). But it's fairly well accepted these days that concurrent > programming is just too difficult for most programmers if they have to > worry about the 'how' part. At best, most such programs achieve only > a large-scale granularity as tasks with logically distinct divisions are > manually separated out into jobs for thread pools and the like. I agree with you here on all parts except the one that an imperative language needs to become complex by adding concurrency support. It's all about how it's done. You're right, we need to be describing what, not how. I think I'd call D a multi-paradigm language in the same way I'd call C++ multi-paradigm. It's all how you use it. If we had some proper multiprogramming capabilities it could very well be a description of what, and not how. That'd be the goal for sure. > I actually think that the const system could help somewhat. Pure functions, > for example, offer guarantees that are fairly similar to what a functional > programming language offers. However, for such an approach to compete > with a true functional language the D programmer would have to use pure > functions almost exclusively rather than sparely, which is what I expect. And > in the case that the programmer uses pure functions exclusively, what he's > doing is mimicking functional programming in a language that isn't naturally > suited to it. So in short, I think the pure/const/whatever stuff may actually > get D part of the way there, but I also think that it's a bit like using a boat > in an automobile race. Sure you can bolt some wheels to it and drop in an > engine, but why do that when you could use a car instead? Yeah I hear what you're saying here. It's a tough thing, because you definitely don't want the bolted on after-thought feeling. On the other hand it works pretty good for C++, and since D is directly competing against other system level languages (like C++) it's really not going to be a competitor in the upcoming parallel battle if something's not figured out. > In these arenas, the only mainstream competitors for D that I'm aware of > are C and C++ (and maybe Java, if you count it as an embedded language). > That's it. And this field isn't liklely to expand any time soon. Further, I > think we've gotten to the point where there's no longer any compelling > reason to write monolithic programs entirely in a single language, so why > even try to be the go-to language for every problem? Particularly if doing > so risks diluting the strength of one's offering in another area. You make good points. And I agree with everything you say. I just keep wanting to hold onto the idea that it should be possible to provide a reasonably simple way to give parallel programmers a hand when using D. Hell I'd even be happy with an implementation of the upcoming OpenMP 3.0 draft. It's not original, but it'd sure as hell help! > performance then there may well be something too all this mess. I only wish > that it could be done in a way that doesn't bring so much baggage along > with it. We went from one way to declare functions in D 1.0 to at least four: > unlabeled, const, invariant, and pure, all of which cover the same basic > conceptual domain. I feel the same way. |
Copyright © 1999-2021 by the D Language Foundation