Becoming More F#unctional
In Gestalt Psychology, there is a concept of "Functional Fixedness". It's a cognitive bias that prevents individuals from using tools in ways that aren't traditional. A basic example of this would be someone using a flathead screwdriver to tighten and loosen screws but wouldn't think to use it to pry open a paint can. This example is contrived, but it challenges how I approach problems daily. One area in which I never expected this idea to show up was programming.
Everything's an Object
The first programming language I ever learned was C++. Partly because it sounded the coolest, and mainly back in the late aughts, searching for "What is the best programming language?" on Google would yield some variation of "Why, C++, of course!". So, my first exposure to programming was object-oriented.I then pivoted to Java due to its garbage collection and friendlier syntax. After that, it was Python, and then ultimately, landing on C#, which I have been working with professionally ever since. Programming is my tool, and I only knew the "Object-oriented" way of using it. It's easy to imagine that object-oriented programming is the be-all-end-all of programming. After all, OOP is modeled after the real world, where a class can represent everything, and polymorphism allows us to create subtypes from those things. To quote the creator of C++, Bjarne Stroustrup, "A fire engine is kind of a truck, which is a kind of a car, which is a kind of a vehicle." It fits nicely with the way we look at things in our daily lives.
What Does "Functional" Mean, Really?
After a considerable amount of time researching programming languages, you realize that object-oriented languages are almost always accompanied by a paradigm called "functional programming." C++, C#, Java, JavaScript, Python, Rust, and Go all have this paradigm. I mostly brushed this off, thinking that it just means these languages have functions, but it's far more nuanced than that. Functional programming encompasses many different concepts:
- Declarative programming: Where you describe what you want the program to do, whereas imperative is describing how the program will do something.
- Pure Functions: Where functions have no side effects.
- Higher-Order Functions: Where functions can return other functions as their result, allowing for functions to be composed together.
- Immutability: Where variables cannot be modified once they are declared.
These are just a few concepts that make functional programming unique but can make it harder to grok.
Using The Tool Differently
Once I realized that OOP wasn't the only way to program, it immediately piqued my interest. Since I was already deep in the .NET ecosystem, what better way to learn about functional programming than to dive deeply into F#? I was immediately lost. F# seemed more akin to Python than C# but was missing many familiar things. While loops and if-statements were still there, they were seldom used over recursion and match expressions. It almost felt like I was learning to program all over again (in a way, I was). What opened my eyes was picking up the book "F# in Action" by Isaac Abraham. His book recognizes that C# and F# are not one-to-one, but he also understands that it's easier to learn that way. He does a great job describing concepts that are familiar and others that are brand new. Ultimately, keeping an open mind and allowing myself to think of programming outside of an object-oriented mindset made me understand and get excited to try something new.
A Paradigm Shift?
While functional programming is an interesting concept, it isn't going to replace my day-to-day work. It's fun to use and has changed how I approach problems and come up with solutions, so the value isn't just in having a new tool under my belt (or, rather, a new way to use a tool) but also in changing my perspective on building software. A word of warning, though: once you pick up functional programming, it may be difficult to put down!