|
The Transition to Concurrency
Avoid State: Immutable Types
The key to functional programming, which happens to be a very good policy to enforce in general, is avoiding state. Mutable state, that is. Mutable state in software is like moving parts in hardware. It makes your code more vulnerable to external conditions, and it makes it harder to maintain.
To people with no experience with functional programming languages, this can be hard to grasp. Or rather, it can be hard to realize that there is a difference (it was for me, anyway).
In pure functional programs everything is immutable. This basically means that every variable isn't - it's a constant. There is no way to declare a C++ type immutable but what you can do is declare a variable const, which basically means that the type of the given variable is an immutable version of the type specified.
A Simple Example
Consider this simple color class:
class Color
{
private:
double red_;
double green_;
double blue_;
public:
Color(double red, double green, double blue) : red_(red), green_(green), blue_(blue) {}
double GetRed() const { return red_; }
double GetGreen() const { return green_; }
double GetBlue() const { return blue_; }
double SetRed(double red) { red_ = red; }
double SetGreen(double green) { green_ = green; }
double SetBlue(double blue) { blue_ = blue; }
void Multiply(double factor)
{
red_ *= factor;
green_ *= factor;
blue_ *= factor;
}
};
Fairly common style, I would say. Nice encapsulation even though you can read and write all private fields. But if you wanted to, you could easily introduce a contract by adding checks in the accessors and the multiply method.
To make this class immutable, we could change it like this:
class Color
{
private:
double const red_;
double const green_;
double const blue_;
public:
Color(double red, double green, double blue) : red_(red), green_(green), blue_(blue) {}
double GetRed() const { return red_; }
double GetGreen() const { return green_; }
double GetBlue() const { return blue_; }
Color Multiply(double factor) const
{
return Color(red_ * factor, green_ * factor, blue_ * factor);
}
private:
Color& operator = (Color const& a) {}
};
At first glance, the difference may not seem important. The Set* methods are gone and the Multiply method has changed a little. No biggie. But the real change is in the fact that this class is now immutable.
Once instantiated, you have no way to change the contents of the object. You can multiply the color by a factor, but this gives you a new instance. Also, the assignment operator has been declared private which means that it cannot be invoked from the outside, not even implicitly. This makes the class thread-safe even though it contains no semaphores.
You are forced to use it in a way that will work in multiple threads and you can be sure that it will never produce deadlocks or become corrupted by simultaneous access. Introducing a contract in this version means checking the invariant in the constructor - that's all.
|