The Problem With F# Evangelism
There seems to be a constant struggle to convince seasoned C# developers to give F# a try. Which is a pity because language and concepts deserve better.
A couple of days ago I was out for lunch with some fellow developers, all of them seasoned and proficient to some degree at developing with C#.
I told them that I was currently rewriting a component of a Xamarin app I was working on, from C# to F#, because I had quite some trouble to get things to work with C#. They asked me why I would do that: "After all, F# is a language for mathematicians, right?"
I tried to explain my problem and why F# and its built-in support for structural equality was helping me with it. Then I tried to give them an understanding of some language features I really started to appreciate recently, for example sum types aka Discriminated Unions.
Somewhere on my way I lost their attention. And I guess I know why:
"F# ... Structural equ... bla bla bla ... Discriminated... bla bla bla "
Maybe I am not very good in explaining things (I definitely often am not), but I also recognized that I knew this kind of situation. I have been there before – on the other side of the table.
For a long time I wondered what this functional programming cult was all about, why people talking about it often seem so full of enthusiasm, even ecstatic. And now I sat there, sparkling eyes, talking about things I refused to recognize until a few months ago.
Seemed I got trapped. Now I had the same hard time convincing others of the benefits of F# some people had with me before (hi there 👋🏻).
Starting With F# Can Be As Hard As Quitting Smoking
Disclaimer: No, C# and OOP are in no way like smoking. But if you're used to something so much, it's very hard to change your behavior, or the way you think.
More than a decade ago it took me some years and many attempts to finally quit smoking, and similar was my start with F#. Out of curiosity I attended three small F# workshops over the last years, and everytime I walked out not being enlightened but more or less frustrated.
Not to say that was the fault of the lecturers, but each time I failed to win my fight with that obscure syntax and my own imperative way of thinking.
The Problem With C#: Not Much Of A Problem
Maybe one of the biggest obstacles for F# and functional programming in the .NET world is that C# is a very good language. It's not like Swift vs. Objective-C, where Swift is the obvious choice if you're not masochistic. And C# got a lot of functional elements which we use day by day without thinking, too (for example LINQ and higher-order functions).
So most developers, including me until recently, think there's not much that can't be done in C# in an elegant way, following the known ways of OOP. And therefore often there is no need to think outside the box.
Fundamentalism Is Tedious
Another obstacle that seems to make things more complicated is the other side of the coin that I described earlier as enthusiasm.
It's the total belief expressed by functional programming folks that one language is superior to another. And with the language its concepts. So F# is better than C#, and all object oriented programming (OOP) is bullshit, because functional programming (FP) is the silver bullet. Period.
That's of course totally subjective and maybe a bit unfair, more like a gut feeling out of following discussions in my Twitter's filter bubble rather than being based on facts. But it has been my perception of the F# community for quite some time.
If you get told that all the knowledge you gained and you worked hard for, the knowledge you're making good money with, is just plain wrong, it very quickly becomes just tedious.
And it provokes a counter reaction: Not being amenable anymore for any argument at all.
First Reveal The Problem, Then Offer A Solution
Of course there are problems which seem to be inherent to OOP. Those problems need to be discussed to create aha moments which then set the stage for smart alternatives and solutions. Maybe more functional alternatives and solutions.
- Hardly repeatable side effects: As a class manages its own state and that state changes multiple times during its lifetime, it's hard to reproduce the exact issue you got with an error report.
- Race Conditions in multithreading environments: Accessing an element you were sure is contained in a list, but now is not (anymore).
- Accidently mutated state: Passing an object to a service method someone else has written, and getting along with the service method's result a mutation of that passed object.
- Passing along countless simple types: Method signatures expecting Integers, Guids or Strings are normal. So instead of that user's id it's also possible to pass his or her age – the compiler wouldn't know.
- Having objects with invalid state: Let's say there is a class called Result which has two properties: ResultType, which can be Error or Success, and a simple ErrorMessage. Now it's possible to create a result of type Error, but without setting the ErrorMessage. And if the type is Success, the ErrorMessage would always be null.
Almost every code base I know suffers from problems like those described here.
It's possible to fix all that with C#, of course. But it requires a lot of experience, effort and sometimes dirty tricks.
I remember a case where I serialized/deserialized an object before passing it to a service I wouldn't trust, just to make sure my original object wasn't manipulated.
And if it's time to start thinking about workarounds and hacks in C#, it's maybe the best moment to introduce some neat and elegant ways how it could be handled if only the code was written in F#. :-)
Do you have a software project you’d like to discuss?