My recent work on Professional F# 2.0 has left me thinking a lot about the nature of abstractions.
In computation, different types of abstraction are trade-offs between clarity and freedom in different areas. As you move away from the machine architecture, you lose the freedom to push bits around in whichever way you might want. In the case of garbage collection, this means you might not be able to hand optimize your memory usage. For managed code, this might mean you can no longer hand optimize your code to a CPU-instruction level. With even a small degree of abstraction, your choices are cut down to only a small subset of what was previously possible.
In exchange for this loss, you get to think about problems in a constrained way. This means you can make more valid assumptions about what is going on. You can express ideas cleanly, without additionally expressing the machine’s management of the resources involved in those ideas. You can manage less and express more.
The benefit of these constraints goes beyond just what you personally have to worry about while programming. Some types of abstraction, such as function signature standardization or managed assemblies, enable post-compilation binary compatibility. Other types of abstraction, such as immutability, allow the compiler to do high level optimizations beyond what was previously possible.
So, while you might be able to do everything with the JMP instruction that you can with C#’s foreach, it takes much more effort to express the same idea with JMP, and a lot more can go wrong. Similarly, while you may be able to express all of the ideas in C# that you can in F#, it takes much more effort to express those same ideas in C#, and a lot more can go wrong.
And who knows… In the future, we F# users might even get some sweet optimizations out of the deal.
Enjoy this post? Continue the conversation with me on twitter.