解压状态monad是否有合理的方法?

LeM*_*Miz 6 random monads state haskell

我想要一个像这样的功能:

unzipState :: (MonadState s m) => m (a, b) -> (m a, m b)
Run Code Online (Sandbox Code Playgroud)

这将采用返回元组的(有状态)计算,并返回两个(从属)计算.

困难当然是从一个或另一个计算中提取值应更新另一个中的状态.

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

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

type Random a = forall r. (State RandomGen r) => State r a
Run Code Online (Sandbox Code Playgroud)

让我们说你有:

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)
Run Code Online (Sandbox Code Playgroud)

能写的很自然:

let x = normal
    y = normal
    (u, v) = unzipState $ correlateWith 0.5 $ liftM2 (,) x y
    ... now I am able to perform computation on u and v as correlated random variables
Run Code Online (Sandbox Code Playgroud)

有没有明智的方法来做到这一点?我有点挣扎,但没有成功.霍尔格也没有任何帮助.

编辑

很好的答案告诉我,我的问题是不明确的.尽管如此,有人可以解释一下为什么 python中的以下实现(我认为是正确的,但没有经过多少测试)无法在Haskell中进行翻译(使用STrefs的魔力,闭包和其他我不承认的东西; - )):

def unzipState(p):
    flist, glist = [], []
    def f(state):
        if not flist:
            (fvalue, gvalue), newstate = p(state)
            glist.insert(0, gvalue)
            return (fvalue, newstate)
        else:
            fvalue = flist.pop()
            return (fvalue, state)
    def g(state):
        if not glist:
            (fvalue, gvalue), newstate = p(state)
            flist.insert(0, fvalue)
            return (fvalue, newstate)
        else:
            gvalue = glist.pop()
            return (gvalue, state)
    return (f, g)
Run Code Online (Sandbox Code Playgroud)

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

EDIT2

现在很清楚.功能f和g显然不是纯粹的,因为它们的输出不仅取决于状态的值.

再次感谢 !

Hei*_*mus 5

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


虽然我认为我明白你想做什么,但Randommonad也明白为什么不能这样做.要看到这一点,你必须type Random a = ...放弃具体的实现,并考虑给出的抽象解释

v :: Random a意味着v是类型值的概率分布a

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

现在,这意味着unzipState简单地返回一对概率分布,可用于构造其他概率分布.关键是虽然do语法看起来非常具有启发性,但您实际上并未对随机变量进行采样,因此您只需计算概率分布.随机变量可以相关,但概率分布不能.


请注意,可以创建RandomVariable a与随机变量对应的不同monad .但是,您必须提前修复样本空间Ω.实施是

type RandomVariable a = ? -> a
Run Code Online (Sandbox Code Playgroud)



如果您想要随机变量和自动放大样本空间的能力,您可能需要两个绑定操作

bind1 :: Random ? a -> (a -> Random ? b) -> Random ? b
bind2 :: Random ?1 a -> (a -> Random ?2 b) -> Random (?1,?2) b
Run Code Online (Sandbox Code Playgroud)

和一些依赖型魔术来应对产品的扩散之类的(?1,(?2,?3)).