Lets discuss what are we trying to achieve by applying functional programming to game development
Artūras Šlajus
2022
Let's discuss what we want to achieve with all of these techniques.
We want these things:
Why do runtime errors happen?
Runtime errors are always failures in the code.
A runtime error happens when:
Runtime errors are completely avoidable.
In theory, if there was no way to abort the program (either by throwing an exception or just killing the process), you would be forced to handle everything.
However, it is often more practical to assume the situation is exceptional and abort the program instead of handling things.
Problems start when ordinary situations are being treated as exceptions.
Functional programming helps us with this goal in a couple of ways:
Either<IOError, string> readFile(string path);
// instead of
File openFile(string path);
Either<ImmutableList<IOError>, ImmutableList<string>> readFiles(
IEnumerable<string> paths
) => paths.Select(readFile).validateAll()
// The length of an array is never negative, why is `int` used here?
public uint LengthU<A>(this A[] arr) => (uint) arr.Length;
However, we can avoid some of them by:
Option<A> RandomElement<A>(this IReadOnlyList<A> list);
// instead of
A RandomElement<A>(this IReadOnlyList<A> list)
We can't avoid all logic errors in our code.
// If you have an instance of this type, it means that
// the inner string was a valid Unity asset ID at the
// time of creation of this structure.
class AssetGuid {
public readonly string id;
// Private constructor
AssetGuid(string id) => this.id;
public Either<Error, AssetGuid> create(string id) =>
/* ... */;
}
However, we can avoid some of them by:
We can't avoid all logic errors in our code.
struct HitPointsWereReplenishedProof {}
HitPointsWereReplenishedProof replenishHitPoints();
void applyAttackDamages(
HitPointsWereReplenishedProof proof
);
However, we can avoid some of them by:
We can't avoid all logic errors in our code.
At the end of the day the goal is simple:
However, we can avoid some of them by:
We can't avoid all logic errors in our code.
Code is written once, but read many times. Often, a lot later.
We need to make sure that anyone after any amount of time would be able to:
To do that, we'll cover:
Tests are important for:
Pure functions are ultimately testable:
DateTime calculateLevelUpTime(
DateTime now, Level level
) =>
now + level * 1.minute();
// instead of
DateTime calculateLevelUpTime(Level level) =>
DateTime.Now + level * 1.minute();
Next we will start looking into the basic blocks of functional programming.