haskell - 计算期间在环境中隐式携带 STRef

标签 haskell design-patterns monads monad-transformers

我正在从事一些更大的计算,需要在某些关键时刻使用可变数据。我想尽可能避免IO。 我的模型过去由 ExceptT over ReaderT over State 数据类型组成,现在我想用提到的替换 State ST

为了简化,我们假设我想在整个计算过程中保留单个 STRefInt ,并且让我们跳过 ExceptT 外部层。我最初的想法是将 STRef s Int 放入 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`

...它失败了,因为

Couldn't match type ‘s’ with ‘s1’

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

我在这里试图实现的功能是使 supply 可以随时访问,但不显式传递(它不值得)。最舒服的存储位置是环境中,但我看不到初始化它的方法。

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

如何正确设计这个模型?我所说的“正确”不仅指“类型检查”,还指“对代码的其余部分友好”和“尽可能灵活”。

最佳答案

runST 必须给出一个多态参数,并且您希望参数来自 Comp。因此 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)

因为 Comp 会超过 s,因此您无法执行返回所包含的 STRef 的操作;但您可以公开一个在内部使用引用的操作:

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 要求多态操作。所以:

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)

关于haskell - 计算期间在环境中隐式携带 STRef,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54316935/

相关文章:

haskell - 具有类约束类型的值实际上会在运行时成为函数吗?

haskell - 是否可以在 OS X 上静态链接 Haskell 应用程序?

haskell - 请更正我对 Maybe Monad 的使用

f# - F#中单子(monad)定律的解释

parsing - 双递归函数中的 Haskell 类型错误

haskell - 当我的解析器显式查找时,为什么会得到 "unexpected end of input"?

scala - Spark/Scala、数据集和案例类的多态性

java - Java中工厂类的用途

java - 设计 super 英雄游戏

c# - Bind 和 Map 之外的 Option<T> monad 的标准操作