F# decompiled into C#
The complete code for this post is available on GitHub
A favorite trick of F# proponents is to take some F# code, compile it, and then decompile the result to C# using a tool such as ILSpy. This shows you the C# code you would have to write to get the same functionality as the F# code.
Generally, the F# code is much shorter than the equivalent C# code – due to things like one-line type definitions, lack of curly braces, and so on.
Just typed a 15 lines #FSharp program of @dsyme's book, compiled it, decompiled it using @ilpsy, got 720 lines of #CSharp code. Woot!
— Axel Heer (@axelheer) April 6, 2013
Looking at #fsharp code decompiled to #csharp really shows how powerful the language, and not to mention the CLR, really is.
— Christian Palmstie~1 (@cpx86) July 10, 2015
However, I couldn’t find many concrete examples of this kind of decompilation on the web, so I thought I would put together some demonstrations for reference.
This post will not cover all possible F# code. For example, I won’t show any examples of lambdas or partial application, which end up looking messy when decompiled. Rather, I’ll just focus on two core things: basic type definitions and stand-alone functions in modules.
So I have grouped the examples as follows:
- Record Types, with examples of both immutable and mutable record types, and a type with extra properties and methods.
- Classes, with examples of a basic class, then one with custom equality, then an interface, an abstract base class, and some concrete subclasses.
- Discriminated Union Types, with examples of a single-case union used as wrapper, an “enum” set of choices, and a more complex example with extra data for each choice.
- Modules, with examples of simple functions, and also a submodule.
- Pattern Matching, which shows the code that is generated for various kinds of pattern matching.
I did not write the C# code from scratch. Instead, I used the following process:
- I created some F# types, such as those in this file.
- I compiled this to a DLL, opened the DLL in ILSpy, and decompiled the code to C#.
- For each F# type, I created a corresponding C# class.
- I then tweaked the C# code to make it more idiomatic, using auto-properties, removing duplicate code, renaming variables, etc. I also added a few explanatory comments where needed.
- I next used Resharper and tweaked the C# code some more until R# was happy.
- The final C# code looks something like this.
NOTE: The goal was not to create perfect C# code, but to preserve the F# compiler output as much as possible, without giving C# devs too many conniptions.
In F#, most types that contain fields or properties are defined as record types rather than classes, so let’s start with these.
Here is an example of a simple immutable record in F#, with comments attached to the type and each property.
/// Example of a simple immutable record
type FinalGameScore = {
/// Game property
Game: string
/// FinalScore property
FinalScore : int
}
In C# the equivalent of the generated code would look like this (I’ll discuss the code in detail below).
/// <summary>
/// Example of a simple immutable record
/// </summary>
[Serializable]
public sealed class FinalGameScore :
IEquatable<FinalGameScore>,
IStructuralEquatable,
IComparable<FinalGameScore>,
IComparable,
IStructuralComparable
{
/// <summary>
/// Game property
/// </summary>
public string Game { get; internal set; }
/// <summary>
/// FinalScore property
/// </summary>
public int FinalScore { get; internal set; }
/// <summary>
/// Constructor
/// </summary>
public FinalGameScore(string game, int finalScore)
{
this.Game = game;
this.FinalScore = finalScore;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public int GetHashCode(IEqualityComparer comp)
{
var num = 0;
const int offset = -1640531527;
num = offset + (this.FinalScore + ((num << 6) + (num >> 2)));
var game = this.Game;
return offset + (((game == null) ? 0 : game.GetHashCode()) + ((num << 6) + (num >> 2)));
}
/// <summary>
/// Needed for custom equality
/// </summary>
public override int GetHashCode()
{
return this.GetHashCode(LanguagePrimitives.GenericEqualityComparer);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(FinalGameScore obj)
{
return obj != null
&& string.Equals(this.Game, obj.Game)
&& this.FinalScore == obj.FinalScore;
}
/// <summary>
/// Implement custom equality
/// </summary>
public override bool Equals(object obj)
{
var finalGameScore = obj as FinalGameScore;
return finalGameScore != null && this.Equals(finalGameScore);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(object obj, IEqualityComparer comp)
{
// ignore the IEqualityComparer as a simplification -- the generated F# code is more complex
return Equals(obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(FinalGameScore obj)
{
if (obj == null)
{
return 1;
}
int num = string.CompareOrdinal(this.Game, obj.Game);
if (num != 0)
{
return num;
}
return this.FinalScore.CompareTo(obj.FinalScore);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj)
{
return this.CompareTo((FinalGameScore)obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj, IComparer comp)
{
// ignore the IComparer as a simplification -- the generated F# code is more complex
return this.CompareTo((FinalGameScore)obj);
}
}
Let’s go through the generated C# code in sections.
The first thing to notice is that the generated code implements a number of interfaces:
public sealed class FinalGameScore :
IEquatable<FinalGameScore>,
IStructuralEquatable,
IComparable<FinalGameScore>,
IComparable,
IStructuralComparable
In particular it overrides equality (both the generic one from Object
and IEquatable<T>
) and comparison (both IComparable
and IComparable<T>
) and also
IStructuralEquatable
and IStructuralComparable
(for details on what these are for, see
this SO question).
The first part of the class body is similar to what we would write ourselves. Two get-only properties and a constructor.
/// <summary>
/// Game property
/// </summary>
public string Game { get; internal set; }
/// <summary>
/// FinalScore property
/// </summary>
public int FinalScore { get; internal set; }
/// <summary>
/// Constructor
/// </summary>
public FinalGameScore(string game, int finalScore)
{
this.Game = game;
this.FinalScore = finalScore;
}
Note that the F# code generates internal set
properties rather than private
ones. Since all the code in the assembly is F#,
and there is no special behavior in the getter or setter, this makes no practical difference.
The next section implements GetHashCode
and three kinds of Equals
.
/// <summary>
/// Needed for custom equality
/// </summary>
public int GetHashCode(IEqualityComparer comp)
{
var num = 0;
const int offset = -1640531527;
num = offset + (this.FinalScore + ((num << 6) + (num >> 2)));
var game = this.Game;
return offset + (((game == null) ? 0 : game.GetHashCode()) + ((num << 6) + (num >> 2)));
}
/// <summary>
/// Needed for custom equality
/// </summary>
public override int GetHashCode()
{
return this.GetHashCode(LanguagePrimitives.GenericEqualityComparer);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(FinalGameScore obj)
{
return obj != null
&& string.Equals(this.Game, obj.Game)
&& this.FinalScore == obj.FinalScore;
}
/// <summary>
/// Implement custom equality
/// </summary>
public override bool Equals(object obj)
{
var finalGameScore = obj as FinalGameScore;
return finalGameScore != null && this.Equals(finalGameScore);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(object obj, IEqualityComparer comp)
{
// ignore the IEqualityComparer as a simplification -- the generated F# code is more complex
return Equals(obj);
}
The code that is generated for the third Equals
method (using IEqualityComparer
) is almost a duplicate of the primary Equals
method, so to make the comparison fairer,
and to be more like the code that someone would write by hand, I have removed it and replaced with a call to the primary Equals
method.
Finally, the various kinds of comparisons are implemented. Again I have removed the duplicated logic and tidied up the code to be a bit more idiomatic.
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(FinalGameScore obj)
{
if (obj == null)
{
return 1;
}
int num = string.CompareOrdinal(this.Game, obj.Game);
if (num != 0)
{
return num;
}
return this.FinalScore.CompareTo(obj.FinalScore);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj)
{
return this.CompareTo((FinalGameScore)obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj, IComparer comp)
{
// ignore the IComparer as a simplification -- the generated F# code is more complex
return this.CompareTo((FinalGameScore)obj);
}
You might not always need special equality and comparison code to be generated, in which case you can mark a type with
NoEqualityAttribute
and/or NoComparisonAttribute
.
We’ll see NoComparisonAttribute
used in the next example.
Let’s have a look at how you would implement a record with one mutable property, such as a mutable CurrentScore
, and without generating comparison code:
/// Example of a simple mutable record
[<NoComparisonAttribute>]
type UpdatableGameScore = {
/// Game property
Game: string
/// Mutable CurrentScore property
mutable CurrentScore : int
}
In the generated C# code, the CurrentScore
property now has a setter as well as a getter, and the IComparable
interfaces and the CompareTo
implementations have been eliminated.
Here is the relevant excerpt:
[Serializable]
public sealed class UpdatableGameScore :
IEquatable<UpdatableGameScore>,
IStructuralEquatable
{
/// <summary>
/// Game property
/// </summary>
public string Game { get; internal set; }
/// <summary>
/// Mutable CurrentScore property
/// </summary>
public int CurrentScore { get; set; }
/// <summary>
/// Constructor
/// </summary>
public UpdatableGameScore(string game, int currentScore)
{
this.Game = game;
this.CurrentScore = currentScore;
}
// remaining code snipped
Source for UpdatableGameScore.cs
Finally, we often want to add extra properties or methods to a type.
In the example below, I’ve defined a Person
type that has a FullName
property and a IsBirthday
method
in addition to the core properties of FirstName
, LastName
and DateOfBirth
.
/// Definition of a Person
type Person = {
/// Stores first name
FirstName: string
/// Stores last name
LastName: string
/// Stores date of birth
DateOfBirth: DateTime
}
with
/// FullName property
member this.FullName =
this.FirstName + " " + this.LastName
/// IsBirthday method
member this.IsBirthday() =
DateTime.Today.Month = this.DateOfBirth.Month
&& DateTime.Today.Day = this.DateOfBirth.Day
The first part of the generated C# code looks like this:
/// <summary>
/// Definition of a Person
/// </summary>
[Serializable]
public sealed class Person :
IEquatable<Person>,
IStructuralEquatable,
IComparable<Person>,
IComparable,
IStructuralComparable
{
/// <summary>
/// Stores first name
/// </summary>
public string FirstName { get; internal set; }
/// <summary>
/// Stores last name
/// </summary>
public string LastName { get; internal set; }
/// <summary>
/// Stores date of birth
/// </summary>
public DateTime DateOfBirth { get; internal set; }
/// <summary>
/// Constructor
/// </summary>
public Person(string firstName, string lastName, DateTime dateOfBirth)
{
this.FirstName = firstName;
this.LastName = lastName;
this.DateOfBirth = dateOfBirth;
}
/// <summary>
/// FullName property
/// </summary>
public string FullName
{
get
{
return this.FirstName + " " + this.LastName;
}
}
/// <summary>
/// IsBirthday method
/// </summary>
public bool IsBirthday()
{
return DateTime.Today.Month == this.DateOfBirth.Month && DateTime.Today.Day == this.DateOfBirth.Day;
}
// remaining code snipped
and of course there is the usual extra code for equality and comparison. Here is just one piece of that:
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(Person obj)
{
return obj != null
&& string.Equals(this.FirstName, obj.FirstName)
&& string.Equals(this.LastName, obj.LastName)
&& LanguagePrimitives.HashCompare.GenericEqualityERIntrinsic(this.DateOfBirth, obj.DateOfBirth);
}
The LanguagePrimitives
namespace comes from the F# core library, and contains helper code for hashing, equality testing, comparison, etc.
Sometimes you need inheritance or other OO features. So let’s see what F# classes look like in C#.
Here’s a simple class Product
with one immutable property (Id
), two mutable properties, a secondary constructor, two methods, and a static property containing a constant.
/// Example of a simple class
type Product(id, name, price) =
/// immutable Id property
member this.Id = id
/// mutable Name property
member val Name = name with get,set
/// mutable Price property
member val Price = price with get,set
/// secondary constructor
new(id,name) = Product(id,name,Product.DefaultPrice)
/// True if price > 10.00
member this.IsExpensive = this.Price > 10.00
/// Example of method
member this.CanBeSoldTo(countryCode) =
match countryCode with
| "US"
| "CA"
| "UK" -> true
| "RU" -> false
| _ -> false //all others
/// Example of static property
static member DefaultPrice = 9.99
Here’s what that looks like in the generated C#:
/// <summary>
/// Example of a simple class
/// </summary>
[Serializable]
public class Product
{
/// <summary>
/// immutable Id property
/// </summary>
public object Id { get; internal set; }
/// <summary>
/// mutable Name property
/// </summary>
public object Name { get; set; }
/// <summary>
/// mutable Price property
/// </summary>
public double Price { get; set; }
/// <summary>
/// True if price > 10.00
/// </summary>
public bool IsExpensive
{
get
{
return this.Price > 10.0;
}
}
/// <summary>
/// Example of static property
/// </summary>
public static double DefaultPrice
{
get
{
return 9.99;
}
}
/// <summary>
/// primary constructor
/// </summary>
public Product(object id, object name, double price)
{
this.Id = id;
this.Price = price;
this.Name = name;
}
/// <summary>
/// secondary constructor
/// </summary>
public Product(object id, object name)
: this(id, name, DefaultPrice)
{
}
/// <summary>
/// Example of method
/// </summary>
public bool CanBeSoldTo(string countryCode)
{
if (!string.Equals(countryCode, "US"))
{
if (!string.Equals(countryCode, "CA"))
{
if (!string.Equals(countryCode, "UK"))
{
return string.Equals(countryCode, "RU") && false;
}
}
}
return true;
}
}
As you can see, the generated code no longer implements IEquatable
and IComparable
, which makes it much shorter.
One thing to note is that the pattern matching in the F# version of CanBeSoldTo
method has been unrolled into a series of if
statements in C# (since C# doesn’t have pattern matching).
Other than that, the generated code looks mostly as you would expect.
Now what happens if we want a class with a custom implementation of equality, let’s say an Entity
class that compares using an Id
?
/// Example of custom equality
type Entity(id:int, name:string) =
/// immutable Id property
member this.Id = id
/// mutable Name property
member val Name = name with get,set
/// Implement custom equality
override this.Equals(obj) =
match obj with
| :? Entity as ent ->
this.Id = ent.Id // no null checking needed
| _ -> false // all other cases
/// Needed for custom equality
override this.GetHashCode() =
hash this.Id
/// Implement custom equality
interface IEquatable<Entity> with
member this.Equals(ent) =
this.Id = ent.Id // no null checking needed
Note that I have annotated the class constructor type Entity(id:int, name:string)
to force the id
to be an int
and the name
to be a string
.
Here’s the corresponding generated C#:
/// <summary>
/// Example of custom equality
/// </summary>
[Serializable]
public class Entity : IEquatable<Entity>
{
/// <summary>
/// immutable Id property
/// </summary>
public int Id { get; internal set; }
/// <summary>
/// mutable Name property
/// </summary>
public string Name { get; set; }
public Entity(int id, string name)
{
this.Id = id;
this.Name = name;
}
/// <summary>
/// Implement custom equality
/// </summary>
public override bool Equals(object obj)
{
var entity = obj as Entity;
if (entity == null) return false;
return this.Id == entity.Id;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public override int GetHashCode()
{
return this.Id;
}
/// <summary>
/// Implement custom equality
/// </summary>
bool IEquatable<Entity>.Equals(Entity ent)
{
return this.Id == ent.Id;
}
}
Again, this code looks like standard C# code.
Finally, let’s look at some OO code in F# and C#. We’ll start with an interface:
/// Interface
type IShape =
abstract Name : string
abstract Draw : unit -> unit
And the generated C# is similar:
/// <summary>
/// Interface
/// </summary>
public interface IShape
{
string Name { get; }
void Draw();
}
Next, we’ll define an abstract base class that implements IShape
. The Name
property is concrete, but the subclasses are expected to provide their own Draw
method.
/// Abstract Base Class
[<AbstractClass>]
type ShapeBase(name) as self =
/// concrete implementation of Name property
member this.Name = name
/// abstract definition of Draw method
abstract Draw : unit -> unit
/// Explicit implementation of interface
interface IShape with
member this.Name = self.Name
member this.Draw() = self.Draw()
And the generated C#:
/// <summary>
/// Abstract Base Class
/// </summary>
[Serializable]
public abstract class ShapeBase : IShape
{
/// <summary>
/// Explicit implementation of interface
/// </summary>
public string Name { get; internal set; }
/// <summary>
/// abstract definition of Draw method
/// </summary>
public abstract void Draw();
protected ShapeBase(string name)
{
this.Name = name;
}
And finally, here’s a subclass of ShapeBase
in F#:
/// Concrete class Square
type Square(name,size) =
inherit ShapeBase(name)
/// subclass specific property
member this.Size = size
/// concrete implementation of Draw method
override this.Draw() =
Console.Write("I am a square with size {0}",size)
And in C#:
/// <summary>
/// Concrete class Square
/// </summary>
[Serializable]
public class Square : ShapeBase
{
/// <summary>
/// subclass specific property
/// </summary>
public int Size { get; internal set; }
public Square(string name, int size)
: base(name)
{
this.Size = size;
}
/// <summary>
/// concrete implementation of Draw method
/// </summary>
public override void Draw()
{
Console.Write("I am a square with size {0}", this.Size);
}
}
Source for all these examples:
- Source for F# code
- Source for IShape.cs
- Source for ShapeBase.cs
- Source for Square.cs
- Source for Circle.cs
Now to discriminated unions, a feature that C# does not have. So how are they represented behind the scenes?
We’ll start with a single-case union, typically used to wrap a more primitive type to avoid primitive obsession.
/// example of single-case union as a wrapper round a primitive
type ProductId = ProductId of int
One of the nice things about F# is that this type gets implementations of equality and comparison, just like record types.
This means that the generated C# code is going to be very long again, alas.
/// <summary>
/// example of single-case union as a wrapper round a primitive
/// </summary>
[Serializable]
public class ProductId :
IEquatable<ProductId>,
IStructuralEquatable,
IComparable<ProductId>,
IComparable,
IStructuralComparable
{
/// <summary>
/// Implemented for all F# union types. Not used in this case.
/// </summary>
public int Tag
{
get
{
return 0;
}
}
/// <summary>
/// Property to access wrapped value
/// </summary>
public int Item { get; private set; }
/// <summary>
/// static public constructor
/// </summary>
public static ProductId NewProductId(int item)
{
return new ProductId(item);
}
/// <summary>
/// private constructor
/// </summary>
internal ProductId(int item)
{
this.Item = item;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public int GetHashCode(IEqualityComparer comp)
{
const int num = 0;
return -1640531527 + (this.Item + ((num << 6) + (num >> 2)));
}
/// <summary>
/// Needed for custom equality
/// </summary>
public sealed override int GetHashCode()
{
return this.GetHashCode(LanguagePrimitives.GenericEqualityComparer);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(ProductId obj)
{
return obj != null && this.Item == obj.Item;
}
/// <summary>
/// Implement custom equality
/// </summary>
public sealed override bool Equals(object obj)
{
var productId = obj as ProductId;
return productId != null && this.Equals(productId);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(object obj, IEqualityComparer comp)
{
// ignore the IEqualityComparer as a simplification -- the generated F# code is more complex
return Equals(obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(ProductId obj)
{
if (obj == null)
{
return 1;
}
return this.Item.CompareTo(obj.Item);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj)
{
return this.CompareTo((ProductId)obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj, IComparer comp)
{
// ignore the IComparer as a simplification -- the generated F# code is more complex
return this.CompareTo((ProductId)obj);
}
}
The fact that the equivalent C# code is so long is one reason why primitive obsession is so common.
I know that, personally, I am much less likely to create a wrapper class in C# than in F#, just because of the effort involved.
Anyway, ignoring the equality and comparison code, we can see that the meat of the implementation is just a wrapper around a Item
property, along with a static constructor NewProductId
.
public class ProductId
{
/// <summary>
/// Property to access wrapped value
/// </summary>
public int Item { get; private set; }
/// <summary>
/// static public constructor
/// </summary>
public static ProductId NewProductId(int item)
{
return new ProductId(item);
}
/// <summary>
/// private constructor
/// </summary>
internal ProductId(int item)
{
this.Item = item;
}
Another common use of discriminated unions is to emulate an Enum.
For example, here’s one with three colors:
/// example of simple "enum"
type Color = Red | Green | Blue
This generates the following C# code:
/// <summary>
/// example of simple "enum"
/// </summary>
[Serializable]
public class Color :
IEquatable<Color>,
IStructuralEquatable,
IComparable<Color>,
IComparable,
IStructuralComparable
{
public static class Tags
{
public const int Red = 0;
public const int Green = 1;
public const int Blue = 2;
}
// singletons -- one for each "enum"
internal static readonly Color _unique_Red = new Color(0);
internal static readonly Color _unique_Green = new Color(1);
internal static readonly Color _unique_Blue = new Color(2);
/// <summary>
/// Implemented for all F# union types. Used in this case to distinguish between the singletons.
/// </summary>
public int Tag { get; private set; }
/// <summary>
/// Static method to get one of the singletons
/// </summary>
public static Color Red
{
get
{
return _unique_Red;
}
}
public bool IsRed
{
get
{
return Tag == 0;
}
}
/// <summary>
/// Static method to get one of the singletons
/// </summary>
public static Color Green
{
get
{
return _unique_Green;
}
}
public bool IsGreen
{
get
{
return Tag == 1;
}
}
/// <summary>
/// Static method to get one of the singletons
/// </summary>
public static Color Blue
{
get
{
return _unique_Blue;
}
}
public bool IsBlue
{
get
{
return Tag == 2;
}
}
/// <summary>
/// private constructor
/// </summary>
internal Color(int tag)
{
Tag = tag;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public int GetHashCode(IEqualityComparer comp)
{
return Tag;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public sealed override int GetHashCode()
{
return GetHashCode(LanguagePrimitives.GenericEqualityComparer);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(Color obj)
{
if (obj != null)
{
return Tag == obj.Tag;
}
return false;
}
/// <summary>
/// Implement custom equality
/// </summary>
public sealed override bool Equals(object obj)
{
var color = obj as Color;
return color != null && Equals(color);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(object obj, IEqualityComparer comp)
{
// ignore the IEqualityComparer as a simplification -- the generated F# code is more complex
return Equals(obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(Color obj)
{
if (obj == null)
{
return 1;
}
return Tag.CompareTo(obj.Tag);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj)
{
return CompareTo((Color)obj);
}
/// <summary>
/// Implement custom comparison
/// </summary>
public int CompareTo(object obj, IComparer comp)
{
// ignore the IComparer as a simplification -- the generated F# code is more complex
return CompareTo((Color)obj);
}
}
The C# implementation creates a set of static singleton instances (_unique_Red
, _unique_Green
, etc.) which are then used everywhere else. They are differentiated by their Tag
number.
In F#, you can also create a “real” enum by assigning int values to each case, like this:
/// example of a real C# enum
type ColorEnum = Red=1 | Green=2 | Blue=3
When decompiled into C# the definition is exactly an enum
:
[Serializable]
public enum ColorEnum
{
Red = 1,
Green,
Blue
}
So, why not use this all the time in F#? The main reason is that the enum
style is not suitable for exhaustive pattern matching.
Any int can be cast to an enum, so the F# pattern matching will always need to have a wildcard case. This is not needed for the discriminated union version that we defined in the previous section.
Finally, let’s look at a more complex union type.
Say that we have a PaymentMethod
type that can be cash, check or credit card. We might model it like this:
type CheckNumber = CheckNumber of int
type CardType = MasterCard | Visa
type CardNumber = CardNumber of string
/// PaymentMethod is cash, check or card
[<NoComparisonAttribute>]
type PaymentMethod =
/// Cash needs no extra information
| Cash
/// Check needs a CheckNumber
| Check of CheckNumber
/// CreditCard needs a CardType and CardNumber
| CreditCard of CardType * CardNumber
Note that I’m using the [<NoComparisonAttribute>]
since I don’t expect to be sorting the payment methods.
Decompiled to C#, this 12-line snippet results in four top-level classes and around 600 lines of code, as we’ll see!
First we need to define classes for the three helper types CheckNumber
, CardType
and CardNumber
.
For example, here’s the first few lines of CheckNumber
:
[Serializable]
public class CheckNumber :
IEquatable<CheckNumber>,
IStructuralEquatable,
IComparable<CheckNumber>,
IComparable,
IStructuralComparable
{
/// <summary>
/// Property to access wrapped value
/// </summary>
public int Item { get; private set; }
/// <summary>
/// static public constructor
/// </summary>
public static CheckNumber NewCheckNumber(int item)
{
return new CheckNumber(item);
}
/// <summary>
/// private constructor
/// </summary>
internal CheckNumber(int item)
{
this.Item = item;
}
// [snipped rest of file]
This is similar to the ProductId
type, as is CardNumber
. And CardType
is very similar to Color
.
Now for the main PaymentMethod
type.
I’m going to ignore all the equality and comparison code, and just focus on how it is implemented.
- Each case is represented by an inner class that is a subclass of
PaymentMethod
. - Each inner class has properties to store the data associated with that case.
- The
Cash
case has no associated data, so it only needs one instance, implemented as a static singleton. - A
Tag
is used to differentiate the subclasses and to help with equality testing and comparison.
Here’s the core code:
/// <summary>
/// PaymentMethod is cash, check or card
/// </summary>
[Serializable]
public abstract class PaymentMethod :
IEquatable<PaymentMethod>,
IStructuralEquatable
{
public static class Tags
{
public const int Cash = 0;
public const int Check = 1;
public const int CreditCard = 2;
}
/// <summary>
/// Private Subclass: Cash needs no extra information, so is represented by a singleton
/// </summary>
[Serializable]
internal class _Cash : PaymentMethod
{
}
/// <summary>
/// Public Subclass: Check needs a CheckNumber
/// </summary>
[Serializable]
public class Check : PaymentMethod
{
public CheckNumber Item { get; private set; }
internal Check(CheckNumber item)
{
Item = item;
}
}
/// <summary>
/// Public Subclass: CreditCard needs a CardType and CardNumber
/// </summary>
[Serializable]
public class CreditCard : PaymentMethod
{
public CardType Item1 { get; private set; }
public CardNumber Item2 { get; private set; }
internal CreditCard(CardType item1, CardNumber item2)
{
Item1 = item1;
Item2 = item2;
}
}
/// <summary>
/// Implemented for all F# union types. Used in this case for equality and comparison
/// </summary>
public int Tag
{
get
{
return (!(this is CreditCard)) ? ((!(this is Check)) ? 0 : 1) : 2;
}
}
// Cash has no extra data so can be implemented as singleton instance
internal static readonly PaymentMethod _unique_Cash = new _Cash();
/// <summary>
/// static public "constructor"
/// (just gets the singleton)
/// </summary>
public static PaymentMethod Cash
{
get
{
return _unique_Cash;
}
}
public bool IsCash
{
get
{
return this is _Cash;
}
}
/// <summary>
/// static public constructor
/// </summary>
public static PaymentMethod NewCheck(CheckNumber item)
{
return new Check(item);
}
public bool IsCheck
{
get
{
return this is Check;
}
}
/// <summary>
/// static public constructor
/// </summary>
public static PaymentMethod NewCreditCard(CardType item1, CardNumber item2)
{
return new CreditCard(item1, item2);
}
public bool IsCreditCard
{
get
{
return this is CreditCard;
}
}
/// <summary>
/// private constructor
/// </summary>
internal PaymentMethod()
{
}
/// <summary>
/// Needed for custom equality
/// </summary>
public int GetHashCode(IEqualityComparer comp)
{
if (!(this is _Cash))
{
const int offset = -1640531527;
var check = this as Check;
if (check != null)
{
const int num = 1;
return offset + (check.Item.GetHashCode(comp) + ((num << 6) + (num >> 2)));
}
var creditCard = this as CreditCard;
if (creditCard != null)
{
var num = 2;
num = offset + (creditCard.Item2.GetHashCode(comp) + ((num << 6) + (num >> 2)));
return offset + (creditCard.Item1.GetHashCode(comp) + ((num << 6) + (num >> 2)));
}
}
return 0;
}
/// <summary>
/// Needed for custom equality
/// </summary>
public sealed override int GetHashCode()
{
return GetHashCode(LanguagePrimitives.GenericEqualityComparer);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(PaymentMethod obj)
{
if (obj == null)
{
return false;
}
if (Tag != obj.Tag)
{
return false;
}
var check1 = this as Check;
if (check1 != null)
{
var check2 = (Check)obj;
return check1.Item.Equals(check2.Item);
}
var creditCard1 = this as CreditCard;
if (creditCard1 != null)
{
var creditCard2 = (CreditCard) obj;
return creditCard1.Item1.Equals(creditCard2.Item1) && creditCard1.Item2.Equals(creditCard2.Item2);
}
return true;
}
/// <summary>
/// Implement custom equality
/// </summary>
public sealed override bool Equals(object obj)
{
var paymentMethod = obj as PaymentMethod;
return paymentMethod != null && Equals(paymentMethod);
}
/// <summary>
/// Implement custom equality
/// </summary>
public bool Equals(object obj, IEqualityComparer comp)
{
// ignore the IEqualityComparer as a simplification -- the generated F# code is more complex
return Equals(obj);
}
}
In comparison with the 12 lines of F# code, the generated C# looks like this:
- Source for CheckNumber.cs (about 120 lines)
- Source for CardType.cs (about 150 lines)
- Source for CardNumber.cs (about 120 lines)
- Source for PaymentMethod.cs (about 220 lines)
That’s around 600 lines altogether. It’s true that some of that may be methods that we might not need, but even so, it’s still quite a lot of code to write by hand.
Now let’s look at modules.
In F#, a module is the standard technique for grouping standalone functions.
module ModuleExample
/// add two numbers
let Add x y = x + y
/// add 1 to a number
let Add1 x = x + 1
Behind the scenes, this code is implemented as static methods on a static class. Here’s the generated C# code:
public static class ModuleExample
{
/// <summary>
/// add two numbers
/// </summary>
public static int Add(int x, int y)
{
return x + y;
}
/// <summary>
/// add 1 to a number
/// </summary>
public static int Add1(int x)
{
return x + 1;
}
In F#, types can also be defined in modules:
module ModuleExample
/// define a empty class inside a module
type Something() = class end
This is represented in C# as a inner class – defined inside the static module class:
public static class ModuleExample
{
// [snipped Add and Add1]
[Serializable]
public class Something
{
public Something()
{
}
}
In F#, a module can contain submodules. For example, I might want to group some functions that work with the FinalGameScore
type defined at the top of this post.
module ModuleExample
// [snipped Add and Add1]
/// Create a submodule
module GameFunctions =
open RecordTypeExamples
/// Create a game with score=12
let CreateGame name = {Game=name; FinalScore=12}
/// Change the score for an existing game
let ChangeScore newScore game =
{game with FinalScore=newScore}
/// Example of a higher order function
let MapScore f game =
{game with FinalScore=f game.FinalScore}
In the generated C#, this code becomes another inner static class:
public static class ModuleExample
{
// [snipped Add and Add1]
/// <summary>
/// Create a submodule
/// </summary>
public static class GameFunctions
{
/// <summary>
/// Create a game with score=12
/// </summary>
public static FinalGameScore CreateGame(string name)
{
return new FinalGameScore(name, 12);
}
/// <summary>
/// Change the score for an existing game
/// </summary>
public static FinalGameScore ChangeScore(int newScore, FinalGameScore game)
{
return new FinalGameScore(game.Game, newScore);
}
/// <summary>
/// Example of a higher order function
/// </summary>
public static FinalGameScore MapScore(FSharpFunc<int, int> f, FinalGameScore game)
{
return new FinalGameScore(game.Game, f.Invoke(game.FinalScore));
}
}
}
A couple of things to note:
First, the F# code never mentions the type FinalGameScore
– it is inferred by the {game with FinalScore=newScore}
code.
On the other hand, the generated C# code mentions the type FinalGameScore
in many places – that’s why a tool like Resharper is so useful when you want to rename things.
Second, the MapScore
function is a simple example of a higher order function.
It takes an int->int
function to change the score. In normal C#, this would be represented by a Func<int,int>
, but F# has it’s own func type FSharpFunc
.
If you need to expose higher order functions from F# to C#, you will probably need to change the function type to a Func
.
The source code for the two implementations is available here:
Finally, let’s see what F# pattern matching code looks like when turned into C# code.
Let’s start with a simple integer pattern match. The code below has some integer pattern matching, plus a guard when x%2 = 0
, and finally the wildcard.
/// demonstrates some simple pattern matching
let IntPatternMatching x =
match x with
| 1 -> "1"
| 2 -> "2"
| 3 -> "3"
| 4 -> "4"
// example of guard
| e when x%2 = 0 -> "even"
// wildcard
| _ -> "other"
The generated code is a straightforward switch statement.
public static string IntPatternMatching(int x)
{
switch (x)
{
case 1:
return "1";
case 2:
return "2";
case 3:
return "3";
case 4:
return "4";
default:
if (x % 2 == 0)
{
return "even";
}
return "other";
}
}
But what if the value being matching on is not a primitive? In F# we can do nested pattern matching that accesses the internal of the value.
For example, let’s say that we have a Person
type that contains a Name
type. Then in F# we can write:
type Name= {First:string; Last:string}
type Person = {Name:Name; Age:int}
/// demonstrates some nested pattern matching
let NestedPatternMatching person =
match person with
| {Name={First="Jane";Last="Doe"}} -> "Jane Doe"
| {Name={First="Jane"}} -> "Jane something"
| {Name={Last="Doe"}} -> "something Doe"
// example of guard
| {Age=age} when age > 18 -> "Adult"
// wildcard
| _ -> "other"
The generated code looks like this in C#, which is quite clunky, but similar to what you would have to write by hand:
public static string NestedPatternMatching(Person person)
{
if (string.Equals(person.Name.First, "Jane"))
{
if (string.Equals(person.Name.Last, "Doe"))
{
return "Jane Doe";
}
return "Jane something";
}
else
{
if (string.Equals(person.Name.Last, "Doe"))
{
return "something Doe";
}
if (person.Age > 18)
{
return "Adult";
}
return "other";
}
}
Note that the static string.Equals
is used to avoid an additional null check for person.Name.First
and person.Name.Last
.
F# supports pattern matching in function parameters as well. This is a great way to specify the parameter type and to extract a value in one step.
For example, in the following code, we extract the inner string from the Email
value, lowercase it, and return a new Email
value.
/// demonstrates some in-parameter pattern matching
let LowercaseEmail (Email e) =
e.ToLowerInvariant() |> Email
The C# for this is very similar, except that the parameter type must be given explicitly:
/// <summary>
/// demonstrates some in-parameter pattern matching
/// </summary>
public static Email LowercaseEmail(Email email)
{
var lower = email.Item.ToLowerInvariant();
return Email.NewEmail(lower);
}
When it comes to pattern matching on lists, F# provides some nice syntax for fixed sized lists (e.g. [a;b]
) or heads and tails (e.g. a::b::rest
).
/// demonstrates some list-testing pattern matching
let ListTesting list =
match list with
| [] ->
sprintf "Empty list"
| [a;b] ->
sprintf "Exactly two elements %A and %A" a b
| a::b::rest ->
sprintf "Two or more elements starting with %A and %A" a b
| a::rest ->
sprintf "One or more elements starting with %A" a
When we look at the generated code this pattern matching consists of a series of if
statements as shown below.
public static string ListTesting<T>(FSharpList<T> list)
{
// test for empty
var tail1 = list.TailOrNull;
if (tail1 == null)
{
return ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<string, Unit, string, string, Unit>("Empty list"));
}
// first element is valid
var firstElem = list.HeadOrDefault;
// test for one or more elements
if (tail1.TailOrNull == null)
{
var print = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<T, string>, Unit, string, string, T>("One or more elements starting with %A"));
return print.Invoke(firstElem);
}
// second element is valid
var secondElem = tail1.HeadOrDefault;
// test for exactly two elements
var tail2 = tail1.TailOrNull;
if (tail2.TailOrNull == null)
{
var print2 = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<T, FSharpFunc<T, string>>, Unit, string, string, Tuple<T, T>>("Exactly two elements %A and %A"));
return print2.Invoke(firstElem).Invoke(secondElem);
}
// test for two or more elements
var print3 = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<T, FSharpFunc<T, string>>, Unit, string, string, Tuple<T, T>>("Two or more elements starting with %A and %A"));
return print3.Invoke(firstElem).Invoke(secondElem);
}
Notes:
- I have simplified the real generated code somewhat while maintaining the core features.
- The long-winded
ExtraTopLevelOperators.PrintFormatToString
is just the external name ofsprintf
! Obviously, if I were writing this in C# myself, I would usestring.Format
. print2
andprint3
are in curried form and are invoked twice – once for each parameter.
So, replacing the F# printing with string.Format
, a more idiomatic C# version would look like this:
public static string ListTesting<T>(FSharpList<T> list)
{
// test for empty
var tail1 = list.TailOrNull;
if (tail1 == null)
{
return "Empty list";
}
// first element is valid
var firstElem = list.HeadOrDefault;
// test for one or more elements
if (tail1.TailOrNull == null)
{
return string.Format("One or more elements starting with {0}", firstElem);
}
// second element is valid
var secondElem = tail1.HeadOrDefault;
// test for exactly two elements
var tail2 = tail1.TailOrNull;
if (tail2.TailOrNull == null)
{
return string.Format("Exactly two elements {0} and {1}", firstElem, secondElem);
}
// test for two or more elements
return string.Format("Two or more elements starting with {0} and {1}", firstElem, secondElem);
}
Finally, let’s look at how pattern matching on types is done using the :?
operator.
/// demonstrates some type-testing pattern matching
let TypeTesting obj =
match box obj with
| :? string as s ->
sprintf "Obj is string with value %s" s
| :? int as i ->
sprintf "Obj is int with value %i" i
| :? Person as p ->
sprintf "Obj is Person with name %s %s" p.Name.First p.Name.Last
| _ ->
sprintf "Obj is something else"
The generated code looks like this:
public static string TypeTesting<T>(T obj)
{
// NOTE: This is a simplified version of the real generated code
var str = obj as string;
if (str != null)
{
var printString = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<string, string>, Unit, string, string, string>("Obj is string with value %s"));
return printString.Invoke(str);
}
if (LanguagePrimitives.IntrinsicFunctions.TypeTestGeneric<int>(obj))
{
var i = (int)(obj as object);
var printInt = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<int, string>, Unit, string, string, int>("Obj is int with value %i"));
return printInt.Invoke(i);
}
var person = obj as Person;
if (person != null)
{
var printPerson = ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<FSharpFunc<string, FSharpFunc<string, string>>, Unit, string, string, Tuple<string, string>>("Obj is Person with name %s %s"));
return printPerson.Invoke(person.Name.First).Invoke(person.Name.Last);
}
return ExtraTopLevelOperators.PrintFormatToString(
new PrintfFormat<string, Unit, string, string, Unit>("Obj is something else"));
}
Notes:
- Again, I have simplified the real generated code somewhat while maintaining the core features.
- Note that in the F# code I never had to test that a string or Person was null. The null case is handled by the wildcard branch. In the generated code, the null test is added for me.
printPerson
is in curried form and is invoked twice – once for each parameter.
The source code for the pattern matching examples is available here:
The title of this post is “F# decompiled into C#”. That was the primary purpose of this post – to demonstrate the kind of boilerplate code that the F# compiler generates for you “for free”, as a service to people who don’t have a tool like ILSpy handy.
The subtitle of this post is “What C# code do you have to write to get the same functionality as F#?” That is, if I stopped using F#, and wanted to write equivalent code in C#, what would I have to do?
Now obviously, the code generated by the F# compiler has a lot of stuff in it that I would not bother to reproduce if I were writing the equivalent C# by hand.
For example, we might not need to implement CompareTo
in many cases, and many of the methods in the generated code might be tweaked to be smaller.
Nevertheless, even with these caveats, and even writing the most compact and idiomatic C# code possible, it is hard to create, say, an immutable wrapper type with equality in a very few lines of C# code.
Unfortunately, that overhead discourages me from creating small classes that model the domain and encourages the “primitive obsession” code smell. I’m sacrificing good design and maybe even introducing bugs because the language makes it painful for me to do what I want.
For those of you who think I am being “brutal” – sorry. This is not meant to be a gloat or an attack on C#, just an example of what F# provides for free.
EDIT: Based on the feedback in the comments, I have tweaked the C# code to be more idiomatic, using auto-properties, etc. Also, the summary above and some of the body text has been updated.
The complete code for this post is available on GitHub