This is exactly the kind of situation you would use lensing for. The type IRef<'T> is an abstract class that is implemented by Var<'T>, but also returned by the Lens method which creates a bidirectional binding into another IRef<'T>:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Book =
  { Title : string
    Pages : Page list }

  static member LensTitle (v: IRef<Book>) : IRef<string> =
    v.Lens (fun b -> b.Title) (fun b t -> { b with Title = t })

  static member LensPages (v: IRef<Book>) : IRef<Page list> =
    v.Lens (fun b -> b.Pages) (fun b p -> { b with Pages = p })

// Creating a Var<Book>:
let someBook = Var.Create { Title = "Expert F# 3.0"; Pages = [] }
// Lensing into its title:
let someBooksTitle = someBook |> Book.LensTitle
// Releasing the 4th edition:
someBooksTitle.Set "Expert F# 4.0"
// It's now released.
someBook.Value // { Title = "Expert F# 4.0"; Pages = [] }

Input functions take IRef<'T> as argument, so you can do something like Doc.Input [] someBooksTitle.

By on 3/21/2016 2:08 AM ()

I kind of had the intuition of using Lens after István pointed it out to me and also after seeing your gist. But my issue with that is that it becomes horribly complicated when I want to lens into a member inside Comment which is two level deep Book > Page > Comment.

Would you have an example for that?

By on 3/21/2016 2:46 AM ()

Let's assume you have lenses defined for each type in a fashion as Loic mentioned with the addition of a lens that select and updates a single page/comment in the given list. You could define a ListModel of books then lens all the way down to the content of a comment. So using an immutable model your code might look like the following: http://try.websharper.com/snippet/qwe2/00007D. But there is an issue here: since our model is immutable, we have to copy and update the whole thing even if we just change one comment. So, while this clean and pretty, if you have a huge amounts of books and pages and comments this will get pretty slow. What i would do in that case is to define pages and comments to be ListModel<int, Page> and ListModel<int, Comment>. With this you lose the ability to conveiniently make your model persistent but you get much better performance because if a single comment changes you'd only need to update a single DOM node and not the whole tree as with the above solution.

By on 3/21/2016 3:54 AM ()

Hi Loïc and István,

I took some time to change my previous book sample to use the lens all the way down way (not sure how do describe it) with the help of the samples you both provided.

There was an issue with the lost of focus on the input due to refresh of the model. I fixed it using a snapshot.

I really like using the Lenses, it feels really like natural way to update nested members of immutable object although it seems like it won't work in my case.

Thanks for the great advices!

By on 3/22/2016 3:00 PM ()

Nice thanks for the sample! Those two ways seem definitly more appropriate than having a record of Vars.

If there was 100 books, with 100 pages in each books, with 100 comments in each pages, would that be considered a huge amount?

If I had to take the route of having separate lists, you mentioned:

define pages and comments to be ListModel<int, Page> and ListModel<int, Comment>

Do you mean changing the Book and Page lists into ListModel?

By on 3/21/2016 5:52 AM ()

Yes, I meant replacing the lists with ListModels. I'm not exactly sure on performance here but I would think having 100s of everything would already cause some UI flickering when modifying something like a comment. I would probably go with using ListModels in that case.

By on 3/21/2016 3:45 PM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper