F# – Record types vs classes

You can become a serverless blackbelt. Enrol to my 4-week online workshop Production-Ready Serverless and gain hands-on experience building something from scratch using serverless technologies. At the end of the workshop, you should have a broader view of the challenges you will face as your serverless architecture matures and expands. You should also have a firm grasp on when serverless is a good fit for your system as well as common pitfalls you need to avoid. Sign up now and get 15% discount with the code yanprs15!

In F#, you can defined record types which differ from tuples and discriminated unions in that they allow you to organize values into a type and name those values through fields:


Looks like a cut-down version of a standard .Net class with a couple of properties? Yes they do, but records offer several advantages over the .Net classes.

Type Inference

One of the biggest advantage records offer is that it works seamlessly with F#’s type inference system. Whereas classes must be annotated in order to be used, record types can be inferred by the fields you access!

Consider this simple function:


and see what its function signature reads:


Magic! Well, with a caveat :-P As you can imagine, if you define a second record type which contains the same set of fields then it might just confuses the compiler a little. For instance, if you define a record type for employees:


The function signature for the printPerson function will become:


So to tell the printPerson function to use the Person record instead, you’ll need to annotate the parameter p:



Unlike classes, record fields are immutable by default. This immutability by default approach (zero side effect) is inline with everything else in F# and in functional languages in general, and offers a degree of safety guarantee.

However, whether immutability is an advantage very much depends on your use case, Eric Lippert made an excellent argument here that the lack of side effects and immutable data structures function languages offer are both advantages and disadvantages.

Fortunately, with F# record types, it’s possible to change the field values, either by using the copy and update record expression:


or you can explicitly specify that a field should be mutable:


then you are allowed to update the value of the mutable fields:


No Inheritance

Records cannot be inherited, which gives you a future guarantee of safety but then again you can equally make arguments against it, another post another day maybe..

Pattern Matching

Records can be used as part of standard pattern matching whereas classes cannot without resorting to active patterns or with when guards.

This is important, as you’re likely to be using pattern matching everywhere in your code and frankly, why wouldn’t you? It’s so awesome ;-) You can mach the structure of records and not every record field needs to be a part of the pattern match:


Equally you can capture values and match literal:


Structural Equality Semantics

Standard .Net classes have reference equality semantics (two reference type variables are deemed equal if they point to the same location in memory), but records have structural equality semantics (two values are deemed equal if their fields are equal):


In this respect, records are the same as .Net structs, which also structural equality semantics and cannot be inherited.

Update 27/09/2011:

One thing I forgot to mention is that whilst you can define a record type with just the list of fields, you can also add methods and properties using the member keyword:


Liked this article? Support me on Patreon and get direct help from me via a private Slack channel or 1-2-1 mentoring.
Subscribe to my newsletter

Hi, I’m Yan. I’m an AWS Serverless Hero and I help companies go faster for less by adopting serverless technologies successfully.

Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.

Hire me.

Skill up your serverless game with this hands-on workshop.

My 4-week Production-Ready Serverless online workshop is back!

This course takes you through building a production-ready serverless web application from testing, deployment, security, all the way through to observability. The motivation for this course is to give you hands-on experience building something with serverless technologies while giving you a broader view of the challenges you will face as the architecture matures and expands.

We will start at the basics and give you a firm introduction to Lambda and all the relevant concepts and service features (including the latest announcements in 2020). And then gradually ramping up and cover a wide array of topics such as API security, testing strategies, CI/CD, secret management, and operational best practices for monitoring and troubleshooting.

If you enrol now you can also get 15% OFF with the promo code “yanprs15”.

Enrol now and SAVE 15%.

Check out my new podcast Real-World Serverless where I talk with engineers who are building amazing things with serverless technologies and discuss the real-world use cases and challenges they face. If you’re interested in what people are actually doing with serverless and what it’s really like to be working with serverless day-to-day, then this is the podcast for you.

Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. We will also cover latest features from re:Invent 2019 such as Provisioned Concurrency and Lambda Destinations. Enrol now and start learning!

Check out my video course, Complete Guide to AWS Step Functions. In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. There is something for everyone from beginners to more advanced users looking for design patterns and best practices. Enrol now and start learning!