放大Free Monad上的实例

时间:2017-03-03 22:35:10

标签: haskell lens state-monad free-monad

我正在尝试构建一个免费的monad(使用free),它就像StateT monad一样,但是它允许你在基本状态AppState上运行monad。我有一个单独的构造函数LiftAction,它包含这些类型。我们的想法是保持zoom向下操作,直到它们到达AppState,AppState可以在其扩展映射中存储不同的状态。

这是我之前(失败)尝试使用mtl:Lift through nested state transformers (mtl)

无论如何,因为它基本上是StateT的包装器,我给它一个MonadState实例,但现在我正在努力添加使用{{3}缩放monad状态的功能。 };我得到一些奇怪的编译器错误我无法理解(镜头错误通常不是非常用户友好)。

这是我的代码和初步尝试:

{-# language GeneralizedNewtypeDeriving #-}
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language RankNTypes #-}
{-# language TypeFamilies #-}
{-# language UndecidableInstances #-}
module Eve.Internal.AppF
  ( Action(..)
  , App
  , AppState(..)
  , liftAction
  , execApp
  ) where

import Control.Monad.State
import Control.Monad.Free
import Control.Lens

type App a = Action AppState a
data AppState = AppState
  { baseExts :: Int -- Assume this actually contains many nested states which we can zoom
  }

data ActionF s next =
    LiftAction (Action AppState next)
    | LiftIO (IO next)
    | StateAction (StateT s IO next)
    deriving Functor

newtype Action s a = Action
  { getAction :: Free (ActionF s) a
  } deriving (Functor, Applicative, Monad)

liftActionF :: ActionF s next -> Action s next
liftActionF = Action . liftF

instance MonadState s (Action s) where
  state = liftActionF . StateAction . state

liftAction :: Action AppState a -> Action s a
liftAction = liftActionF . LiftAction

execApp :: Action AppState a -> StateT AppState IO a
execApp (Action actionF) = foldFree toState actionF
  where
    toState (LiftAction act) = execApp act
    toState (LiftIO io) = liftIO io
    toState (StateAction st) = st

type instance Zoomed (Action s) = Zoomed (StateT s IO)
instance Zoom (Action s) (Action t) s t where
  zoom l (Action actionF) = Action $ hoistFree (zoomActionF l) actionF
    where
      zoomActionF _ (LiftAction act) = LiftAction act
      zoomActionF _ (LiftIO io) = LiftIO io
      zoomActionF lns (StateAction act) = StateAction $ zoom lns act

我收到了错误:

/Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:65: error:
    • Couldn't match type ‘a’ with ‘c’
      ‘a’ is a rigid type variable bound by
        a type expected by the context:
          forall a. ActionF s a -> ActionF t a
        at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:42
      ‘c’ is a rigid type variable bound by
        the type signature for:
          zoom :: forall c.
                  LensLike' (Zoomed (Action s) c) t s -> Action s c -> Action t c
        at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:7
      Expected type: LensLike'
                       (Control.Lens.Internal.Zoom.Focusing IO a) t s
        Actual type: LensLike' (Zoomed (Action s) c) t s
    • In the first argument of ‘zoomActionF’, namely ‘l’
      In the first argument of ‘hoistFree’, namely ‘(zoomActionF l)’
      In the second argument of ‘($)’, namely
        ‘hoistFree (zoomActionF l) actionF’
    • Relevant bindings include
        actionF :: Free (ActionF s) c
          (bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:22)
        l :: LensLike' (Zoomed (Action s) c) t s
          (bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:12)
        zoom :: LensLike' (Zoomed (Action s) c) t s
                -> Action s c -> Action t c
          (bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:7)

据我所知,由于StateT嵌入在Free构造函数中,并且它失去了a的类型,因此我感到很困惑。

我以前有一个工作版本,通过定义我自己的缩放功能,在给定“镜头”的情况下缩放基础StateT,但诀窍是我希望这也适用于Traversal' s,所以最干净方法是写缩放实例。

任何人都知道如何编译它?提前致谢!!如果可能,请在发布之前尝试编译您的答案,谢谢!

1 个答案:

答案 0 :(得分:1)

虽然我无法获得前面的编译,但我提出了一个可接受的解决方案,使用FreeT作为State monad的包装器,它只是将提升值的缩放推迟到以后,不幸的是我需要手动实现{结果是{1}}和MonadTrans,这一点并不容易理解。除了Gabriel Gonzalez的(略微过时的)指南外,解释FreeT还有点棘手,没有太多好的教程。

这是我最终的结果

MonadFree