Skip to content

How to write a lens for grandchildren by ID when you don't know the child ID #64

@cmeeren

Description

@cmeeren

I have the following type definitions in F#. How can I create a lens from Parent to a given GrandChild (identifier by ID)?

type GrandChild = { Id: int }

type Child = { Id: ChildId; Children: GrandChild list }

type Parent = { Children: Map<ChildId, Child> }

Here is my ugly attempt, which seems like just the kind of thing lenses is used to avoid:

module Parent =

  let grandChild grandChildId : Lens<Parent, GrandChild> =
    (fun x ->
      x.Children
      |> Map.toList
      |> List.collect (fun (_, c) -> c.GrandChildren)
      |> List.find (fun gc -> gc.Id = grandChildId)
    ),
    (fun v x ->
      {
        x with
          Children =
            x.Children
            |> Map.map (fun _ c ->
                { c with
                    GrandChildren =
                      c.GrandChildren
                      |> List.map (fun gc -> if gc.Id = grandChildId then v else gc) }
            )
      }
    )

I'm not even sure this follows the optic laws.

Part of my challenge here is that there is no correspondence between the child IDs and the grandchild IDs. When I'm at the parent level and want to update a specific GrandChild, I have to check all Children and all GrandChildren.

I assume that there is an easier and more composable way of doing this, but it's escaping me.

If it would simplify anything, you can assume that the grandchild specified by grandChildId is guaranteed to exist in the hierarchy (hence the use of Lens instead of Prism).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions