Since Functional Programming (FP) is a paradigm, it is not limited to a specific language. You can write functional programs in any language, although it is quite ugly and tedious in some. Here I will demonstrate that the D programming language is very suitable for FP.
For the basics, D has first-class anonymous functions and closures (aka delegates).
auto square = function int(int x) { return x * x; }
int exponent = 2;
auto square = delegate int(int x)
{ return pow(x, exponent);}
There is even some syntactic sugar for lambda expressions. For example, the following is an equivalent definition.
auto square = (int x) => x * x;
The D standard library provides the standard FP convenience functions. We have to admit that currying and composition is more elegant in Haskell.
import std.algorithm: map, filter, reduce;
import std.functional: curry, memoize, compose;
Another point of FP is that objects usually are immutable.
Compared to C++, the D type system provides better
tools for immutability.
You can qualify variables as immutable
,
which is similiar to C's const
and Java's final
,
but it is transitive.
Transitivity means that everything reachable
by an immutable pointer is also immutable.
The D beginner is often confused that there is also const
,
which improves safety when casting.
Basically, if you have a function which does not mutate an argument,
then qualify it const, but not immutable.
The point is that D does not implicitly cast something to immutable,
but you can cast to const.
Hence, your function can be called with mutable arguments too.
In contrast, in C++ functions are often declared twice,
once const and once non-const.
void foo(const int x);
const int a = 11;
int b = 36;
immutable int c = 42;
foo(a);
foo(b);
foo(c);
Another point that is stressed in FP is purity.
In D you can mark your functions pure
,
which means
- can not read or write global or static (mutable) state
- can not call impure functions (includes IO).
However, there are some pragmatic loopholes.
For example, a pure function can throw exceptions
(use nothrow
to prevent that too),
can terminate the program,
can allocate heap memory (although memory management is global state), and
can do impure things within a debug statement.
Note that pure functions can modify their arguments
and they can return mutable references.
To prevent that, you can use const/immutable qualifiers.
Haskell is famous for lazyness,
although that is not a strict requirement for FP.
In D arguments can specifically marked for
lazy evaluation.
For example, the string argument for a log function
is often constructed through an expression.
With lazy
that expression is only evaluated,
if logging is enabled.
void log(lazy char[] dg) {
if (logging)
fwritefln(logfile, dg());
}
void foo(int i) {
log("Entering foo() with i set to " ~ toString(i));
}
Functional programmers often boast about the safety in their language. D has a whole lot of features for safe programming. For example
- the @safe annotation to prevent undefined behavior
- contracts for additional checking
- -safe compiler switch to disable unsafe language features
- scope guards to not forget cleaning up at the end of a scope
- a unittest keyword to keep tests right beside the code
Thanks to Vladimir Panteleev and Ali Çehreli for their advice. There is a discussion about this article in the D forum.
On 2015-08-19, I gave a talk about this at the Functional Programming User Group Karlsruhe. See slides as OpenDocument or exported pdf.