haskelltypeshaskell-lenszipper

stepping into zipper with `to` lens


I'm struggling with lens and zippers. Consider below code run in ghci

> import Control.Lens
> import Control.Zipper
> 
> :t within (ix 1) $ zipper ([1,2,3] :: [Int]) 
> within (ix 1) $ zipper ([1,2,3] :: [Int])
   :: Control.Monad.MonadPlus m => m (Zipper Top Int [Int] :>> Int)

Having data A t = A t, how can I create type of zipper like: Control.Monad.MonadPlus m => m (Zipper Top Int [Int] :>> A Int)?

I tried within (ix 1 . to A) $ zipper ([1,2,3] :: [Int]) but it gives an error:

Could not deduce (Contravariant
                    (Bazaar (Indexed Int) (A Int) (A Int)))
  arising from a use of ‘to’
from the context (Control.Monad.MonadPlus m)
  bound by the inferred type of
           it :: Control.Monad.MonadPlus m =>
                 m (Zipper Top Int [Int] :>> A Int)
  at Top level
In the second argument of ‘(.)’, namely ‘to A’
In the first argument of ‘within’, namely ‘(ix 1 . to A)’
In the expression: within (ix 1 . to A)

Solution

  • One way is to make an Iso and compose with that. In ghci:

    > import Control.Lens
    > import Control.Zipper
    >
    > data A t = A t
    > let _A = iso A (\(A a) -> a)
    >
    > let a = within (ix 1 . _A) $ zipper ([1,2,3] :: [Int])
    > :t a
    a :: MonadPlus m => m (Zipper Top Int [Int] :>> A Int)
    > a ^? _Just . focus
    Just (A 2)
    

    Edit: The reason you need (\(A a) -> a) is so you can get back out.

    > data A t = A t
    > let _A = iso A (error "Can't unA")
    >
    > let a = within (ix 1 . _A) $ zipper ([1,2,3] :: [Int])
    > a ^? _Just . focus
    Just (A 2)
    > fmap upward a ^? _Just . focus
    Just [1,*** Exception: Can't unA
    

    I don't think there's a valid way to make this without a function for extracting A. You could write an invalid Traversal but it still won't work properly:

    > data A t = A t
    > let _A f a = a <$ f (A a)
    >
    > let a = within (ix 1 . _A) $ zipper ([1,2,3] :: [Int])
    > let b = a & _Just . focus .~ A 10
    > b ^? _Just . focus
    Just (A 10)
    > fmap upward b ^? _Just . focus
    Just [1,2,3] -- Should be Just [1, 10, 3]