On 1-dic-10, at 04:52, Jesse Phillips wrote:
Fawzi Mohamed Wrote:
The thing is that a lazy structure is very useful in functional
programming.
A lazy evaluation is something that should be possible using pure and
immutable.
I find it jarring that to do that one has to avoid D pure and
immutable.
Don't know what you mean by this.
a lazy list (for example one that list all natural numbers) cannot be
immutable, without the possibility of a backdoor because all the
"next" elements will have to be set at creation time.
(Lazy structures can be seen as memoizing a value produced by a pure
function forever (i.e. never forgetting it).
To be able to safely use pure and immutable as I said one would need
some idioms that are guaranteed to be non optimized by the compiler.
for example casting a heap allocated type should be guaranteed to
remain modifiable behind the back:
auto t=new T;
auto t2=cast(immutable(typeof(t)))t;
auto tModif=cast(typeof(t))t2; // the compiler has not moved or
flagged the memory of t, so one can modify tModif.
This code is valid, the requirements placed on cast will not allow
it to move the data. Even types declared to be immutable my be
modifiable when cast to Unqual!(T), but the compiler can not
guarantee these.
If I am wrong, please let me know why.
The code works now, I would like some assurance that
cast(immutable(T)) doesn't do fancy stuff (or some equivalent way to
ensure that *some* idiom will remain allowed.
If you think about it already now with opCast you cannot really know
what opCast does, so a compiler would be allowed to return an
immutable copy, or (if it uses whole pages) make the whole memory as
read only.
clearly this is unsafe and it is up to the implementer to make sure
that the object is really logically const
and no function will see the internal changes.
Yes, and I don't think compiler support adds any more guarantee than
casting those you want to modify in a const function. This Mutable
struct is supposed to help verify only modifiable data is cast:
https://gist.github.com/721066
you example has an error in parallel, this is a good example of why
the casting away should not be made convenient, and a user should just
use well tested library code (for example dong thunk evaluation), that
might be in a special type or mixed in.
You cannot have separate flag, and value without any synchronization,
as other threads could see their value in a different order, so they
could see dirty=false, but still no determinant.
This is somehow related to dataflow variables that can be set several
times, but only to the same value (and indeed with a lazy list one can
allow two threads to calculate the next element, but then only one
should set it (using atomic ops to avoid collistions).
I have implemented DataFlow variables (but using the blip
paralelization, that delays a task that waits, and resumes it when the
value is ready, not with a thread lock) in
https://github.com/fawzi/blip/blob/master/blip/parallel/smp/DataFlowVar.d
using D1
I've taken many example use-cases for logical const and added them
as unittests. I think it is fairly reasonable if I could just get an
answer to my question about concurrency and declaring immutable types.
This is something that should be done sparingly, probably just in
library code implementing lazy evaluation or memoization (but code
that might be mixed in).
Could you give an example of how lazy evaluation is achieved by
modifying state?
lazy structures need to modify the state (as I showed with the linked
list example), lazy evaluation alone does not need to modify the state
(and is indeed possible in D), but the storing/memoizing of the result
needs it. To say the truth in D memoizing can be done with a static
variable, but think about making the singly linked list like that, and
you should immediately see that if gets *very* inefficient.