首页 > 解决方案 > 修改 ReaderT 中的 ST 依赖环境 – `local` 函数的问题

问题描述

这个问题是这个线程的续集:https ://stackoverflow.com/a/54317095/4400060

我在那里询问有关携带STRefReaderT环境并在其下执行 ST-actions 的问题。我的设置现在看起来像:

import Data.HashTable.ST.Cuckoo as HT

-- |Environment for Comp
newtype Env s = Env { dataspace :: HashTable s Int Data
                    , namespace :: Map Name Int }

-- |Main computation monad
newtype Comp a = Comp (forall s. ReaderT (Env s) (ST s) a)


-- |Evaluate computation
runComp (Comp c) = runST $ do
    ds <- HT.new
    runReaderT c (Env ds empty)


-- |Perform an action on `dataspace` hashmap
onDataspace :: (forall s. HashTable s Int Data -> ST s a) -> Comp a
onDataspace f = Comp $ asks dataspace >>= lift . f

总的来说它很酷——我可以dataspace自由地访问或修改。但是,当我添加不可变时,namespace我开始挣扎。我需要的功能是以不会影响进一步计算的命名空间的方式运行Comp更新的操作namespace——正是这样local做的。

首先,我想为 编写MonadReader实例Comp,但是我遇到了ST的幻像类型并得到了illegal instance错误:

instance MonadReader (Env s) Comp where {}
instance MonadReader (forall s. Env s) Comp where {}
instance forall s. MonadReader (Env s) Comp where {}

完整的错误信息:

Illegal instance declaration for
     ‘MonadReader (EvalEnv s) Evaluator’
     The coverage condition fails in class ‘MonadReader’
       for functional dependency: ‘m -> r’
     Reason: lhs type ‘Evaluator’
       does not determine rhs type ‘EvalEnv s’
     Un-determined variable: s

我理解这个错误,但我认为没有办法绕过它。老实说,我并不需要完整local的功能。我只需要能够以Comp不同namespace但相同的方式运行dataspace

最好的解决方案是提供完整的MonadReader实例。我知道这可能是不可能的,所以作为一种解决方法,我想要一个函数

withNs :: Map Name Int -> Comp a -> Comp a

总结:我希望能够在Comp修改后运行,namespace同时保持dataspace不变作为参考,保留所有更改。

怎么做?如果需要,我可以接受修改我的初始设置。

标签: haskellmonadsmonad-transformersreader-monadst-monad

解决方案


的范围参数sST保持在外部:

newtype Comp s a = Comp (ReaderT (Env s) (ST s) a)

唯一需要更高级别类型的地方是调用runST.

runComp :: (forall s. Comp s a) -> a
runComp = runST $ do
  ds <- HT.new
  runReaderT c (Env ds empty)

在其他任何地方,您都可以简单地在s.

doStuff :: Comp s Bool
doMoreStuff :: Comp s Int

那么MonadReader实例可以写成:

instance MonadReader (Env s) (Comp s) where
  ...

推荐阅读