Generic lenses for F# record-types
F# record-types come in really handy when you need to model certain types of data-structures: Especially if you need to serialize them later on to send them over the wire or store them inside a database.
Check out Scott Wlaschins introduction to record-types in case you haven’t done so already: http://fsharpforfunandprofit.com/posts/records/
They’re easy to use and let you build highly nested and relational data-structures in no-time. So fast in fact that I always forget how tedious it is to update and modify them, as you basically need to traverse up the entire tree to modify a single property at one end of the tree.
Knowing Haskells Lens library and how it solves this issue, I immediately went on the search for an F# library to help me out. Aether (https://github.com/xyncro/aether) looks like a good implementation of Lenses but I wasn’t so happy about having to write the accessor functions for all the properties on my records.
Andrew Cherry goes a bit deeper into Lenses, Aether and the problems with updating record- types: https://kolektiv.github.io/fsharp/aether/2014/08/13/aether-guide/
Since I wasn’t worrying about performance at all: Quotation-expressions and reflection seemed like a viable option and as it turns out this was way simpler to solve than I imagined.
Here is a nice post on what quotation-expressions are and how they can be useful by Tomas Petricek: http://tomasp.net/blog/fsquotations.aspx/
By wrapping the path to a property of a record type inside a quotation-expression you gain access to the entire tree-structure. That makes it easy to construct a copy of the tree and swap out the value of the given property.
I am quite happy with that solution as it doesn’t require any additional code and should work with every record-type; no matter how deeply nested they are.
Here are a couple of examples on how the above record (BusCompany) is modified by simply providing the property and the new value.
Not sure how useful this is going to be for others as it is certainly not the fastest way of doing this. It was really helpful though for sketching out data-types, models and their relationships…
As it turns Robert Jeppesen used the same technique and has a very similar implementation over here: https://bitbucket.org/rojepp/reflenses/src/0c80f965aeaa?at=default