Key Takeaways
- C# and F# are languages, each with growing user bases, that approach functional programming in fundamentally different ways. C# relies on object-oriented, imperative principles, and F# relies on functional principles.
- C# is a multi-paradigm, object-oriented language first released by Microsoft twenty years ago. It was the eighth most popular language among developers in 2022, with nearly a third of developers using it.
- Although C# is primarily object-oriented and mutable/stateful, it does have functional capabilities.
- Since its inception, the F# community has grown steadily, if slowly. It now includes a GitHub group as well as many different community projects, from Javascript transpilers (Fable) to package managers (Paket) to web-development libraries (Suave) and more.
- Some developers are using F# as a complement to C#, rather than relying on the functional capabilities that exist natively in C#.
Efficiency is everything in the world of application development. Developers and organizations that can get products to market faster and provide more rapid refreshes will always have an edge over the competition.
So everyone's always looking for ways to reduce development time, time spent on debugging and testing, and time pushing finished releases out to customers.
Functional programming was in common use decades ago but lost ground quickly to object-oriented programming languages, which soon became the de facto standard.
But in recent years, there has been a resurgence of interest in functional programming as a way to improve development efficiency and build more stable and robust applications.
C# and F# are languages with growing user bases that approach functional programming in fundamentally different ways: C# as an adjunct to its inherent imperative paradigm and F# as its primary paradigm.
This article investigates which of these languages does the best job for developers looking to put functional programming in place for their teams and projects.
The debate on functional vs object-oriented programming
To understand the debate, we have to start at a little higher level with the difference between imperative and declarative programming paradigms.
- Imperative programming: A process-based programming approach, where developers specify how to achieve results step-by-step. Imperative programming focuses on program state and state changes. Object-oriented programming languages like Java and C++ generally follow the imperative paradigm.
- Declarative programming: A results-oriented programming approach, where developers specify the types of results desired. Declarative programming is stateless and execution order agnostic. Functional programming languages like LISP, Python, Haskell, and F# follow the declarative programming paradigm.
But it is an oversimplification to try and neatly divide all existing programming languages into these categories, as several languages offer aspects of each. They are hybrid or multi-paradigm languages.
For example, even though Java and C++ traditionally fall within the imperative classification, they also have functional aspects. The same is true of C#. Similarly, although F# is considered a functional language, it also has imperative capabilities.
Look at the list of the top ten languages developers use. You will see that they encompass mostly hybrid languages, with some focusing on imperative and others more dominantly functional.
(from 2022 StackOverflow Developer Survey)
JavaScript, which has held the top spot in this survey for a decade, is a multi-paradigm language, offering both imperative, object-oriented features and functional features.
Given the range of adoption, it is helpful to consider the different benefits each paradigm presents and the various use cases where each excels.
Imperative programming: benefits and use cases
Among the primary benefits of the imperative paradigm is that code written using it is generally easily understood and can be easy to read. Furthermore, given the meticulous workflow descriptions that imperative programming requires, even novice developers find it easy to follow.
But the level of detail imperative programming requires also comes with downsides. For example, in more complex applications, code can quickly become bloated. As the code size increases, ease of reading and comprehension just as quickly fall off.
Moreover, as the code expands, the potential for bugs and errors increases. Thus, developers working with imperative languages often find themselves spending a lot of time debugging and testing, delaying product releases.
Nonetheless, imperative programming remains incredibly popular and has a wide range of use cases. Traditional applications for imperative programming include:
- Hypertext and hypermedia
- Object Database Management Systems (ODBMS)
- Client-server systems
- Real-time systems
- Artificial intelligence, machine learning, and neural networks
- Automation
Functional programming: benefits and use cases
The benefits of functional programming land more on the efficiency side of the equation. Functional code, while less easy to read and comprehend at first glance, tends to be closer to bug-free (i.e., no side effects for state changes), reducing developer time spent on debugging and testing.
Fewer bugs also lends itself to more secure applications, limiting the attack surface for cybercriminals to reduce the odds of ransomware attacks, malware, or SQL injections.
Functional programming is also better at both parallel processing and lazy evaluation. In addition, functional code is more modular and reusable, reducing the need for redundant code. The smaller code set is easier to maintain and can be higher performing. However, functional code can be memory intensive, eliminating any speed benefits from reduced code size and actually leading to decreased overall performance.
Functional programming is particularly popular among academics and data scientists, because it is effective at dealing with the manipulation of large data sets.
Given its focus on parallel processing and immutability, functional programming is particularly useful for:
- Data Science
- Spreadsheet applications
- Finance and risk applications
- Batch processing
Functional programming: C# vs F#
C# and F# are languages that have been gaining in popularity in recent years. Although they are both multi-paradigms, their primary focus differs, with C# relying on object-oriented, imperative principles and F# relying on functional principles. But does one outperform the other when it comes to functional programming?
What is C#, and who is using it?
C# is a multi-paradigm, object-oriented language first released by Microsoft some 20 years ago. As you can see from the usage statistics above, it was the eighth most popular language among developers in 2022, with nearly a third of developers using it. It also has high satisfaction scores, with two-thirds of C# users saying they love using it.
C# is finding many uses in the development of web and cloud services, as well as game development. Companies from Microsoft to TrustPilot to StackOverflow are creating applications and services with C#.
Functional programming in C#
Although C# is primarily object-oriented and mutable/stateful, it does have functional capabilities. Here are a few ways to implement the functional paradigm in C#.
Create immutability
Because data types in C# are inherently mutable, when you want to use functional programming principles, you need to create immutability. And this is more complicated than simply relying on immutable data types in F#. Specifically, to create immutable types in C#, you must make the type read-only, remove setter properties, use a constructor to provide the parameters, and then create a new instance every time a state change is needed, rather than mutating an existing instance.
Use LINQ and Lambda expressions
Microsoft built the LINQ (Language Integrated Query) framework specifically to introduce functional programming features into C#. LINQ provides functions for operating on lists or sequences, including mapping (Select), sorting (OrderBy), and filtering (Where). Each of these expressions has functions for arguments. The expressions create new instances of the sequence rather than mutating the existing sequence. LINQ is particularly useful for querying datasets, whether SQL tables, XML data, or other sources.
LINQ also allows the use of Lambda expressions, which are essentially anonymous functions. Anonymous functions are a key aspect of functional programming. Lambda expressions can act as arguments for other functions in the code, creating higher-order functions, another common feature of functional programming.
Use method chains
A commonly used feature of F# is the pipeline operator, which passes the result of one function to another function. Pipelining is not built into C, but developers can imitate pipelines in certain situations using method chains, or fluent interfaces. This can be effected using the StringBuilder functionality in C#.
Method chains also allow you to replicate another common feature of functional programming, currying. Currying allows a function with multiple arguments to receive those arguments at different times. Essentially, in functional programming, if a function does not receive all the needed inputs, it returns a new function with the missing inputs as its arguments.
In C#, you implement currying with method chains to decompose a multi-function argument into a nested sequence of several single argument functions. However, this is not as neat or as efficient as in F#.
These are just some of the ways that skilled C# developers can apply functional programming principles in C#. And while they may require more effort than simply using F#, for those developers who want all the other features C# has to offer, they are a viable alternative.
What is F#, and who is using it?
Initially released by Microsoft for the Windows platform in 2005, F# is a predominantly functional programming language. It expanded to encompass Linux and macOS platforms in 2010, and then JavaScript in 2013. Also, in 2013, the F# Software Foundation launched to support Microsoft in its development and both F# and the F# community.
Since its inception, the F# community has grown steadily, if slowly. It now includes a GitHub group as well as many different community projects, from Javascript transpilers (Fable) to package managers (Paket) to web-development libraries (Suave) and more.
Despite its age, F# still has substantial room for adoption. General usage statistics are a little hard to come by, but a 2021 survey by JetBrains of more than 30000 developers indicated that only 1% had recently used F# or were planning to do so in the near future. By comparison, 21% had recently used C#, and 4% planned to use it soon. And F# has a slightly lower satisfaction rating than C#, although more than 60% of developers say they love using it.
Currently, there are fewer than 100 companies that are known to use F# in production, although they include some well-known names like Walmart and Huddle.
Some developers are using F# as a complement to C#, rather than relying on the functional capabilities that exist natively in C#. Because both compile to .NET Intermediate Languages (IL), it is possible to use both in the same project.
Which language has the edge for functional programming?
It seems like this should be a very simple question to answer: F# is functional first, while C# is imperative first, so the edge goes to F#. And at the highest level, this statement is true. But application development, like life, is rarely simple enough for the generalized application of high-level truths.
The better question to ask is which language is right for you, your team, and the projects you are working on at the moment. And this is a much more complex question to answer.
Choosing the right language for functional programming
When selecting between C# and F# for your functional programming needs, there are several considerations that should factor into your decision:
- What are the needs of the project? The specific feature sets of your application may take you a long way in the decision process. For example, if the most important features of your application revolve around the user interface elements, with only minor data manipulation, then you may want to focus on C#. But if your application is data intensive and the UI elements are less important, you will probably lean towards F#.
- What is your comfort level with each language? If your application will benefit from functional programming, but you aren’t experienced enough with C# to easily deal with the manipulations needed to make immutable types and translate from declarations to expressions, then you should consider F#.
- How will your team deal with different languages? When working with a development team, you need to consider the skill sets and experience of each member of the team. You may have one very skilled F# developer, but others with little to no exposure to F#. This may lead to difficulties for team members in working with code written by other developers.
- Is a multi-language approach best? Is your development team sufficiently skilled to make both C# and F# work together on the same project? Are there reasons that either of these languages alone doesn’t sufficiently address all the needs of your application? But think carefully before taking this approach - you should only do so if it is more efficient and more effective than using either language separately.
Conclusion
When a development project demands functional programming, there are many options available to the development team, from purely functional languages to multi-paradigm languages like C# and F#. Making the best choice among good competing options is not always simple, but the time invested in making that decision will be rewarded with improved development efficiencies and better-end products.