[Haskell-cafe] Conditional lens

Artyom Kazak yom
Thu Oct 10 21:00:44 UTC 2013


I am working with TypeReps, and while writing some functions I have
noticed that I could use lenses to simplify them; however, I have stumbled
upon some difficulties.

First I?ll try to clarify which functions I want to write:

     * a function for converting TypeRep of, say, `Maybe x` to `[x]`
       (for all x). It requires checking if the TyCon is `Maybe` and
       replacing it with []-TyCon. If it wasn?t `Maybe`, I return Nothing.

     * a similar function for replacing `Char`s and `Lazy.Text`s to just
       `Text`. Again, if the TypeRep-to-be-replaced doesn?t satisfy my
       conditions, I return Nothing.

These two functions (and some others, I suppose) can be written concisely
with the help of one combinator. I don?t know how to write it as
a composable Lens, so I?ll give it here as an ad-hoc Lens-modifying
function instead:

     ifL :: (a -> Bool) -> Lens s t a b -> Lens s (Maybe t) (Maybe a) b
     ifL p l = lens getter setter
         get s = getConst $ l Const s
         getter s   = let a = get s
                      in  if p a then Just a else Nothing
         setter s b = let a = get s
                      in  if p a then Just (set l b s) else Nothing

It works like this:

     > (0, 2) & ifL even fs .~ "hello"
     Just ("hello",2)

     > (1, 2) & ifL even fs .~ "hello"

With `ifL`, my initial ugly

     changeTyCon :: TyCon -> TyCon -> TypeRep -> Maybe TypeRep
     changeTyCon tc tc' t | t^.tyCon == tc = Just $ t & tyCon .~ tc'
                          | otherwise      = Nothing

boils down to

     changeTyCon tc tc' = ifL (== tc) tyCon .~ tc'

Why did I call the initial version ?ugly?? Well, because

     a) it manually handles `Maybe`s, and
     b) it has to perform both getting and setting (two passes).

So, my questions are:

     1. What would be the idiomatic way to write `ifL`?

     2. How can I do something like `t ^. ifL (== tc) tyCon`?
        Currently it doesn?t work because view?s type has been
        simplified in lens-3.9.

     3. Perhaps it would be better to represent `ifL` as a Traversal
        which simply ignores values that don?t match the condition?
        Then I could (?) use `failover` to do what I want. I searched
        for something filter-like in lens library, but haven?t found

     4. If I haven?t missed anything and it indeed can?t be done with bare
        lens, would `ifL` or something similar be welcome as an addition
        to the library?


More information about the Haskell-Cafe mailing list