I have two functions, one with a MonadState constraint of the wrapped type, the other has a MonadState constraint of the unwrapped type. I'd like to seamlessly run the second function within the first.
For example:
import qualified Control.Monad.State.Strict as MTL
import Data.Monoid (Sum)
import Control.Lens.Zoom (zoom)
import Control.Lens.Wrapped (_Wrapped')
outer :: (MTL.MonadState (Sum Int) m) => m Int
outer = zoom _Wrapped' inner
inner :: (MTL.MonadState Int m) => m Int
inner = MTL.get
The above seems to me like it should work, yet I get 3 type checker errors. The first:
Could not deduce (Control.Lens.Zoom.Zoom m0 m Int (Sum Int))
arising from a use of ‘zoom’
from the context (MTL.MonadState (Sum Int) m)
bound by the type signature for
outer :: MTL.MonadState (Sum Int) m => m Int
The type variable ‘m0’ is ambiguous
From my searching, I get the impression that zoom
can't do what I want. (found this quote on http://ircbrowse.net/browse/haskell "reltuk: yeah, thats the unfortunate downside of lenses is that zooming forces you to a concrete state monad") I guess that lines up with the error message stating that "m0 is ambiguous".
I'd really rather not change my MonadState constraint into a concrete monad.
Is there some other standard way of doing what I want?
This will not type check:
sumOuter :: (Functor (Zoomed m Int),
Zoom m m Int t,
Wrapped t,
Unwrapped t ~ Int,
MTL.MonadState (Sum Int) m) => m Int
sumOuter = zoom _Wrapped' sumInner
sumInner :: (MTL.MonadState Int m) => m Int
sumInner = MTL.get
zoom
has its own class for overloading, so no wonder just MonadState
doesn't cut it. The Zoom
class covers roughly the same ground as mtl
, although it has somewhat more complicated machinery. In any case, you're not obligated to program in concrete monads.
You can try enabling NoMonomorphismRestriction
and inferring a type:
{-# LANGUAGE NoMonomorphismRestriction #-}
import Control.Lens.Internal.Zoom
import Control.Lens
import Control.Monad.State
import Data.Monoid
import Control.Monad.Except -- for illustration
outer = zoom _Wrapped' get
Now :t outer
gives
outer ::
(Functor (Zoomed m (Unwrapped t)), Zoom m n (Unwrapped t) t, Wrapped t)
=> n (Unwrapped t)
This isn't pretty, but it seems to work.
> runState outer (Sum 10)
(10, Sum {getSum = 10})
> runState (runExceptT outer) (Sum 10) :: (Either String Int, Sum Int)
(Right 10,Sum {getSum = 10})
EDIT:
If you really want to specialize to Sum Int
as the outer state, and also want to have MonadState (Sum Int) n
constraint, this suffices:
sumOuter ::
(Functor (Zoomed m Int), Zoom m n Int (Sum Int)) => n Int
sumOuter = zoom _Wrapped' sumInner
sumInner :: (MTL.MonadState Int m) => m Int
sumInner = MTL.get
What about the MonadState
constraint? There's no need to write it out, because Zoom mnst
has MonadState sm
and MonadState tn
as superclasses.
Likewise, the more general outer
function already implies MonadState tn
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.