本文介绍了计算期间在环境中隐式携带STRef的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在做一些更大的计算,需要在某些关键时刻使用可变数据.我想尽可能地避免IO.我的模型以前是在State数据类型上在ReaderT上构造了ExceptT,现在我想用提到的ST代替State.

I am working on some bigger computation that requires use of mutable data in some critical moments. I want to avoid IO as much as I can.My model used to constist of ExceptT over ReaderT over State datatype, and now I want to replace State with mentioned ST.

为简化起见,假设我想在整个计算过程中将单个STRefInt保持在一起,并跳过ExceptT外层.我最初的想法是将STRef s Int放入ReaderT的环境中:

To simplify, let's assume I would like to keep single STRef with an Int during the whole computation, and let's skip the ExceptT outer layer. My initial idea was to put STRef s Int into ReaderT's environment:

{-#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`

...它失败了,因为

...and it fails because

这似乎很清楚,因为我混合了两个单独的幻影ST状态.但是,我不知道如何绕过它.我曾尝试将幻像s添加为CompEnv参数,但是结果是相同的,并且代码变得更丑陋(但由于缺少这些forall,所以可疑程度较低).

Which seems to be clear, because I have mixed two separate phantom ST states. However, I have no idea how to bypass it. I have tried adding phantom s as Comp and Env parameter, but the result was the same and the code got uglier (but less suspicious because of lack of these foralls).

我要在此处实现的功能是使supply随时可访问,但未显式传递(它不值得).存放它最舒适的地方是在环境中,但我看不到对其进行初始化的方法.

The feature I am trying to achieve here is to make supply accessible at any time, but not passed explicitly (it does not deserve it). The most comfortable place to store it is in the environment, but I see no way in initializing it.

我知道有一个像STT monad转换器这样的东西可能在这里有帮助,但是它与更雄心勃勃的数据结构(例如哈希表)不兼容(或者是?),所以我不想将其用作只要我不能在那里自由使用经典的ST库.

I know there is such a thing like STT monad transformer which may help here, but it is not compatible with more ambitious data structures like hashtables (or is it?), so I don't want to use it as long as I cannot freely use classic ST libraries there.

如何正确设计此模型? 适当"的意思不仅是进行类型检查",而且是对其余代码友好"和尽可能灵活".

How to properly design this model? By "properly" I mean not only "to typecheck" but to "to be nice to the rest of the code" and "as flexible as possible".

推荐答案

runST提供一个多态参数,并且您希望您的参数来自Comp. Ergo Comp必须包含多态的东西.

runST must be given a polymorphic argument, and you want your argument to come from Comp. Ergo Comp must contain a polymorphic thing.

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)

由于Comp关闭s,因此您无法执行返回所包含的STRef的操作;但是您可以公开内部使用引用的操作:

Because Comp closes over s, you can't make an action which returns the contained STRef; but you can expose an action which uses the reference internally:

onRef :: (forall s. STRef s Int -> ST s a) -> Comp a
onRef f = Comp $ asks supply >>= lift . f

例如onRef readSTRef :: Comp IntonRef (`modifySTRef` succ) :: Comp ().可能更符合人体工程学的另一种选择是使Comp本身是单态的,但使runComp要求具有多态性.所以:

e.g. onRef readSTRef :: Comp Int and onRef (`modifySTRef` succ) :: Comp (). Another choice that might be more ergonomic is to make Comp itself monomorphic, but have runComp demand a polymorphic action. So:

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)

这篇关于计算期间在环境中隐式携带STRef的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

11-01 19:51