I’m delivering a talk on NoSQL and F# at the F# User Group in Cambridge, MA in a few weeks. As I started prepping my old C# and NoSQL presentation for an F# makeover, it didn’t take long for me to find some critical differences that more or less fly in the face of common ORM patterns.
The fetch-modify-update idiom is often used when dealing with updates to a record. If you consider a form where an Artist record may be edited, the (web) workflow is typically to:
- Fetch the record for display on an edit form
- Re-fetch the record when changes are submitted
- Update any properties that were changed by the form (perhaps with MVVM and AutoMapper)
- Save the record that was fetched and updated
In abbreviated code, this might look like:
var artists = _mongoDatabase.GetCollection(COLLECTION);
var artist = artists.Find(Query.FindOne(Query.EQ("_id", id)));
artist.Name = viewModel.Name;
artists.Save(artist);
This pattern works well with ORMs such as the Entity Framework and NHibnerate. It also works with O(Non)RMs such as the MongoDB C# driver, which is shown in the code above. Again, the idea is to retrieve a record, modify some properties and save it back to the database, whether relational or NoSQL. However, this pattern breaks one of the core functional programming concepts, namely immutability.
As I sat down with the F# equivalent of my NoSQL demoware, I had a choice to make. I could take advantage of F#’s multi-paradigm language features and simply allow for mutable Artist instances or I could work through the problem using immutability. I chose the latter option, which I’ll describe below. But first, I’ll note that this post is certainly geared towards C# developers who are new to F#. F# developers probably don’t get too hung up on value bindings vs. variables as I do.
The code in F# is still only 4 lines, but there are a couple of notable differences.
let artists = mongoDatabase.GetCollection("Artists")
let artist = artists.Find(Query.FindOne(Query.EQ("_id", id)))
let updatedArtist = { artist with Name = viewModel.Name }
mongoCollection.Save(updatedArtist) |> ignore
The requirement that artist be immutable leads to the use of an F# record. An F# record is sort of like an anonymous class in C# (no constructor or methods), but it has a name and has built in facilities for copying one record to another. That copying facility is what is demonstrated on the third F# line. The updatedArtist value is a member for member copy of the artist, except for the Name property that is set to the view model’s name property.
type Artist = { Name : string; Genre : string; Id : ObjectId }
Records seem to work particularly well with ORMs and O(Non)RMs where POCOs are used to represent database records, whether documents or table rows.