polysemy-research / polysemy

:gemini: higher-order, no-boilerplate monads
BSD 3-Clause "New" or "Revised" License
1.04k stars 72 forks source link

enable lens support for Polysemy State effect #347

Closed goertzenator closed 2 years ago

goertzenator commented 4 years ago

The lens library cannot be used with the Polysemy State effect. Can this be enabled in some way?

A few ideas can be found in this reddit thread.

KingoftheHomeless commented 4 years ago

Yeah, it's in fact trivial.

stateToStateViaLens :: Member (State bigSt) r
                    => Lens' bigSt smallSt
                    -> Sem (State smallSt ': r) a
                    -> Sem r a
stateToStateViaLens lens = interpret $ \case
  Put smallSt -> modify' (set lens smallSt)
  Get -> gets (view lens)

I think this would be a good addition. I do not want to depend on lens, but that's what microlens is for. I'd welcome a pull request.

If you want combinators similar to the MonadState ones in lens, I'm significantly less sure about that. You'd have to convince me it's worth it.

Edit: Or we could make it a separate library à la fused-effects.

goertzenator commented 4 years ago

Thanks, but I'm struggling to understand how to apply that. Below is my [incorrect] guess, and the last function lensplay2 illustrates what I seek to achieve.

https://gist.github.com/goertzenator/2c055e96679c373a47cb894580725317

berberman commented 4 years ago
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -fplugin=Polysemy.Plugin #-}

module Test where

import Data.Functor.Const
import Lens.Micro
import Polysemy
import Polysemy.State

view :: MemberWithError (State s) r => Getting a s a -> Sem r a
view l = getConst <$> gets (l Const)

(%=) :: MemberWithError (State s) r => ASetter s s a b -> (a -> b) -> Sem r ()
l %= f = modify (l %~ f)

(+=) :: (MemberWithError (State s) r, Num a) => ASetter s s a a -> a -> Sem r ()
l += x = l %= (+ x)

(*=) :: (MemberWithError (State s) r, Num a) => ASetter s s a a -> a -> Sem r ()
l *= x = l %= (* x)

test :: Member (State (Int, Int)) r => Sem r ()
test = do
  _1 += 232
  _2 *= 6

-- (233, 666)
runTest = run $ execState (1, 111) test

As a microlens user, I would prefer rewriting functions in microlens-mtl with polysemy types, so that lenses can be used directly. Maybe we can make a library called microlens-polysemy, though it seems to be trivial XD

TheMatten commented 4 years ago

Separate package sounds like a good idea - at least because we could then do the same thing e.g. for optics :slightly_smiling_face:

tek commented 4 years ago

I think it might be worth mentioning here that there's a library implementing something along these lines: https://hackage.haskell.org/package/polysemy-optics

isovector commented 2 years ago

Closing this; feel free to reopen if you feel it's still an issue.