5 1/2 F# features every C# programmer should lust after

C# is a great programming language, but there’s still a bevy of features in other programming languages that I often wish were in C#. I’m doing quite a bit of work in F# nowadays, and I thought it would be fun to jot down a few F# features that I really miss when I come back to C#.

1. The Option type

Tony Hoare, the inventor of the null reference, called it his ‘billion dollar mistake.’ Null is a disaster because it means that every reference type has a magic value that will destroy your program. For value types, we now have ‘normal’ value types and nullable value types to express the difference between, say, ‘this function returns an integer’ and ‘this function might return an integer, or it might not.’ But we don’t have that for reference types.

F# provides the Option type to distinguish between ‘there definitely is a value’ and ‘there might or might not be a value.’ F#’s Option is a bit like Nullable but can be used with any type, value or reference, and comes equipped with helper functions and pattern matching support. Here’s an example of Option in use:

let names = [ "alice"; "bob"; "carol"]
let name = List.tryFind (fun s -> length s < 4) names  // returns an Option
match name with
| Some n -> printfn "%s has a short name" n
| None   -> printfn "They all have long names"

Notice that if we erroneously try to use the option returned from List.tryFind as if it were a real string, the error is caught at compile time:

let names = [ "alice"; "bob"; "carol"]
let name = List.tryFind (fun s -> length s < 4) names  // returns an Option
printfn "%s has a short name" name  // Error! Expected string but got string option

In C#, the ‘not found’ case would be represented by null, and the compiler would not be able to catch the potential error.

The FSharpx project provides some helpers to make it easy to use the F# Option type from C#, though it’s of limited use except in interop because the C# compiler still won’t stop you using nulls even in non-optional situations.

2. Immutable record types

Immutable types are a great way to improve the correctness of programs. Any type that represents a value, rather than an entity which needs to preserve identity as its attributes change, should be immutable. Even quite complex objects, such as expression trees in LINQ or syntax trees in Roslyn, benefit from immutability. Unfortunately, C# makes it far more effort to write immutable types than mutable ones. Here’s two ways to implement a 2D Point type in C#, one mutable (wrong!) and one immutable (right!):

