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 do destructuring like this in Clojure:
user=> (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}})
#'user/book
user=> (let [{name :name {pages :pages isbn-10 :isbn-10} :details} book]
(println "name:" name "pages:" pages "isbn-10:" isbn-10))
name: SICP pages: 657 isbn-10: 0262011530
The purpose is to get the nested values for name, pages etc from the nested structure.
In both cases you're coming up with a mechanism to reusably retrieve values from a nested structure.
My question is: What are the similarities and differences between a lens in Haskell and destructuring in Clojure?
Your Clojure code would most closely be expressed like this in Haskell:
book :: Book
book = Book {name = "SICP", details = Details {pages = 657, isbn_10: "0262011530"}}
main = do
let Book {name = name, details = Details {pages = pages, isbn_10 = isbn_10}} = book
putStrLn $ "name:" ++ name ++ "pages:" ++ (show pages) ++ "isbn-10:" ++ isbn-10
Here we used Haskell's pattern matching, which is basically a more general form of Clojure's destructuring bind, to get at the contents of the record. This has nothing to do with lenses.
Now let's look at your Haskell code with lenses. This code has three record types, Game, Unit and Point, and those types each have an automatically-defined set of functions to access their members without pattern matching. These functions are called _score
, _health
, _x
and so on (just like the member names). They take a Game
, Unit
or Point
respectively and produce the value of their respective member. If these functions would have to be defined manually (which they don't because Haskell defines such functions for all record types automatically), it'd look like this:
_health (Unit {health = h}) = h
So far this still has nothing to do with lenses.
The lens in the code is health
. It contains the function _health
(which, as described, takes a Unit
and produces its health) and the function (\unit v -> unit { _health = v })
, which takes a unit and a value and produces a new unit whose health has been set to that value. In other words the lens contains the code to both get and set the health of a unit. The Lens library now contains various functions that you can use to compose and work with such lenses.
So to answer your question: There are no similarities between lenses and destructuring.