Reputation: 3838
I am trying to model a domain as records and discriminated unions, i.e. immutable. I noticed, that I have a m:n relationship, say authors and books. And now I'm looking for a good way of expressing it, such that:
So far, i didn't find any solution fulfilling all of the criteria. What I found was:
Are there any solutions that fulfil all of the above, maybe something like an immutable ref cell? And if so, what do they look like.
Upvotes: 3
Views: 210
Reputation: 243061
As I mentioned in the comments, if you have an immutable list of authors and map over it, you do not actually end up copying all the authors. The authors who do not change will just be pointing to the same instance (so the overhead of immutable solution is not that big).
However, it is true that if you have multiple books with the same author, there is no aliasing and so the authors will all be separate values (and you will have to map over all books).
I think a reasonable representation in this case would be to keep the authors and books separate and link them via a key (such as author name - in the example below - or some other ID):
type Author = { Name : string; Address : string }
type Book = { Title : string; Author : string }
// For efficient lookup, create a hashtable with authors
let authors = dict [ "Tomas", { Name = "Tomas"; Address = "Cambridge" } ]
// Books are stored simply as a list
let books = [ { Title = "Real World FP"; Author = "Tomas" } ]
// To get nice access, add AuthorDetails property to the Author record
type Book with
member x.AuthorDetails = authors.[x.Author]
for book in books do
printfn "%s (%s, %s)" book.Title book.Author book.AuthorDetails.Address
This does not let you mutate the collection authors
. If you were writing this in a purely functional way, you would probably have some recursive function that keeps current authors as argument and so you would not need mutation (just build a new dictionary).
But I think it is reasonable to have a ref
value holding the dictionary, or even keep a mutable dictionary (but I would do that only if you do not have concurrency; in fact, in presence of concurrency, Map
might be safer choice).
Upvotes: 4