How to implement unsafePartsOf using lens

Viewed 51

I'm trying to implement a simple version of unsafePartsOf with the specialized signature:

unsafePartsOf :: Traversal s t a b -> Lens s t [a] [b]

After trying to understand how it works, I come up with this (repl).

myUnsafePartsOf :: Traversal s t a b -> Lens s t [a] [b]
myUnsafePartsOf traverse = lens getter setter
  where
    -- getter :: s -> [a]
    getter s = s ^.. traverse
    -- setter :: s -> [b] -> t
    setter s xs = evalState (s & traverse %%~ assignElem) xs
    assignElem _a = state $ \case
        [] -> error "list not long enough"
        (b : bs) -> (b, bs)

I think that using lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b to separate the definition getter and setter will make them easier to understand.

However, this function doesn't type check because ^.. operator (toListOf) has a (specialized) signature of toListOf :: Traversal' s a -> s -> [a] where the original s and t (as well as a and b) would get unified.

I then tried to implement a more general version of toListOf (the same repl) with the following code:

toListOf :: Traversal s t a b -> s -> [a]
toListOf traverse s = execState (traverse getElem s) []
  where getElem a = modify (a:)

but failed again because that would require to unify b with () due to the usage of modify. I understand that there is no way for me to create an arbitrary b in the getElem so this function is impossible.

Note that it's actually possible to define the safe partsOf using lens in a similar way:

myPartsOf :: Traversal' s a -> Lens' s [a]
myPartsOf t = lens getter setter
  where
    getter s = s ^.. t
    -- setter :: s -> [a] -> s
    setter s xs = evalState (s & t %%~ assignElem) xs
    assignElem a = state $ \case
        [] -> (a, [])
        (x : xs) -> (x, xs)

Is it possible to define unsafePartsOf using lens? If so how can I do it?

1 Answers

You can define the getter as:

getter s = s ^.. (\f -> Const . getConst . traverse (Const . getConst . f))

and it appears to typecheck.

Related