修改 ReaderT 中的 ST 依赖环境 - `local` 函数的问题
Modify ST dependent environment in ReaderT – problem with `local` function
此问题是此线程的 sequel:
我问的是在 ReaderT
的环境中携带 STRef
并在其下执行 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
时,我开始挣扎。我需要的功能是 运行ning Comp
操作更新 namespace
不会影响进一步计算的命名空间——正是 local
所做的。
首先我想为 Comp
编写 MonadReader
实例,但是我遇到了 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
不变作为参考,保留所有更改。
怎么做?如果需要,我可以接受修改我的初始设置。
ST
的范围参数 s
应保留在外部:
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
...
此问题是此线程的 sequel:
我问的是在 ReaderT
的环境中携带 STRef
并在其下执行 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
时,我开始挣扎。我需要的功能是 运行ning Comp
操作更新 namespace
不会影响进一步计算的命名空间——正是 local
所做的。
首先我想为 Comp
编写 MonadReader
实例,但是我遇到了 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
不变作为参考,保留所有更改。
怎么做?如果需要,我可以接受修改我的初始设置。
ST
的范围参数 s
应保留在外部:
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
...