Haskell 有没有一种合理的方法来解压状态单子?

Haskell 有没有一种合理的方法来解压状态单子?,haskell,random,state,monads,Haskell,Random,State,Monads,我想要一个函数,比如: unzipState :: (MonadState s m) => m (a, b) -> (m a, m b) 它将进行一个(有状态)计算,返回一个元组,并返回两个(依赖)计算 当然,困难在于从一个或另一个计算中提取值会更新另一个计算中的状态 一个有用的(和激励性的)应用程序是Random monad,表示为 {-# LANGUAGE Rank2types #-} import qualified System.Random as SR import Co

我想要一个函数,比如:

unzipState :: (MonadState s m) => m (a, b) -> (m a, m b)
它将进行一个(有状态)计算,返回一个元组,并返回两个(依赖)计算

当然,困难在于从一个或另一个计算中提取值会更新另一个计算中的状态

一个有用的(和激励性的)应用程序是Random monad,表示为

{-# LANGUAGE Rank2types #-}
import qualified System.Random as SR
import Control.Monad.State

type Random a = forall r. (State RandomGen r) => State r a
假设你有:

normal :: Random Double
-- implementation skipped

correlateWith :: Double -> Random (Double, Double) -> Random (Double, Double)
correlateWith rho w = do
                        (u, v) <- w
                        return $ (u, p * u + (1 - p * p) * v)
有没有明智的办法?我挣扎了一下,但什么也没做。胡格尔也帮不了什么忙

编辑

伟大的答案告诉我我的问题是不明确的。尽管如此,有人能解释一下为什么以下python实现(我认为是正确的,但没有经过太多测试)不能用Haskell翻译(有STrefs、闭包和其他我承认不懂的东西的魔力;-):

我并不是说有状态代码可以用Haskell翻译,但我觉得理解为什么和什么时候(甚至在一个例子中)不能翻译会大大提高我的理解

edit2

现在一切都清楚了。函数f和g显然不是纯函数,因为它们的输出不仅取决于状态的值


再次感谢

我不完全清楚您希望它如何工作——因为
correlateWith
对元组进行操作,所以相关变量独立地有什么意义?假设你这样做:

let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
in do u1 <- u
      v1 <- v
      u2 <- u
      u3 <- u
      -- etc...
由于从
u
采样的值的数量根据
u1
而变化,这似乎表明绑定到
vs
的非一元值也会以某种方式追溯到
u1
上,这听起来像是哈斯克尔避免的那种距离上的鬼怪行为

我冒昧地猜测,您试图实现的功能不能简单地在一个简单的PRNG状态monad上进行改装,但可能还有其他选择。

不可能构造一个满足您需要的通用函数
unzipState
,如果只是因为您可能无法为其预期效果提供正式的规范。换句话说,假设您已经实现了一些函数
unzipState
。你怎么知道它是正确的?你必须证明它满足某些定律/方程,但这里的麻烦是首先要找到这些定律


虽然我想我理解你想做什么,但是
随机的
单子也清楚地说明了为什么不能做。要看到这一点,你必须放弃具体的实现<代码>类型随机A=…<代码>,并考虑由

给出的抽象解释。
v::随机a
表示
v
a
类型值的概率分布

“绑定”操作
(>>=)::Random a->(a->Random b)->Random b
只是从旧概率分布构造新概率分布的一种方法

现在,这意味着
unzipState
只返回一对概率分布,可用于构造其他概率分布。关键是,虽然
do
语法看起来很有启发性,但实际上你并没有对随机变量进行采样,你只是计算概率分布。随机变量可以关联,但概率分布不能


请注意,可以创建与随机变量相对应的不同monad
随机变量a
。但是,必须提前修复采样空间Ω。实施过程非常简单

type RandomVariable a = Ω -> a


如果希望同时使用随机变量和自动扩大样本空间的功能,则可能需要两个绑定操作

bind1 :: Random Ω a -> (a -> Random Ω b) -> Random Ω b
bind2 :: Random Ω1 a -> (a -> Random Ω2 b) -> Random (Ω1,Ω2) b

还有一些依赖类型的魔法来处理产品的扩散,比如
(Ω1,(Ω2,Ω3))

Haskell中有很多关于贝叶斯单子的相关内容。这里有一个参考:

另请参见本页提供的“纯功能性惰性非确定性编程”:

编辑:我也不明白为什么不能只给
correlateWith
以下类型签名,并直接在do块中运行它,而不是在let绑定中尝试“推送”随机状态。
correlateWith::Double->Random Double->Random Double->Random(Double,Double)

我猜语义将与流的语义完全相同:数据流a=a:u=uniform::Random A
v=uniform::Random A
,那么
u=v=uniform
是非常正确的。请记住,这里有两个不同的概念:从样本空间Ω导出的随机变量,以及以概率加权的
A
类型的值集合(=概率分布)。你的
随机a
单子对应的是后者,而不是前者。同样可以将其实现为
随机a=[(a,概率)]
[如果不对
随机a
单子和你追求的特定效果进行形式化,恐怕讨论这个问题有点棘手。]你是
let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
in do vs <- generateStream v
      u1 <- u
      if someCondition u1 then u else return u1
      -- etc...
type RandomVariable a = Ω -> a
bind1 :: Random Ω a -> (a -> Random Ω b) -> Random Ω b
bind2 :: Random Ω1 a -> (a -> Random Ω2 b) -> Random (Ω1,Ω2) b