July 03, 2006
Hasan Aljudy wrote:
> Lars Ivar Igesund wrote:
> 
>> Hasan Aljudy wrote:
>>
>>> damn, this doesn't sound too good.
>>> I for one don't give a damn about const, so why should I be bothered to
>>> think about the constness of my code/classes and add "mutable"
>>> qualifiers all over the place?
>>
>>
>>
>> Hmm, this sounds bad, for you ... you are aware that immutable objects are
>> your guarantee in multithreaded system to have safe sharing of data? Have
>> you looked at any well designed library in languages like C++, Java or C#?
>> Try to count the immutable classes present, I think you'll find quite a
>> few.
> 
> 
> Let me repeat:
> Is there anything preventing anyone from creating immutable classes?

Technically no.  Although at present, there are precisely two possible methods, and both require wrapping every single field up with a D-style property.  (True properties could actually have the potential to make this simpler... but we don't have those either.)

Method #1 - Write the class as innately immutable, then write a mutable subclass.  This is the methodology at work in Mango, for example, where the module mango.io.FilePath actually defines two classes, FilePath:Object and MutableFilePath:FilePath.

Method #2 - Same as above, but with one class that has a pair of methods (mutable() and immutable() for example) that activate/deactivate mutability, usually locking on something such as instance of another class (the identity of which doesn't particularly matter). For (quick'n'dirty) example:

# class Foo {
#   private Object mlock ;
#
#   public void mutable (Object o) {
#     if (mlock is o)
#       mlock = null;
#   }
#
#   public void immutable (Object o) {
#     if (mlock is null)
#       mlock = o;
#   }
#
#   private void checkLock () {
#     if (mlock !is null)
#       throw new Exception("immutable object");
#   }
#
#   /* ... everything else ... */
# }

Now then, if in class Foo we want to define some property bar as an int, we now have no choice but to do something like this:

#   private int p_bar ;
#
#   public int bar () { return p_bar; }
#
#   public int bar (int x) {
#     checkLock;
#     return p_bar = x;
#   }

Imagine if class Foo has twenty different fields/properties, most of which surely don't require any special processing -- meaning that the property wraps are pointless like the example above, although canonical.  Also imagine you have classes you wish to use as immutable, which you have no control over the design of: then you have to write an entire wrapper class, fully re-implementing the original class's readable interface.

Ew.

-- Chris Nicholson-Sauls
July 03, 2006
Chris Nicholson-Sauls wrote:
> Technically no.  Although at present, there are precisely two possible methods, and both require wrapping every single field up with a D-style property.  (True properties could actually have the potential to make this simpler... but we don't have those either.)
> 
> Method #1 - Write the class as innately immutable, then write a mutable subclass.  This is the methodology at work in Mango, for example, where the module mango.io.FilePath actually defines two classes, FilePath:Object and MutableFilePath:FilePath.
> 
> Method #2 - Same as above, but with one class that has a pair of methods (mutable() and immutable() for example) that activate/deactivate mutability, usually locking on something such as instance of another class (the identity of which doesn't particularly matter). For (quick'n'dirty) example:

Method #3 - each mutator function/method actually creates a clone of the original, mutates it, and returns that instead.

Method #1 & #2 are typically the most efficient, but can expose an achilles heel. For instance, suppose you had an immutable String class? At some point, it will need to expose the content therein -- perhaps via a toString() method, or perhaps something more subtle? At that point, the class either returns a .dup of the internal content, or it exposes the internal content directly by returning it (as an array). Neither of these options are entirely attractive, and it's the reason why fully immutable classes/structs are not entirely practical in D at this time.

This "return value" concern is also the reason why I believe that a "minimalist" support for "read-only" would be to protect just the return values. With the latter, one could happily return internal aggregate content (as arrays) and be safe in the knowledge that the compiler will catch pedestrian errors. However, I'd much rather see const-by-default instead -- that's a more practical solution, and should offer notable opportunities for optimization.

One should note that the goal of such mechanisms is to tighten up the "contract" between caller and callee. This becomes increasingly important within multithreaded designs, to the point of becoming a fundamental requirement in some ways (in terms of robustness, simplicitly, efficiency, deterministic behaviour, etc). CoW is another mechansism intended to achieve similar results, but the key distinction is the lack of an enforceable contract. With mutable/immutable decls, the compiler has the necessary information to catch cases where the contract is being broken. With CoW, the compiler does not have that meta information available (whether the design says it's OK to alias or not).

Of course, this won't stop some determined fool from bypassing the contract altogether: even with immutable-by-default, one could presumably cast() lvalues in some manner to subvert the contract. But that's not the point -- the compiler would be looking to catch honest mistakes, and not trying to cure stupidity :p

Hope this helps.
July 04, 2006
kris wrote:
> Chris Nicholson-Sauls wrote:
> 
>> Technically no.  Although at present, there are precisely two possible methods, and both require wrapping every single field up with a D-style property.  (True properties could actually have the potential to make this simpler... but we don't have those either.)
>>
>> Method #1 - Write the class as innately immutable, then write a mutable subclass.  This is the methodology at work in Mango, for example, where the module mango.io.FilePath actually defines two classes, FilePath:Object and MutableFilePath:FilePath.
>>
>> Method #2 - Same as above, but with one class that has a pair of methods (mutable() and immutable() for example) that activate/deactivate mutability, usually locking on something such as instance of another class (the identity of which doesn't particularly matter). For (quick'n'dirty) example:
> 
> 
> Method #3 - each mutator function/method actually creates a clone of the original, mutates it, and returns that instead.
> 
> Method #1 & #2 are typically the most efficient, but can expose an achilles heel. For instance, suppose you had an immutable String class? At some point, it will need to expose the content therein -- perhaps via a toString() method, or perhaps something more subtle? At that point, the class either returns a .dup of the internal content, or it exposes the internal content directly by returning it (as an array). Neither of these options are entirely attractive, and it's the reason why fully immutable classes/structs are not entirely practical in D at this time.
> 
> This "return value" concern is also the reason why I believe that a "minimalist" support for "read-only" would be to protect just the return values. With the latter, one could happily return internal aggregate content (as arrays) and be safe in the knowledge that the compiler will catch pedestrian errors. However, I'd much rather see const-by-default instead -- that's a more practical solution, and should offer notable opportunities for optimization.
> 
> One should note that the goal of such mechanisms is to tighten up the "contract" between caller and callee. This becomes increasingly important within multithreaded designs, to the point of becoming a fundamental requirement in some ways (in terms of robustness, simplicitly, efficiency, deterministic behaviour, etc). CoW is another mechansism intended to achieve similar results, but the key distinction is the lack of an enforceable contract. With mutable/immutable decls, the compiler has the necessary information to catch cases where the contract is being broken. With CoW, the compiler does not have that meta information available (whether the design says it's OK to alias or not).
> 
> Of course, this won't stop some determined fool from bypassing the contract altogether: even with immutable-by-default, one could presumably cast() lvalues in some manner to subvert the contract. But that's not the point -- the compiler would be looking to catch honest mistakes, and not trying to cure stupidity :p
> 
> Hope this helps.

Not intending to start a long drawn out 'const' discussion again but...

Multithreading appears to be the hardest of hardware parallelism to implement.  Race conditions will not magically disapear with const-correctness (Delegates alone negate that).

I really believe that true hardware parallelism will come from data parallelism:

Data[1000]	mydata;
foo( mydata );

processor 1 operates on mydata[0-499]
processor 2 operates on mydata[500-999]

Data parallelism is (in the general sense) inherently faster,doesn't require locking, and is not prone to things like task-switching, locks, race conditions, etc

Apparently NVidia has figured this out already.
Of course this requires a more relational/vector view of the data than is currently mainstream.

Here is a relevant presentation:
http://www.cs.princeton.edu/~dpw/popl/06/Tim-POPL.ppt

(PDF here:http://www.st.cs.uni-sb.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf ).

Const has some nice properties, but I rate it about a 5 on a scale of 10 for dire features.  Yes, I know libraries benefit from it.
I would counter that library authors have serious design issues to consider even with const, and that COW is not a bad tradeoff in the meantime.

-DavidM



July 04, 2006
David Medlock wrote:
[snip]
> Not intending to start a long drawn out 'const' discussion again but...

nor I :D


> Multithreading appears to be the hardest of hardware parallelism to implement.  Race conditions will not magically disapear with const-correctness (Delegates alone negate that).

Not all of them, no. But a surprising number of instances do; almost as if by magic. I'm just speaking from experience only. Nothing theoretical or empirically measured.

BTW: I'm one of those who feel the multithreading model is just too hard for 95%+ of programmers to get right. I'm among that 95%, although I've written OS schedulers and so on. It's just too easy to make a subtle mistake, and not being able to mechanically 'prove' the correctness of a typical/traditional multithreaded design illustrates just how bad the situation really is.

But that does not hinder the value of immutability ~ it's very useful for single-thread designs also.


> I really believe that true hardware parallelism will come from data parallelism:
> 
> Data[1000]    mydata;
> foo( mydata );
> 
> processor 1 operates on mydata[0-499]
> processor 2 operates on mydata[500-999]
> 
> Data parallelism is (in the general sense) inherently faster,doesn't require locking, and is not prone to things like task-switching, locks, race conditions, etc

No question about that, but you still need rendezvous points or some other form of synchronization, unless there's no (fan-in) response involved?

The thing is that immutable, as part of a contract, makes the conversation between callers and callees that much more deterministic. This is a useful artifact regardless of how many threads or how much hardware duplication there is - even single threaded. After all, don't we want the compiler to tell us when we're doing something contrary to someone else's design? It may even be beneficial somewhere in the above example.

But yeah ... no long, drawn-out discussion needed :)


> Apparently NVidia has figured this out already.
> Of course this requires a more relational/vector view of the data than is currently mainstream.
> 
> Here is a relevant presentation:
> http://www.cs.princeton.edu/~dpw/popl/06/Tim-POPL.ppt
> 
> (PDF here:http://www.st.cs.uni-sb.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf ).


Thanks; they look interesting. There are a number of excellent alternatives to multithreading ~ you might be interested in perusing for occam/tputer/csp/jcsp etc


> 
> Const has some nice properties, but I rate it about a 5 on a scale of 10 for dire features.  Yes, I know libraries benefit from it.

Yes; like everything else, the priority is often dependent upon what you doing.


> I would counter that library authors have serious design issues to consider even with const, 

That can be true. But as those are resolved, the need for const becomes  more pronounced :D

> and that COW is not a bad tradeoff in the
> meantime.

Sure; as a stopgap measure it is ok. It's the lack of 'enforcability' that makes it less than suitable for a long-term solution ~ IMO

-----

But it's not at all clear what your messsage is, David. Are you speculating that D is currently too immature for immutability to be useful? Or, that the multithreading model should be re-evaluated? Asking only because I'm not sure what you're getting at overall?


July 04, 2006
kris wrote:

> David Medlock wrote:
> [snip]
> 
> But it's not at all clear what your messsage is, David. Are you speculating that D is currently too immature for immutability to be useful? Or, that the multithreading model should be re-evaluated? Asking only because I'm not sure what you're getting at overall?
> 
> 

Basically that multithreaded as a 'hold the computers hand' is a semantic dead end street.  Sure it works in small cases, but for programming in the large you hit Ahmdal's Law, among other issues.

No question some programmers can do it, but there are better methods imo.

Code-level multithreading won't have much(any?) effect on processors such as the Cell from what I have read so far.

Look at that presentation: 500 Gigaflops of computing power on consumer hardware.  It just requires you to follow its vectorized paradigm.

By comparison the first Cray had like 2 MegaFlops of crunching power.
Looking at the Wiki entry for Seymour Cray, he notes that keeping the computer 'fed' I/O data was the trick.

Sounds awfully familliar.

-Just my opinion, of course.
DavidM
July 04, 2006
David Medlock wrote:

> kris wrote:
> 
>> David Medlock wrote:
>> [snip]
>>
>> But it's not at all clear what your messsage is, David. Are you speculating that D is currently too immature for immutability to be useful? Or, that the multithreading model should be re-evaluated? Asking only because I'm not sure what you're getting at overall?
>>
>>
> 
> Basically that multithreaded as a 'hold the computers hand' is a semantic dead end street.  Sure it works in small cases, but for programming in the large you hit Ahmdal's Law, among other issues.
> 
> No question some programmers can do it, but there are better methods imo.
> 
> Code-level multithreading won't have much(any?) effect on processors such as the Cell from what I have read so far.
> 
> Look at that presentation: 500 Gigaflops of computing power on consumer hardware.  It just requires you to follow its vectorized paradigm.
> 
> By comparison the first Cray had like 2 MegaFlops of crunching power.
> Looking at the Wiki entry for Seymour Cray, he notes that keeping the computer 'fed' I/O data was the trick.
> 
> Sounds awfully familliar.
> 
> -Just my opinion, of course.
> DavidM


PS.  D has vector capabilities so I wouldn't rule it out at all.
(Though I don't know what Walter's long term strategy is here...)

July 04, 2006
David Medlock wrote:
e this helps.
> 
> Not intending to start a long drawn out 'const' discussion again but...
> 
> Multithreading appears to be the hardest of hardware parallelism to implement.  Race conditions will not magically disapear with const-correctness (Delegates alone negate that).
> 
> I really believe that true hardware parallelism will come from data parallelism:
> 
> Data[1000]    mydata;
> foo( mydata );
> 
> processor 1 operates on mydata[0-499]
> processor 2 operates on mydata[500-999]
> 
> Data parallelism is (in the general sense) inherently faster,doesn't require locking, and is not prone to things like task-switching, locks, race conditions, etc
> 
> Apparently NVidia has figured this out already.
> Of course this requires a more relational/vector view of the data than is currently mainstream.
> 
> Here is a relevant presentation:
> http://www.cs.princeton.edu/~dpw/popl/06/Tim-POPL.ppt
> 
> (PDF here:http://www.st.cs.uni-sb.de/edu/seminare/2005/advanced-fp/docs/sweeny.pdf ).
> 
> Const has some nice properties, but I rate it about a 5 on a scale of 10 for dire features.  Yes, I know libraries benefit from it.
> I would counter that library authors have serious design issues to consider even with const, and that COW is not a bad tradeoff in the meantime.
> 
> -DavidM
> 
> 
> 


I can see why Nvidia would want to look at concurrency from a data parallelism perspective.  That's what graphics is all about... processing and moving masses of data.  Since the data weighs so heavily in graphics world, is much like a large array and easily partitioned into "work" loads, it seems obvious for graphics hardware manufacturers to look at parallelism from that perspective.  This is a specific case, though, and may not work at all the same way in networks and other generic systems. Adopting such a perspective for all other possibilities may be just as dangerous for general-purpose langauges as it is beneficial for Nvidia's specific field of operation.

Nonetheless, I'm sure it's good to be aware of such methodologies. Looking at things from a different angle is always a good idea.  But certain process algebras like CSP have done a very good job of describing concurrency in a clear and provable fashion.  Feature additons, like immutability, would seem to be highly useful low level features to help strengthen the possible concurrency implementations.

-JJR
July 04, 2006
David Medlock wrote:
> kris wrote:
> 
>> David Medlock wrote:
>> [snip]
>>
>> But it's not at all clear what your messsage is, David. Are you speculating that D is currently too immature for immutability to be useful? Or, that the multithreading model should be re-evaluated? Asking only because I'm not sure what you're getting at overall?
>>
>>
> 
> Basically that multithreaded as a 'hold the computers hand' is a semantic dead end street.  Sure it works in small cases, but for programming in the large you hit Ahmdal's Law, among other issues.
> 
> No question some programmers can do it, but there are better methods imo.
> 
> Code-level multithreading won't have much(any?) effect on processors such as the Cell from what I have read so far.
> 
> Look at that presentation: 500 Gigaflops of computing power on consumer hardware.  It just requires you to follow its vectorized paradigm.
> 
> By comparison the first Cray had like 2 MegaFlops of crunching power.
> Looking at the Wiki entry for Seymour Cray, he notes that keeping the computer 'fed' I/O data was the trick.
> 
> Sounds awfully familliar.
> 
> -Just my opinion, of course.
> DavidM


Yes, it does sounds very familiar -- to take full advantage of specific hardware you may need to step away from the 'norm'. Whatever that 'norm' may be. No surprise then, that it pays to keep an open mind?

You won't hear any argument from me regarding the typical multithreading "paradigm" ... and there are most certainly more effective methods in one manner or another ... there have been for 30 years ... *shrug*

If you intend to go on a crusade, to change the face of multithreading as we currently know and "love" it, I'll sign right up :D

But, again, the state of immutability is *not* married to multithreading -- it just happens to be particularly useful there too :p



July 04, 2006
Sean Kelly wrote:
> Bruno Medeiros wrote:
>> Sean Kelly wrote:
>>>
>>>  > and doesn't D already have a const keyword?
>>>
>>> D has const as a storage attribute, which is a bit different from the above.  D's const may only apply to concrete data types, and implies that the data will never be modified for the duration of the program. This allows the compiler to place such data in ROM and to perform some optimizations that would otherwise not be possible.  But this is quite limited in that it may only be applied to data that can be evaluated at compile-time.
>>>
>>
>> There is a slight difference from D's const and a const that places the data in ROM as you cannot get the address of a D const var (it's not an lvalue).
> 
> Ever tried taking the address of a const string?  That you can't take the address of other const types is simply a result of optimization.
> 

I tried it now, and the const string also can't be taken an address from:

  const char[] str = "ABC";

  void func()
  {
    *(&str) = "123"; // : "ABC" is not an lvalue
  }


-- 
Bruno Medeiros - CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
July 04, 2006
Bruno Medeiros wrote:
> D's const has two meanings. The usual meaning is from the form:
>   const int var = <some constant initializer>;
> and it means that the variable is a compile-time constant and no storage is allocated for it, instead the value is substituted whenever the var is used (like #define, but safer).
> 
> The second meaning is from the form:
>   const int var; // no initializer
> and means that the var must be initialized once in a constructor, and then it becomes non-recursively immutable (meaning you can't change the value of the var, but you can change referenced values). The var has storage and as such is an lvalue.
> This is basically the same as final in other languages (Java, C#), and I wonder why it isn't in D as well(?). Walter said he didn't want a const keyword overloaded with many different meanings.

I agree. I think it's a mistake to have a 'const' that isn't actually const! 'final' seems far more appropriate.