How to use isomorphisms to your advantage
The concept of isomorphisms has its origin in algebra. It means that it is possible to translate back and forth between two concepts without information loss. For example, each positive integer can be mapped to a square number by multiplying it with itself. And for each square number, the corresponding integer can be derived by taking the square root. Since conversions back and forth exist, the concepts of positive integers and square numbers form an isomorphism.
The same principle can be translated to software design concepts and types. Although the concepts may be different, and thus may have different properties, when they are sufficiently similar, isomorphisms may exists so that you can convert on to the other. More specifically, a software design isomorphism can be established if two refactoring operations exist that can transform one concept into the other and vice versa.
Interface to lambda
A lambda function is basically a method reference stored in a variable. For example, this one which always returns true
:
Func<bool> lambda = () => true;
Other parts of the code can accept a lambda as follows:
public void DoSomething(Func<bool> isAvailable) {
bool canContinue = isAvailable();
...
}
Suppose that you want to convert it to a type for some reason. You can start by defining an interface like this:
public interface ILambda {
bool GetValue();
}
And add an implementation for the function:
private class Lambda : ILambda {
public bool GetValue() {
return true;
}
}
Then modify the code that accepts the lambda to:
public void DoSomething(ILambda isAvailable) {
bool canContinue = isAvailable.GetValue();
...
}
This is a refactoring transformation since it does not change behavior. It however does change part of the design of the code. The inverse transformation can be made by reverting the steps. This means that lambdas and interfaces with a single method can be regarded as isomorphic: you can convert back and forth as you like.
Of course, once you converted a lambda to an interface, you have further possibilities. For example, you are now free to add other methods to the interface to add new functionality.
Why should I care?
Why should I care? Each concept in software design has pros and cons. To stay with the lambda/interface trade-off: lambdas are easier to define and require much less boilerplate code (a single line of code instead of a class). Ordinary functions can be used as lambdas too. On the other hand, interfaces are more type safe; you may define two separate interfaces with the same method signature and the compiler will make sure that they are not being mixed up. Interfaces can also be further extended by adding new methods and be used to introduce named abstraction levels.
In practice, this means often that a trade-off must be made between concepts to weight their relative pros and cons. If an isomorphic relation exists between concepts, then it does not matter much which of the two concepts is chosen initially. After all, when need arises, it is possible to convert to the other form relatively quickly.
This means that you don’t need to waste time by making a big tradeoff. In case of isomorphic concepts, you go faster by just choosing either of them and converting when the need shows up. That way, you learn fastest which of the two was the right choice.