Pure functions in D

Sergey Gromov snake.scaly at gmail.com
Sat Sep 27 17:38:54 PDT 2008


Sun, 28 Sep 2008 07:28:19 +0900,
Bill Baxter wrote:
> But I think a stack overflow is something that doesn't usually occur
> in a properly written program, and they are also are not something you
> can recover from.  Insufficient memory errors can be and often are
> recovered from.   E.g. you try to create a giant image in an image
> editing program, if there's not enough memory your image editor
> *should* realize the image alloc failed and bail out gracefully
> telling you there's not enough memory for that operation.

My thoughts so far.

Since a real purity is unachievable, there are different grades of 
purity which make a practical difference:

A. Independence of an external state in the sense that a pure function 
can be executed in parallel without the need for synchronization.  This 
is the simplest, most obvious and a must have kind of purity.  It also 
allows for an arbitrary execution order of functions with independent 
arguments and results.  Even malloc() conforms to this kind of purity 
because memory management is thread-safe at the low level anyway so it 
can be executed in parallel without compiler taking additional 
precautions.

B. Repeatability of the result that allows to cache parameters and reuse 
the same result if the same parameters are given.  This type of purity 
is way stricter than the first and is harder to check and utilize.

What happens if an exception is thrown?

Purity of type A is perfectly preserved.  Naturally, an exception can 
happily be thrown in parallel and in any order.  Purity of type B can 
also be preserved if an exception is thrown consistently with function 
parameters and compiler is able to prove that.  If another kind of 
exception is thrown, the purity B is broken.

What does it mean, broken purity?  It means that any results of a B-pure 
computation are invalid and must be discarded.  I think that a smart 
compiler should catch exceptions from B-pure functions and, if an 
exception is unexpected, wrap them in a PurityError exception which B-
pure functions must never catch.  Yet this exception does no harm to the 
purity of A-pure functions and can be handled there.

Now to exception handling.  Any exceptions from A-pure functions and 
selected exceptions from B-pure functions are considered the function 
result.  Therefore

try {
  localState = pureFun();
} catch(...) {
  localState = ...
}

is a valid pure statement, it can be executed in parallel and in any 
order with the surrounding statements and localState can be completely 
replaced with a cached or pre-computed value if pureFunc is B-pure.  
This is actually valid for any try-catch statement with an important 
note: the statements inside the try block can never be executed in 
parallel to each other, though B-pure functions still can be replaced 
with their cached values/exceptions.  Also a try-catch block in a B-pure 
function can never catch a PurityError.

The image creation routine in your example is A-pure because it creates 
new image every time it is called with the same arguments.  It can 
safely throw OutOfMemory which you catch and report to the user via 
something completely unpure.



More information about the Digitalmars-d mailing list