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:
1 2 3 4 | var artists = _mongoDatabase.GetCollection<Artist>(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.
1 2 3 4 | let artists = mongoDatabase.GetCollection<Artist>("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.
1 | 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.