Search code examples
haskelllenseshaskell-lens

How to write an instance of Control.Lens.AT


I have a data structure that can be understood as analogous to Data.Map in that it maps keys of one type to values of another. I would like to write an instance of Control.Lens.At for this type but I cannot ever seem to satisfy all the requirements.

Given Struct k v with lookup, insert, update, and delete, what must I do to make instance At (Struct k v) work?


Solution

  • The at method should return an indexed lens for a given index gets as input your structure and behaves like this:

    • On getting, if the key is not present, return Nothing, otherwise return the value at the key in the structure.
    • On setting, if new value is Nothing, remove the key from the structure, otherwise set it (or create it if it's not already there) to the value in the Just.
    • The index is just the key given to at.

    This leads to the following code for your requirements:

    instance At (Struct k v) where
      at key = ilens getter setter
        where getter = (key, lookup key)
              setter s Nothing = delete key s
              setter s (Just newValue) = insert key newValue s
    

    I use lens to construct a lens ilens to construct an indexed lens from a getter and a setter. I also assume that your functions have the following types:

    lookup :: k -> Struct k v -> Maybe v
    delete :: k -> Struct k v -> Struct k v
    insert :: k -> v -> Struct k v -> Struct k v
    -- Insert should override the key when it's already there
    

    You still have to define the IxValue and Index type family instances:

    type instance IxValue (Struct k v) = v -- A (Struct k v) contains values of type v
    type instance Index (Struct k v) = k   -- A (Struct k v) has keys of type k.
    

    EDIT: Actually, at must return an indexed lens, not just a lens. I also confused the order of the arguments to the setter.