
On Mon, Dec 21, 2015 at 06:47:54PM +0100, Imants Cekusins wrote:
if Lenses are relevant to this topic, could someone kindly post an appropriate (Truck Parcel Can) datatype and an "unpack" with Lenses example?
For a beginner lenses might be a bit too much, because their error messages can be quite hairy, so I most likely would suggest not using them for the moment. Otherwise modifying data in a deeply nested data structure without lenses can be such a pain, so here's an example. Taking the example from Frerich and making it a bit easier usable with lenses: {-# Language TemplateHaskell #-} import Control.Lens data Can = Can deriving Show data Parcel = Parcel { _cans :: [Can] } deriving Show makeLenses ''Parcel data BoxContent = BCCan Can | BCParcel Parcel deriving Show makePrisms ''BoxContent data Box = Box { _boxContents :: [BoxContent] } deriving Show makeLenses ''Box data TruckContent = TCCan Can | TCParcel Parcel | TCBox Box deriving Show makePrisms ''TruckContent data Truck = Truck { _truckContents :: [TruckContent] } deriving Show makeLenses ''Truck By default 'makeLenses' will make lenses for every field in the record prefixed with '_', so e.g. for the field '_cans' of the record 'Parcel' a lens with the name 'cans' will be created. For ADTs like 'BoxContent' a "special" kind of lens is created - called prism - by calling 'makePrisms'. For every data constructor of 'BoxContent' - in this case 'BCCan' and 'BCParcel' - a prism with the name of the data constructor prefixed with a '_' is created: '_BCCan' and '_BCParcel'. Now put the whole above Haskell code into a file like 'Main.hs'. If you have already 'cabal' installed, then installing the 'lens' library into a sandbox and opening a repl with the 'Main.hs' file is one way of testing it: ~> cabal sandbox init ~> cabal install lens ~> cabal repl And now calling inside of the repl: :load Main.hs Creating a truck with some contents: let truck = Truck [TCBox (Box [BCCan Can])] Looking at the contents of the truck: truck ^. truckContents You can read the '^.' as applying the lens 'truckContents' on the variable 'truck'. It has the same effect as calling the field accessor '_truckContents' on 'truck'. _truckContents truck Now you can go deeper: truck ^.. truckContents . traverse . _TCBox This already involves two new things '^..' and 'traverse'. 'traverse' does visit every 'TruckContent' in 'truckContents', so it's in lens speak a traversal and because it might give multiple results you need the '^..', which collects all results into a list. The '_TCBox' works like a filter, so you're collecting all 'TCBox' of all 'TruckContent'. Try it with '_TCParcel'. Now you can go even deeper: truck ^.. truckContents . traverse . _TCBox . boxContents . traverse . _BCCan Until now you only viewed the data, but if you want to modify the data you need the operators '&', '.~' and '~%'. For e.g. to clear (setting an empty list) the 'boxContents' of every 'TCBox': truck & truckContents . traverse . _TCBox . boxContents .~ [] Or modifying the 'boxContents' by adding a 'BCCan': truck & truckContents . traverse . _TCBox . boxContents %~ (BCCan Can :) This could be also written as: truck & truckContents . traverse . _TCBox . boxContents %~ (\contents -> BCCan Can : contents) This is only the tip of the iceberg regarding lenses, but with 'makeLenses', 'makePrisms', '^.', '^..', '.~', '&' and '~%' you can already get quite far. Hopefully this was a bit helpful and not too much at once. :) These examples use the lenses from the 'lens'[1] library. Greetings, Daniel [1] https://hackage.haskell.org/package/lens