Есть ли разумный способ распаковать монаду состояния?

Я бы хотел иметь такую ​​функцию, как:

unzipState :: (MonadState s m) => m (a, b) -> (m a, m b)

, который потребует (с сохранением состояния) вычисления, возвращающего кортеж, и вернет два (зависимых) вычисления.

Трудность, конечно, в том, что извлечение значений из одного или другого вычисления должно обновлять состояние в другом.

Полезным (и мотивирующим) приложением является случайная монада, выраженная как

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

было бы вполне естественно написать:

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

Есть ли разумный способ сделать этот ? Я немного поборолся, но ничего не добился. Hoogle тоже не помог.

edit

Отличные ответы показали мне, что моя проблема плохо определена. Тем не менее, может ли кто-нибудь объяснить мне , почему следующая реализация на 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)

Не то чтобы я говорю, что код с отслеживанием состояния можно транслировать на Haskell, но мне хочется понять , почему и , когда (даже на пример) это невозможно сделать, это значительно улучшило бы мое понимание.

edit2

Теперь это кристально ясно. Очевидно, что функции f и g не являются чистыми, поскольку их результат зависит не только от значения состояния.

Еще раз спасибо!

6
задан LeMiz 10 January 2011 в 08:02
поделиться