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?

Haskell-Cafe mailing list

Reply via email to