haskellclojurehaskell-lensnested-map

What are the similarities and differences between a lens in Haskell and using a key-sequence in Clojure?


Assumptions:

We can see that we can create a lens like this in Haskell:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

initialState :: Game
initialState = Game
    { _score = 0
    , _units =
        [ Unit
            { _health = 10
            , _position = Point { _x = 3.5, _y = 7.0 }
            }
        ]
    }

health :: Lens' Unit Int
health = lens _health (\unit v -> unit { _health = v })

It's purpose is to get the health value from the game data structure.

We can access a nested structure using a key-sequence like this in Clojure:

(def initialState {:score 0 
                   :units {:health 10
                           :position {:x 3.5
                                      :y 7.0}}})

(def health [:units :health])

(defn initialState-getter [lens]
  (get-in initialState lens))

(initialState-getter health)
> 10

(defn initialState-setter [lens value]
  (assoc-in initialState lens value))

(initialState-setter health 22)
> {:score 0, :units {:health 22, :position {:y 7.0, :x 3.5}}}

Here we see a nested structure being updated by a key sequence.

My question is: What are the similarities and differences between a lens in Haskell and using a key-sequence in Clojure?


Solution

  • Lenses in Haskell are not restricted to keys. You could for example write a lens for the length of a string:

    lengthLens = lens length setLength
    
    setLength s l = take l s ++ take (l - length s) (repeat '\0')
    

    Clojure key sequences are restricted to map/vector/etc keys. I personally don't think this is a loss since I've never had a need for lenses for non-key-like getters and setters.

    As for types vs data, a lens composition in Haskell is data much like functions are data. This is similar to how vectors of keys are data in Clojure.