The path from C# to F# began when I started mentoring a new-to-programming Nationwide employee. This person didn’t have any alliance with a backend technology. I suggested to them to pick up F# - it’s getting popular and is a very versatile language, so that's when we both started our journey to learn it.
I had only touched on F# in the past, I had watched all the videos about it, done all the tutorials - but I had never put “pen to paper” so to speak.
So I had a go myself, you can watch what I found here:
I've also written about my findings on my journey so far below:
What is F#?
F# is a multi-paradigm programming language developed by Microsoft Research. It is developed on top of Intermediate Language (IL) - this is the same language that C# and VB.NET compile down to as well. You guessed it - they’re all interoperable.
There should be no fear about pulling in a C# .NET assembly into an F# file and accessing those APIs.
F# is a functional/object oriented (OOP)/meta language. It is a function-first language with concepts such as Partial Application, Pure Functions and Function Composition, Pattern Matching and Expressions.
Also, because of the interoperability with .NET, F# works with .NET Core - this means that you can use it cross-platform.
I dislike OOP
There are plenty of reasons to love OOP, but many of these can also be reasons to dislike it.
Personally, since going on the journey, I have started to dislike a lot of things about OOP. Funnily enough, the dislike is mainly around the use of objects all the time. With class based OOP languages, every time you want to do something, the knee-jerk reaction is to start outlining a class, public class DoSomething, and then to start laying out methods which will enact the process.
Why can’t we just start creating functions?
In the below C# code sample, we can see heavy uses of types, where there are property, method and argument declarations. There are a lot of semicolons, braces, accessor keywords - in my mind (after journeying), that's a lot of noise.
Now let's compare that to F#
The first difference anyone can make out is the sheer terseness of the code. This is a like-for-like replacement. In comparison, right away there are no braces, no semicolons and 1 type declarations. It is also important to note the unwrap function, the weird |> symbol and what value the specialValue contains.
Why do we have the unwrap function? When we declared ValueHolder, we declared a compile time type that is not any other type apart from ValueHolder. So we need a way to get the int value - that the type was based upon - out of ValueHolder. It's important to remember F# doesn’t do coercion.
What value does specialValue hold? Unit type. Why? F# is an expression-based language. That means everything returns a value from 'if statements' to 'for...loop' statements and everything in between. The sprintf method is for piping text to stdout. It will return a unit type because it has no other return type (much like void - however that can’t be extracted to a variable).
Let’s talk about |>, the pipe forward operator.
Deep dive (pipe forward)
First C# sample:
Second C# sample:
So what’s the difference? The first sample might make the first crack at creating a program that has multiple steps in a process. This is one of the reasons to dislike OOP - the first tool we grab for is a class and we start defining methods on it.
The second sample has been refined and is purely useful for eliminating those “intermediary” values. It looks cleaner and it feels nicer to use. However, you’re responsible for doing this as it’s not baked into the language.
Let’s take a look at the F#:
So with F#, we immediately grab functions, three of them to be precise. Now we see the use of this pipe forward.
What does it do? Simply, it takes the result of execution of one function and passes it as the first argument to the next function. The key thing to remember here, is you may think that the first function execution looks like doIt 0 10. But, that is incorrect. Partial application is something that F# handles under the hood, which means is that for every argument you leave off the end of a function, invocation will create a new function. Those arguments that were left off will now be the arguments to the newly created function. So, when we call doIt 10, we are actually creating a new function, something like x => doIt 10 x.
The difference here is that F# (through the use of partial application and pipe forwarding) has created the “fluent” like api that we created in C#.
We’ve looked at executing process, so now how do we pass the data?
Objects / representations
Following the theme of this post, the C# code:
The key thing to note here is the Person object. I’ve created an “immutable” class, where the properties can only be set on initial creation. To create a new object based on the previous example, I’ve had to create this Clone method. Every time I have to add new properties to this class, I have to modify this Clone method.
So what can F# give me?
Wow. We’ve created all the original functionality in just one line. One theme running throughout has been the lack of type declarations in F# and here is no exception. When I declare Jeff, because F# works on structural equality, it can tell that Jeff is a Person. Finally, with just a simple keyword - 'with' - a record can be copied and new values given to selected properties.
Now let's look at the king of F# - pattern matching:
So, what should be clear is that to use match, it is match test-expression with.
Now, all the examples show the test expression being a type, or list. Test expression can also be traditional conditional expressions. But, this is where match truly has no equal, because this simple expression can handle all the touch discriminative jobs. Whether it be number comparison, list matching, record matching or further conditional matching, it can handle it all. To start your match journey here are some key resources:
We’ve gone through some of the most influential parts of my journey to loving F#. Because I am a C# dev at heart, going from that to F# has involved a lot of changes.
Most importantly, when learning any new language understanding the syntax is key, so we should always be playing around with snippets, running them and seeing how we can change them so we can start drilling that new syntax into our minds.
Once we’ve done that, we can start to convert our C# thinking across to F#. It is not always possible to convert C# to F# verbatim. As you can see, using a few small features of F# can totally transform the way the code looks.
Here is the perfect book to start transferring our knowledge:
- No Billion Dollar Mistake (nulls)
- Expression, no statements
- Active Patterns (match beefcake)
- Lists / Arrays / Sequences
- Sequence Expressions ( [ for i in 1..10 -> i ] )
- Attach ::
- Concat @
- All of the List APIs
- Ionide in VSCode
- FSI instant run
- FSI instant run
What I still can't get my head around
- Dependency Injection
- Partial Application
- Partial Application
- ASP.NET WebApi / Giraffe
- Why won’t Mongo work
- SQL Type Provider on Mac
- What is Type Provider
- What is Type Provider
- Codewars - not always algorithms and maths
- DDD - plz, send help Scott Wlaschin