Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capability focusing using lens? #105

Open
andrevidela opened this issue Mar 17, 2023 · 4 comments
Open

Capability focusing using lens? #105

andrevidela opened this issue Mar 17, 2023 · 4 comments

Comments

@andrevidela
Copy link

andrevidela commented Mar 17, 2023

Hi, apologies, this is neither a feature request nor a bug report. But a question that I did not know where else to ask.

Is there a way to derive a capability using a lens into a state? I understand that the Field/HasField and Pos/HasPos capabilities relate to their lens counterpats. But in my case I have a lens

view :: a -> b 
view = ...
update :: a -> b -> a
update = ...

and a capability HasState "stateName" b and I would like to turn it into a HasState "stateName" a using the lens described above. The ideal type signature would be something like
focus :: HasState t b m => Lens a b -> (forall m' . HasState t a m' => m' r) -> m r

Is there a way to achieve this?

Intuitively, I would assume zoom achieves that but I do not see what transformer to use or how to even define one.

@andrevidela
Copy link
Author

andrevidela commented Mar 17, 2023

I've managed to hack this in but I'm not quite sure if this is the way to go:

newtype WithLens m a = WithLens (m a)
  deriving (Functor, Applicative, Monad)

class HasLens super sub | super -> sub where
  view :: super -> sub
  update :: super -> sub -> super

subState :: HasLens super sub => (sub -> (a, sub)) -> (super -> (a, super))
subState f st = let (v, res) = f  (view st) in (v, update st res)

instance (Monoid superState, HasLens superState subState, HasSink tag superState m) => HasSink tag subState (WithLens m) where
    yield_ tag v = WithLens (yield_ tag (update mempty v ))

instance (Monoid superState, HasLens superState subState, HasSource tag superState m) => HasSource tag subState (WithLens m) where
    await_ tag = WithLens (view <$> await_ tag)

instance (Monoid superState, HasLens superState subState, HasState tag superState m) => HasState tag subState (WithLens m) where
    state_ tag f = WithLens (state_ tag (subState f))

focus
    :: forall super sub tag m r
     . Monoid super
    => HasState tag super m
    => HasLens super sub
    => (forall m'. HasState tag sub m' => m' r) -> m r
focus = zoom @tag @WithLens @'[] @sub

Most notably, it requires Monoid super in order to lift from the substate into the superstate in HasSink. This is because we need a map subState -> superState but there is no such thing. However, provided a neutral element, we can use update which will correctly update the neutral superstate with the data from the substate. That somewhat works but feels very hacky. I'm not quite sure if it makes sense to implement HasState without HasSink but if that was possible, it would clear up this problem.

There is also the problem of the functional dependency on super -> sub which limits the use of this API

@aspiwack
Copy link
Member

Hi @andrevidela ,

Have you looked at the Capability.Reflection module? It lets you define a dictionary manually for a capability. It does take a bit of wrapping your head around, but it would avoid having to create an ad hoc type class.

(if you do build a type class of the sort you did, I would advise adding a tag argument to the class, so that you can attach multiple lenses to a field).

@andrevidela
Copy link
Author

Thank you, for the pointer, I'll take a look!

@aherrmann
Copy link
Member

@andrevidela the capability repository contains some examples using the reflection module here. These might help to better understand the approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants