stepping into zipper with `to` lens
Asked Answered
P

1

6

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)
Pompidou answered 8/1, 2015 at 14:47 Comment(0)
A
2

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]
Ascension answered 8/1, 2015 at 15:34 Comment(5)
Is there any way to do it without specyfing (\(A a) -> a) function? Can I just pass here undefined if in my case it is not that obvious?Pompidou
Not really. The problem is you won't be able to go back up. Using upward to get back out would give an undefined.Ascension
I see... Are there any other options than Iso?Pompidou
Traversal is the 'weakest' you can use but you'll still need (\(A a) -> a) to make a valid Traversal. I don't advice writing an invalid one like I did in my edit since it could lead to some nasty bugs.Ascension
okay, I'll try to workaround unA somehow. Thanks for other options and explaination!Pompidou

© 2022 - 2024 — McMap. All rights reserved.