在ReaderT中修改ST依赖的环境-问题“本地”功能

时间:2019-01-26 11:41:36

标签: haskell monads monad-transformers reader-monad st-monad

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

我当时在问有关在STRef的环境中携带ReaderT并在其下执行ST动作的问题。现在,我的设置如下:

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不变作为参考,以保留所有更改。

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

1 个答案:

答案 0 :(得分:2)

s的范围参数ST应该保留在外部:

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
  ...