这个问题是该线程的续集: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
不变作为参考,以保留所有更改。
该怎么做?如果需要,我可以接受修改我的初始设置。
答案 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
...