Re: [Haskell-beginners] How to nest arbitrary things

how to write an "unpack" function
is this what Lenses are all about? https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/a-... tutorial I stumbled across Lenses only recently so can not write a working example yet. if Lenses are relevant to this topic, could someone kindly post an appropriate (Truck Parcel Can) datatype and an "unpack" with Lenses example?

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

a lens with the name 'cans' will be created.
Does Template Haskell modify code file or can these '_' names be used throughout the code without visible definition? Thank you for this very detailed and specific example. Could we say that lens is a bit like extension to record syntax: allow to traverse, get and set properties for structures that are more complex than simple 1 level record? Why do lens require Template Haskell? Only to generate '_' functions?

On Mon, Dec 21, 2015 at 09:06:55PM +0100, Imants Cekusins wrote:
a lens with the name 'cans' will be created.
Does Template Haskell modify code file or can these '_' names be used throughout the code without visible definition?
In this case Template Haskell doesn't modify the code, fields like '_cans' are just normal fields and can be used with this name. There's only the convention that 'makeLenses' creates for every field with a '_' prefix a lens. In the case of '_cans' a lens like this will be created: cans :: Lens' Parcel [Can] cans = lens getCans setCans where getCans parcel = _cans parcel setCans parcel cans = parcel { _cans = cans } (This is just an idealized implementation and the real one will be most likely a bit more sophisticated and optimized.)
Could we say that lens is a bit like extension to record syntax: allow to traverse, get and set properties for structures that are more complex than simple 1 level record?
It's often compared with some kind of jQuery for data structures.
Why do lens require Template Haskell? Only to generate '_' functions?
To automatically generate the Haskell code like the lens 'cans' in the above example. Greetings, Daniel

On Mon, Dec 21, 2015 at 09:56:04PM +0100, Imants Cekusins wrote:
makeLenses ''Parcel
any cues re: what '' stand for in the above expression?
It's just the escaping of the name needed for Template Haskell. I think it's the same like calling: makeLenses "Parcel"

Thank you very much Daniel. Lens is a lot clearer now. jQuery eh? $(truckContents . _TCBox) ;)
participants (2)
-
Daniel Trstenjak
-
Imants Cekusins