Introduction
F# is a modern functional language for the .NET platform developed by a Microsoft Research team. It’s been in development since 2005 and made it to version 3.1 as of December 2013. It started as an academic research project and over several years matured to a production-ready language that is used by many commercial companies especially in the financial sector.
The reasons for developing a new language that is much different from C# were:
- switching from the object oriented to functional paradigm that is more suitable for calculations and manipulations with data;
- making use of valuable programming concepts that were not included into C# due to its different ideology;
- avoiding well-known inherent problems of C# that are too late to fix.
In terms of language features, F# is a superset of C#, so everything that C# can do F# can do, too. The opposite isn't the case: F# has a number of things that C# isn’t capable of.
There is a full interoperability between F# and other .NET languages. Once the F# code is compiled to an assembly, it can be used from any .NET-family language like C# or VB.NET. It means that F# code can peacefully coexist with other parts of a system written in C#.
Advantages of F# Over C#
Options Instead of NULL References
C# has a dangerous feature that allows using NULL values instead of objects. This feature is a source of numerous bugs that are easy to plant and hard to detect. Trying to tackle the problem results in adding extra checks throughout the code making it less readable.
F# doesn't allow NULL's, instead the options have to be used. They guarantee safety and serve the same purpose of representing missing objects just like NULL's do in C#.
Algebraic Data Types
C# doesn't fully support the algebraic data types (ADT), which allow complex data structures to be composed out of simple ones. It supports product-types in form of classes and structures, but has limited support for the sum-types, which are represented only by enums.
Conversely, F# has a full support for ADT's and makes it possible to describe business entities more accurately in terms of data structures reducing the chance of misinterpreting the data and resulting in higher quality of code. Basically in F# one can avoid unwanted situations by making them unpresentable, that is simply by leaving no chance to write inappropriate code.
Transformations vs. Mutations
C# encourages mutating the data and using states. It means once an object is created, it undergoes series of modifications throughout its lifetime that change its state. Depending on the present state of the object, certain operations may or may not be allowed on it. It requires a check to make sure the object is in a right state before one can safely use it. Failing to do this is likely to result in a bug causing unexpected behavior and crashes.
On the other hand, F# encourages using transformations rather than mutations. A transformation doesn't modify the object, and thus doesn't change the state. It just creates a new object and passes it on keeping the original object intact. Which means that the object can always be in one single state that was given to it at birth. Having to deal with just one state greatly simplifies the development process and reduces the amount of code, because whenever we see an object we know it's in the only state possible and it's safe to use it without doing extra checks.
Expressions vs. Statements
Since C# relies on states and mutations, the order in which these mutations are applied becomes very important because by switching places of two statements we can plant a bug. So the order of the statements in C# code is another thing to take care of. Contrary, a program in F# is essentially a big expression (a formula) composed of smaller expressions that map the input values to some output values. The order in which different parts of the expression are evaluated is not important, because in side-effects-free calculation model the order of evaluation doesn't change the result. So switching from using statements to programming with expressions results in safer code free of bugs caused by the wrong order of statements.
Keeping Data and Logic Separately
C# encourages mixing data with logic by allowing both properties and methods be declared under the same class or structure. But when it comes to serialization, the logic attached to classes has to be stripped off, that is why the classes have to be transformed to the plain data objects with no logic and only after that passed to a serializer.
Functional programming suggests that you don’t mix data with logic; as a result the serialization to JSON or XML is much easier and straightforward.
Type Inference and Concise Syntax
C# syntax is verbose. It takes significantly more typing to express things in C# compared to the same things written in F#. Verbosity of C# particularly comes from having to specify types of method parameters and their return values. F# has an advanced type inference system that deduces types of values from how these values are used, so in most cases it is recommended to omit types in your code letting F# to figure them out. Having to type less results in higher productivity and better maintainability of the code. Some people find F# syntax more elegant and easier to read.
Up-Down Left-to-Right Evaluation
The order in which files, classes, and methods in C# are declared doesn't matter. On the other hand, in F# the order of declaration is strictly up to the order of files, so you cannot refer to or use something that hasn't been declared before. While this may seem an unnecessary complication, it is rather a good thing, because it imposes more discipline on the developer and makes his intentions explicit.
Inherent Testability and Good Design
Modern object oriented design principles known as SOLID can be easily violated in C#; that’s why it is important to know them and follow them closely. However, the very same principles are in the nature of the functional programming. So the F# language encourages you to code the right way from the start and it takes some deliberate effort to mess things up in F#.
Running in Parallel at Low Expense
Running C# code in parallel is a challenging thing due to the racing conditions, which may occur when 2 threads are trying to modify the same object simultaneously. F# eliminates this problem completely by disallowing objects from being modified after creation, whenever a change is needed a new object is constructed by applying a transformation on the original object. This means that the code written in F# can naturally be run in parallel with little additional efforts.
Better Modularity
A basic unit of logic in F# is a function, whereas in C# it's a class. Functions in F# are first-class citizens which can be passed as parameters to other functions and be returned as a result of a function. Writing functions takes less effort than writing classes. This enables a finer level of modularity and more sophisticated composition scenarios at lower cost. The code written this way is more maintainable and easy to refactor.
Focusing on Solving Generic Problems Instead of Specific Ones
F# encourages you to use generic data-types instead of concrete ones. The same algorithm written for generics will work on different concrete types like numbers or strings or complex objects. Having just one generic implementation for different types makes the code more reusable and leaves less room for a mistake.
Type Providers
F# has a unique mechanism for working with heterogeneous sources of data in a convenient unified way called type providers. Type providers abstract the implementation details of various sources of data, ensure the type safety and expose standard well-defined interfaces. They also feature on-demand type discovery when new types are loaded only when needed. There is a repository of type providers to various sources of data, which is constantly updated and contributed to by the community. Type providers enable information-rich programming by making it easier to consume the data from different sources.
Standard Tools for Generating Parsers
F# has 2 standard tools FsLex and FsYacc for generating parsers based on a formal grammar. These tools are named after Lex and Yacc the popular parser generator utilities in the Unix world. FsLex and FsYacc have rich diagnostic capabilities and can be incorporated in the build process helping to streamline the development.
Disadvantages of F#
The following disadvantages only matter if F# is used in an idiomatic way following the principles of the functional programming. If you choose not to follow these principles the disadvantages will no longer take place.
Steep Learning Curve
For a person who hasn’t had any experience in functional programming it can be challenging to adjust to a new way of thinking while coding in F#.
More Complex Data Structures
With the transformation-over-mutation approach it takes more advanced data-structures to do efficient manipulations on them. For example one has to use a binary-tree in F# instead of a hash-table as they would do in C#. Another example would be the extensive use of zippers instead of iterators.
Heavier Load on the Garbage Collector
Following transformation-over-mutation principle closely results in creating more objects that need to be timely disposed. It puts a bigger load on the garbage collector compared to manipulating fewer number object whose state is being changed. A developer has to come up with a balanced approach and use this principle wisely.
Naming is More Challenging
F# doesn't have a function overloading feature that C# has for methods. So two functions in F# that sit in the same module cannot share the same name, which creates a difficulty in naming them uniquely. Coming up with a consistent naming convention is a challenging thing in F#.
Less Advanced Tools
Microsoft invests a lot in making the best tools for C# programmers. Unfortunately there are not that many tools for F# which makes coding less comfortable. F# doesn’t have basic refactoring tools.
Limited Use for Certain Types of Projects
UI applications can be a good illustration of the situation where using F# would be rather awkward because manipulations on the controls imply changing their state and this is something F# wasn’t designed for.
Summary
F# is a modern functional language that was designed as an alternative to C# and VB, which honor the object oriented paradigm. F# has borrowed the best features from them and left the dangerous ones behind. That is why the code written in F# is safer and easier to maintain. Although there are certain types of projects where F# would a poor choice, it should definitely be considered for projects heavy on calculations and manipulations with data. The ability of F# to describe business problems more accurately than C# makes it an ideal candidate for server side applications.
Resources
- F# project at Microsoft Research: http://research.microsoft.com/en-us/projects/fsharp/
- Introduction to F#: http://fsharpforfunandprofit.com/
- Online playground: http://www.tryfsharp.org/Learn
- F# power pack: https://fsharppowerpack.codeplex.com/