I was recently faced with the following code which is simple but provided the perfect example to practice some Haskell.
I needed to calculate how many IPs there are in a subnet based just on the CIDR provided. The concept is simple, the CIDR indicates how many bits must match between a route and a destination to determine if a route should be taken.
A typical subnet in a home setup might be something like: 192.168.1.0/24 which means the first 24 bits must match, giving you a subnet range of 192.168.1.0->192.168.1.255, or 256 addresses. However, the network (first) and broadcast (last) addresses are generally unusable, which means the actual usable range is 192.168.1.1->192.168.1.254 or 254 addresses.
So, the answer should be simple: numips = (32-cidr)^2 - 2
.
However, there are two special cases, a direct route (cidr of 32) which has one matching IP. and a point to point route (cidr of 31) which has 2 matching IP’s. Our algorithm above would return -2 and 0 for these special cases, which is less than ideal.
The C++ implementation is certainly not difficult:
int countips(int cidr)
{
switch (cidr)
{
case 32:
return 1;
case 31:
return 2;
default:
return (1<< (32-cidr)) -2; //Use a bitshift to simulate a power of 2 in C
}
}
However, the point of this article is Haskell’s pattern matching with guards, which does make this look a lot cleaner, in my opinion. Patterns match from top to bottom, and the first match is the one returned. The algorithmic nature of a functional programming language is perfect for something like this:
ipcount cidr
| cidr == 32 = 1
| cidr == 31 = 2
| cidr > 0 = 2^(32-cidr) - 2
ipcount _ = 0
Each | symbol introduces a pattern guard, if the guard matches, that code branch is executed. It is very similar to our switch based implementation above. The second case with a _ for the parameter name indicates a wild card, so anything at all (including a string or whatever) that does not match the first version will match the wild card version and return 0. |
I know this is a trivially simple example for people with a background in Haskell, so I would appreciate it if anyone with more experience wants to add to this example or clean up something I messed up.