public class MutablePoint {
  public double X { get; set; }
  public double Y { get; set; }
public class ImmutablePoint {
  private readonly double _x;
  private readonly double _x;
  public ImmutablePoint(double x, double y) {
    _x = x;
    _y = y;
  public double X { get { return _x; } }
  public double Y { get { return _y; } }

The safe, immutable type is more than twice as long!

Contrast this with F#:

type Point = { X : float; Y : float }

It’s short, it’s immutable and as a special wonder bonus it’s got proper value equality built in for free. And I want it in C#, pronto.

3. Object expressions

Object expressions are a great feature for when you want to implement an interface or override a class member without going to the trouble of spinning out an entire class or subclass for it. This can dramatically reduce the amount of code particularly when you need to capture local variables for your implementation or override. (If you’re familiar with Java’s local classes, object expressions are a lot like that.) Here’s an example using the LightSpeed IAuditInfoStrategy interface:

// blame takes a string and returns an IAuditInfoStrategy
let blame x = {
  new IAuditInfoStrategy with
    member this.Mode = AuditInfoMode.Custom
    member this.GetCurrentUser() = x }

In C#, I would have had to write out a ScapegoatingAuditInfoStrategy class with a field to hold the blamee and a constructor to pass the blamee into the class. In F#, I just told the compiler to make me an IAuditInfoStrategy that always blamed x, and it did. I often end up writing lots of little classes to capture small nuggets of code or fragments of state, and it would be great if C# had something like object expressions to make it easier.

More about object expressions here.

4. Partial application

I actually like C# lambda syntax more than F#. This is just as well, because I have to write a lot more lambdas in C# than I do in F#. The reason is that in F# I can use a trick called partial application to reuse a ‘normal’ function just with particular arguments. Let’s see an example.

Suppose I want to double every element in a sequence. I can do that using the LINQ Select operator, or its F# equivalent Seq.map:

// C#
var doubled = values.Select(v => v * 2);
// F#
let doubled = Seq.map (fun v -> v * 2) values

C# code is more concise than F# code, right? Not so fast! In F# we can partially apply the * function and save ourselves writing the lambda!

let doubled = Seq.map ((*) 2) values

Of course you can do this with your own functions too:

let isDivisibleBy x y = y % x = 0
let evens = List.filter (isDivisibleBy 2) values  // rather than (fun v -> isDivisibleBy 2 v)

Partial application reduces the amount of lambda noise in the code, and can also be useful in creating new functions by specialising existing ones. You can read more about partial application in this series and (more concisely) in this article.

5. Pattern matching

I’ve written extensively about pattern matching elsewhere, and it’s too big a topic to explain thoroughly here. Pattern matching allows you to combine programming by cases (like a switch statement) with custom logic (like an if statement) and decomposing composites to get the bits you’re interested in (like a series of property accesses or collection operations). It’s also extensible using active patterns, which means you can build classifiers independently of the routines that use the classification. And patterns compose far more conveniently than imperative code.

One often-overlooked but very powerful feature of pattern matching is that it can be used in a let statement. This allows you to effectively return multiple values from a function without having to create a XxxResult type or explicitly picking apart a tuple. Combined with the F# compiler being able to silently translate C# out-parameters into tupled return values, this creates some neat, readable idioms:

let dict = new Dictionary<string, string>()
let (found, result) = dict.TryGetValue("alice")
// C# equivalent
var dict = new Dictionary<string, string>();
string result = null;
bool found = dict.TryGetResult("alice", out result);

There’s much more to pattern matching than this, including recursing over lists, visiting over class hierarchies (discriminated unions), working safely with options, type-checking and casting, and, well, the list goes on. I use pattern matching all the time in F# and I always miss it when I have to come back to C# and write imperative (if-style) code to distinguish between cases.

5 1/2. Async

F#’s async workflows make it easy to write asynchronous code in a readable, easy to understand, top-to-bottom way. This is compelling — so compelling, in fact, that a similar feature is going to be adopted into C#. F# still has a bunch of async features that haven’t made it into C#, notably async agents, but for the core scenario C# programmers no longer need to be envious!

And there’s more

There’s so much in F# that would be a huge boon for C# that I could easily have made this a top 10 list. Some of F#’s features can be used from C# (read Mauricio Scheffer’s great post on ’10 reasons to use the F# runtime in your C# app’) but a lot of the features I miss are part of the language. For example, I’d love to see F#-level type inference in C#. After all, which one of these declarations do you find easier to read?

// C#
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) { ... }
// F#
let map selector source = ...  // F# infers types and generic type parameters from implementation

Or how about recursive iterators? Or an interactive prompt in Visual Studio, so you could try out snippets or run demos without having to build a console or NUnit project? (Roslyn has a C# Interactive window, but Roslyn’s still some way off!) And looking to the future, F# type providers open up a whole new order of expressiveness beyond standard code generation techniques.

So what’s in your top 5? What F# features do you miss when you go back to C#?

Tagged as F#

20 Responses to “5 1/2 F# features every C# programmer should lust after”

  • Great article! Though I kinda disagree about Option not being useful in C#: it’s great to wrap reference types. FSharpx includes a ToFSharpOption() extension method that maps nulls to None and otherwise Some, so you’re sure not to have any nulls. Once you have an option, you get all the composability benefits: mapping (Select), monadic bind (SelectMany), LINQ integration and even pattern matching. It also integrates nicely with collections (FSharpx exposes List and Dictionary tryFind as extension methods).

    So yes, it will never be as convenient as in F#, due to lack of type inference, real pattern matching, record types, etc, but it’s very useful and usable.

    A great library that implements options and related functional concepts for C# from scratch (without depending on the F# runtime) is Sasa: https://sourceforge.net/projects/sasa/


  • Great point about integration with the FSharpx collection helpers, and I think it helps to illustrate why you and I may differ on the usefulness of Option in a C# codebase. Using Option at the boundaries of a tight, well defined unit such as a collection type, where you can prevent nulls entering or escaping, would I agree work really well. But many C# codebases make heavy use of .NET or third party APIs, and analysing and encapsulating such APIs to ensure that nulls never enter or exit is probably more effort than just checking for null. Thinking about it though there certainly are places even within the monstrous pile of legacy that is the Visual Studio SDK that I could productively put Option-based wrappers around some particularly risk-prone areas.

    I certainly don’t mean to be dismissive of using FSharpOption in C# or of the extensions that FSharpx provides; as with everything it is a matter of finding where it is most valuable!

  • […] 5 1/2 F# features every C# programmer should lust after – Ivan Towlson shares a look at some of the tasty features in the F# language that developers who program in C# should be lusting after, showing off some great F# features which don’t exist on the C# side of the .NET fence. […]

  • Cool learned something newhere now I’m good for this week. Thanks!

  • I’ve never got around to learning C#, but I lust after the “units of measure” support.

  • […] 5 1/2 F# features every C# programmer should lust after (Ivan Towlson) […]

  • F# code doesn’t live in a vacuum either, you have to consume object-oriented, nullable .NET code too, so it’s in the same situation as C# about this. This doesn’t make options any less useful. As with most things, options are not an all-or-nothing choice and can be gradually adopted. I’ve been doing this for the past months in a pretty big C# codebase, and one of the nice consequences is that it encourages the typical functional way of doing things by composing small pure functions.

  • Good article and I agree somewhat (and I admit to not being anything near to a F# expert), but I would say that Options more or less force you to address the problem of what happens when a method can not return an answer where as C# just lets you “hope for the best”. This is similar to java forcing you to catch all possible exceptions and C# allowing you to hope for the best. Is one better than the other? The point is only important if you know what might be done when things go wrong. An option will not fix a null reference exception, it only forces you to deal with it (and of course you have to code with the intent of things might go wrong).

    The example of immutable types is (IMHO) poorly chosen as you could equally create a struct in C# to have an immutable value type, I am not sure the F# example compiles to a reference type (I am no F# expert and this might be nit picking… I am the first to admit C# is not made to create immutable types).

    Finally I would like to comment on type inference in F#… definitely good, but the cost is (I think) you have to order your source code files so that the inference engine can always create the compiled sequence of valid types. My C# projects have hundreds and even 1000’s of source files and classes. The idea that I would have to sort them into some kind of build order scares me…. I find that this restriction in F# is not discussed often… is it still a requirement?

  • public class ImmutablePoint {
    public double X { get; private set; }
    public double Y { get; private set; }
    public ImmutablePoint(double x, double y) {
    X = x;
    Y = y;

    A shorter version of ImmutablePoint. Granted, without the readonly keyword the X,Y values are technically mutable, but there is no code path to modify them, so achieves the same thing, and all you need to add is a constructor (and private keywords on the setters). You could also make it a struct instead of a class, although that doesn’t make it any smaller (little larger, since struct is one more character, and you need a this() to call the default constructor.

  • 1. The Option type in F# just means you have to check for both None and Some(null). While I agree that we need to fix the null problem, this just makes it worse.

    2. Agreed.

    3. I am having trouble thinking of an interface that I would want to implement that way.

    4. You forgot about the LINQ syntax, “from v in values select v*2”. This left-to-right syntax flows a heck of a lot better than F#’s right-left-center style.

    5. C# needs to fix their switch blocks before they even consider adding real pattern matching.

  • > Good article and I agree somewhat (and I admit to not being anything near to a F# expert), but I would say that Options more or less force you to address the problem of what happens when a method can not return an answer where as C# just lets you “hope for the best”.

    F# should have forced you to check for None, but it doesn’t.

  • @dennis : what you seem to be missing about Option is its composability. Being able to map options lets you code essentially as if it was ‘successful’ and bubble up the None case as much as you want. Much like an exception, except it’s actually composable, pure and part of the function signature.

    Options *do* save you from most nulls. There was a discussion about this in the Scala community some time ago, I recommend googling for “scala null option save”.

    About the order of source files, see the comments in http://www.ikriv.com/blog/?p=28

  • Dennis: That’s an interesting point comparing options to checked exceptions. A common idiom in the F# library is to offer “X” and “tryX” functions, the first of which returns a normal value and throws if it fails, the second of which returns an option. This allows you to clearly document your expectations as to whether you are hoping for the best or failure is a real possibility. As Mauricio notes F# also has idioms to make working with options very fluent and composable.

    You are right that I could (and in production code would) have made Point a struct but this doesn’t actually make any difference to its immutability. Even structs are mutable in C# by default (which is really confusing and unfortunate: mutable struct should be an error!), and C# offers no syntax help for making them immutable.

  • Jason: As you rightly point out, that version of Point is immutable from the outside, but mutable from the inside. That’s safe for consumers, but it’s not self-documenting for maintenance programmers, who may not be aware that there is consumer code that depends on the immutability. (For example, a programmer tasked with implementing a MoveBy method might choose to mutate the X and Y values rather than seeing the readonly keyword and realising they need to return a new Point.)

  • Jonathan: Things like IComparer and IEqualityComparer come up a lot. I don’t need multiple instances of them, I don’t need a whole class, I just want a couple of short methods bundled up into the interface.

    Agreed that if a Some can contain a null then using the Option becomes pointless. But this will never happen for F# types, and when interoperating with ‘null-style’ code you would optionise it in a way that nulls translate to None so that Somes are always non-null (see Mauricio’s description of how FSharpx does it).

    I generally use extension methods rather than LINQ syntax because it’s more composable, and F# has an equivalent to this in left-to-right style using pipelines: values |> List.map ((*) 2). Both languages do a great job here, though; I just wish C# had that extra feature of partial application so I could get rid of the lambda noise even when using the extension methods.

  • James: How could I have forgotten about units of measure? Completely agree!

  • Strongly agreed with most of the points.

    I think most people agree that the evolution of C# has severely slowed down after 3.0. What I’m hoping is that it’s because Roslyn took away a lot of the resources of the C# team (the development of Roslyn began in 2007, and well, it seems to be turning out quiet damn awesome).
    Hopefully by/after C# 6.0, with Roslyn ready, the evolution will come back with an even faster pace it went before 4.0.
    Not saying it has anything to be ashamed of compared to other mainstream languages, but most of the features that make C# stand out from its competitors still come from 3.0, few features worth to mention came afterwards.

  • […] 5 1/2 F# features every C# programmer should lust after […]

  • “In C#, the ‘not found’ case would be represented by null, and the compiler would not be able to catch the potential error.”

    But ReSharper do

  • […] Ivan Towlson wrote an article highlighting sweet features that F# has and C# doesn’t, and one feat…. […]

  • Leave a Reply


Join our mailer

You should join our newsletter! Sent monthly:

Back to Top