haskell - 计算期间在环境中隐式携带 STRef
问题描述
我正在做一些更大的计算,需要在某些关键时刻使用可变数据。我想尽可能地避免 IO。我的模型曾经由ExceptT
over ReaderT
over State
datatype 组成,现在我想State
用提到ST
的 .
为简化起见,假设我想在整个计算过程中保持单一,让我们跳过STRef
外层。我最初的想法是放入' s 环境中:Int
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`
...它失败了,因为
无法将类型“s”与“s1”匹配</p>
这似乎很清楚,因为我混合了两个独立的幻象 ST 状态。但是,我不知道如何绕过它。我尝试添加 phantom s
asComp
和Env
参数,但结果是相同的,并且代码变得更丑陋(但由于缺少这些forall
s 而不太可疑)。
我在这里试图实现的功能是随时可以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 Int
和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)
推荐阅读
- git - 如何在已经是最新的情况下拉取提交
- netsuite - 在 NetSuite 中,如何使用保存的搜索访问已删除的交易行。请注意我不需要删除的交易记录
- excel - 从 VBA 中的 Excel 验证列表中读取和选择
- sql - SQL Server:在存储过程中的变量中存储多个值
- javascript - react-scroll 不应用滚动视图和链接
- c# - 更改数据库表后更新模型类
- python-2.7 - PyScripter 2.5.3 间歇性中断键盘
- python - matplotlib:自定义月份名称(用于在语言环境不起作用时进行翻译)
- typescript - 在 const 字符串声明之后使用 const 断言有什么作用?
- reactjs - Electron 上的 WebPack 问题