I'm trying to create lenses for the following data structure. I'm using lens-family
.
data Tree = Tree {
_text :: String,
_subtrees :: [Tree]
} deriving (Show,Eq,Read,Generic)
I'd like to avoid using Template Haskell for various reasons. For one, it seems it's not available for my version of ghc (7.8.3), that's another (out of scope) problem.
Creating lenses for the record wasn't to hard.
text :: Lens' Tree String
text f (Tree text' subtrees') =
(\text'' -> Tree text'' subtrees') `fmap` (f text')
subtrees :: Lens' Tree [Tree]
subtrees f (Tree text' subtrees') =
(\subtrees'' -> Tree text' subtrees'') `fmap` (f subtrees')
But it seems that lens-family doesn't have default lenses for lists. I assume this is possible. There are for the lens
package. These are my failed attempts:
import Lens.Family2
import Lens.Family2.Unchecked
-- List lenses
_last :: Lens [a] [a'] a a'
_last f l =
-- first try
--lens getter setter
-- second try
(\el -> (init l) ++ el) `fmap`(f (last l))
where
getter = last
setter l el = l ++ el
They both get an error akin to this one:
Could not deduce (a' ~ [a'])
from the context (Functor f)
bound by the type signature for
_last :: Functor f => LensLike f [a] [a'] a a'
at editor.hs:22:10-27
‘a'’ is a rigid type variable bound by
the type signature for
_last :: Functor f => LensLike f [a] [a'] a a'
at editor.hs:22:10
Expected type: f [a']
Actual type: f a'
Relevant bindings include
f :: a -> f a' (bound at editor.hs:23:7)
_last :: LensLike f [a] [a'] a a' (bound at editor.hs:23:1)
In the second argument of ‘fmap’, namely ‘(f (last l))’
In the expression: (\ el -> (init l) ++ el) `fmap` (f (last l))
How can I define the _last
lens?
Edit: Here's a version that builds:
_last f l = (\el -> (init l) ++ [el]) `fmap`(f (last l))
Although, as David/Daniel points out, _last
should be a Traversal, not a lens.
You are missing square brackets around el
((++)
takes a list for both arguments). Your first try should work if you put them in. Your type is also too general. Lists aren't heterogenous in Haskell, so they can only contain values of one type.
Also, as Daniel Wagner says, this can't be a true lens because it is partial. The lens documentation you linked to is out of date. The current lens library has _last
as a Traversal
which avoids this problem because a Traversal
can have 0 or more targets, as opposed to a lens which must have exactly 1.