Pure functional programming in D
Asked Answered
R

1

23

To my mind the power of functional purity is when deep code paths can be verified as side-effect free. What are people's experiences in the scale of the code tree that can be inside a pure specifier, and what of the level of code reuse?

A few things I spotted:

std.algorithm is mostly not marked as pure, but could potentially be largely pure, either by a pure version of algorithms demanding purity of the instantiating function or mixin, or else by the purity specifier itself being statically polymorphic.
Useful convertors like to!string( someInt ) aren't currently pure.

User defined structs seem to have problems (as illustrated below) with:
1. pure destructors on a nested struct
2. a pure postblit function even on a non-nested struct

The following code currently gives multiple errors on DMD 2.052 win 32-bit

struct InnerStruct
{
    pure this(this) {}
    pure ~this() {}
}

struct OuterStruct
{
    InnerStruct innerStruct;
    pure this(this) {}
    pure ~this() {}
}

pure void somePureFunc()
{
    OuterStruct s1 = OuterStruct(); // pure nested destructor does not compile
    OuterStruct s2 = s1;
    InnerStruct is1 = InnerStruct(); // pure non-nested destructor seems to compile
    InnerStruct is2 = is1; // pure non-nested postblit does not compile
}

void main()
{
    somePureFunc();
}
pure_postblit.d(18): Error: pure function 'somePureFunc' cannot call impure function '__cpctor'  
pure_postblit.d(20): Error: pure function 'somePureFunc' cannot call impure function '__cpctor'  
pure_postblit.d(18): Error: pure function 'somePureFunc' cannot call impure function '~this'  
pure_postblit.d(17): Error: pure function 'somePureFunc' cannot call impure function '~this'  
Rhatany answered 28/4, 2011 at 0:13 Comment(0)
A
22

In theory the point of pure in D is that it's supposed to allow guarantees that a function is side effect free regardless of how that function is implemented. There are two kinds of purity in D:

  • All functions marked pure are weakly pure. They may not access any global mutable state (global variables, thread-local variables, static variables, etc.) or perform I/O. They may, however, mutate their arguments. The point of these functions is that they may be called from strongly pure functions (detailed below) without violating the guarantees of strong purity.

  • All functions that are weakly pure and do not have any arguments with mutable indirection are strongly pure. The const and immutable type constructors can be used to guarantee this. (When dealing with structs and classes, the this pointer is considered a parameter.) Strongly pure functions have all of the nice properties that functional programming people talk about, even if they're implemented using mutable state. A strongly pure function always returns the same value for any given arguments and has no observable side effects. Strongly pure functions are referentially transparent, meaning their return value may be substituted for a call to them with a given set of parameters without affecting observable behavior. Any strongly pure function can be safely executed in parallel with any other strongly pure function.

Unfortunately the interaction between generic code and pure (as well as const and immutable) is rather poor. There have been several proposals to fix this, but none have been accepted yet.
\std.algorithm is written to be as generic as possible, so it can't require that its lambda functions and the ranges it accepts be pure. Furthermore, the type system features that were added in D2 are generally the most buggy features in the language, because more basic things have been prioritized ahead of fixing the relevant issues. Right now, pure is basically not usable except for trivial cases like std.math.

Antibes answered 28/4, 2011 at 3:5 Comment(2)
Thanks for your comments. I noticed the weak definition of purity a while back when experimenting, and it is clearly very powerful, allowing full mutable OO programming in pure code, and when creating lazy-evaluation thunks or scheduling promises etc the strong form can easily be asserted by meta-programming on the type qualifiers. I think that for the highly-concurrent vision to really take off, much more needs to be able to be inside a pure envelope. The destructor and postblit issues I mentioned are most affecting me as I need to reference count in pure code, do you think they are bugs?Rhatany
@John: Yes, these are probably bugs.Antibes

© 2022 - 2024 — McMap. All rights reserved.