我正在从事一些更大的计算,需要在某些关键时刻使用可变数据。我想尽可能避免IO。
我的模型曾经由以下组成ExceptT
over ReaderT
over State
数据类型,现在我想替换State
与提到的ST
.
为了简单起见,我们假设我想保持单身STRef
与Int
在整个计算过程中,让我们跳过ExceptT
外层。我最初的想法是把STRef s Int
into ReaderT
的环境:
{-#LANGUAGE Rank2Types#-}
{-#LANGUAGE ExistentialQuantification#-}
data Env = Env { supply :: forall s. STRef s Int }
data Comp a = forall s. Comp (ReaderT Env (ST s) a)
以及评估者:
runComp (Comp c) = runST $ do
s <- newSTRef 0
runReaderT c (Env {supply = s}) -- this is of type `ST s a`
...它失败了,因为
无法将类型“s”与“s1”匹配
这似乎很清楚,因为我混合了两个单独的幻影 ST 状态。但是,我不知道如何绕过它。我尝试过添加幻像s
as Comp
and Env
参数,但结果是相同的,并且代码变得更丑陋(但由于缺少这些而不太可疑)forall
s).
我在这里想要实现的功能是supply
可以随时访问,但没有显式传递(它不值得)。存储它的最舒适的地方是在环境中,但我看不到初始化它的方法。
我知道有这样的事情STT
monad 转换器这可能会有所帮助,但它与哈希表等更雄心勃勃的数据结构不兼容(或者是吗?),所以只要我不能自由使用经典,我就不想使用它ST
那里的图书馆。
如何正确设计这个模型?我所说的“正确”不仅指“类型检查”,还指“对代码的其余部分友好”和“尽可能灵活”。
runST
必须给出一个多态参数,并且您希望您的参数来自Comp
. Ergo Comp
必须包含多态的东西。
newtype Env s = Env { supply :: STRef s Int }
newtype Comp a = Comp (forall s. ReaderT (Env s) (ST s) a)
runComp (Comp c) = runST $ do
s <- newSTRef 0
runReaderT c (Env s)
Because Comp
结束s
,您不能执行返回所包含内容的操作STRef
;但您可以公开一个在内部使用引用的操作:
onRef :: (forall s. STRef s Int -> ST s a) -> Comp a
onRef f = Comp $ asks supply >>= lift . f
e.g. onRef readSTRef :: Comp Int
and onRef (`modifySTRef` succ) :: Comp ()
。另一种可能更符合人体工程学的选择是Comp
本身是单态的,但有runComp
要求多态动作。所以:
newtype Comp s a = Comp (ReaderT (Env s) (ST s) a)
runComp :: (forall s. Comp s a) -> a
runComp act = runST $ case act of
Comp c -> do
s <- newSTRef 0
runReaderT c (Env s)
然后你可以写
getSup :: Comp s (STRef s Int)
getSup = Comp (asks supply)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)