2

Assumptions:

  • I know that Haskell encourages solving problems with a type system, and Clojure eschews a type system, preferring to solve the problem with data structures.

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?

hawkeye
  • 34,745
  • 30
  • 150
  • 304
  • 3
    "Clojure eschews a type system, preferring to solve the problem with data structures" is a very strange claim. Data structures and functions are the basis of Haskell's type system. Haskell just requires you to prove you're using the correct one instead of letting it blow up at runtime. – Carl Feb 14 '15 at 23:38
  • Thanks - that wasn't very clear. Within the Clojure community there is a truism quoted "Data > Functions > Macros" which means where you have a choice about encoding your logic in data structures, or functions or macros, first choose data structures, then functions, then macros if you really have to. For example (:hello {:hello #(println "Hello World")}) is a way of encoding logic for a hello world function. – hawkeye Feb 14 '15 at 23:52
  • I'm not familiar with Clojure's key sequences, but if I had to guess the main difference would be that Haskell's lenses are typed and statically checked at compile time, avoiding runtime failures. – Gabriella Gonzalez Feb 15 '15 at 00:03
  • Maybe I'll update the assumptions - but in Clojure I can wrap the above in a macro-expansion and get a 'compile-time-check'. Does that help? – hawkeye Feb 15 '15 at 00:27
  • 2
    @hawkeye what if the function and/or the arguments are statically unknown in an application? Then at best you get a runtime check, using macros, and failure must be handled somehow all the same. A core point of type checking is to prove invariants about all kinds of statically unknown things without running the program. – András Kovács Feb 15 '15 at 07:16
  • possible duplicate of [When manipulating immutable datastructures, what's the difference between Clojure's assoc-in and Haskell's lenses?](http://stackoverflow.com/questions/21291712/when-manipulating-immutable-datastructures-whats-the-difference-between-clojur) – A. Webb Feb 17 '15 at 13:16

1 Answers1

3

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.