什么是在haskell中转移状态变化的好包装器?

时间:2014-03-27 20:20:09

标签: haskell frp stateful purely-functional

我正在尝试实现一个简单的FRP后端,这符合我自己的兴趣。

我决定使用纯函数:所以,核心没有IO。实施基于信号变压器。

我已经尝试了两种方法:

newtype SF a b = SF { listen :: [a] -> [b] }

https://gist.github.com/Heimdell/9675964#file-streamer-hs-L1

newtype SF a b = SF { run :: a -> (b, SF a b) }

https://gist.github.com/Heimdell/9675964#file-behaviour-hs-L1(错误,抱歉)

这两种方式都可以使fold/integrate :: (a -> b -> b) -> b -> SF a b组合器用于信号集成。

两种方式都有问题:似乎无法制作有效的ArrowApply / Monad实例。

  • Stream-way:我们有一对(arrow, x) - 或unzip ed对列表(arrows, xs)

    • 如果我们map head zipWith ($)'的结果,我们将松开随身携带的箭头变异。
    • 如果我们head arrowsxs,我们会冻结第一个箭头的状态。
  • 显式状态:

    instance ArrowApply Behaviour where
        app =
            Behaviour $ \(bf, a) ->
                let (bf1, c) = bf `runBehaviour` a
    
                in (app, c)
    

    这里我们需要以某种方式有效地将bf1注入到返回的app中,这是不可能的(实际注入(const bf1 *** id)会产生与其他实现中的第二个类似的无效行为。

是否有可能制作允许ArrowApply实例的SF

P.S。:当一个分支长时间不使用时,流路在ArrowChoice中有内存泄漏。现在,我无法解决这个问题。是否有可能制作无泄漏版本?

P.P.S:如果需要时间,他可以输入拉链。

1 个答案:

答案 0 :(得分:2)

我找不到任何可能不会简单地丢弃内部容器状态的实例。这并不奇怪,因为绑定到数据的纯函数的返回应该在每次调用输入时返回相同的内容,无论之前是否已经调用过,您在评论中提示它。

本身

我可以提出的唯一Monad个实例都放弃了内部容器的后续状态。

instance Monad (SF e) where
    return a = SF . const $ (a, return a)
    (>>=) sa f = SF go
        where
            go e = 
                let
                    (a, sa') = run sa e
                    sb = f a
                    (b, _) = run sb e
                in
                    (b, sa' >>= f)

join :: SF e (SF e a) -> SF e a
join ssa = SF go
    where
        go e =
            let
                (sa, ssa') = run ssa e
                (a, _) = run sa e
            in
                (a, join ssa')

使用Monad instance for functions

可以更简洁地表达这些内容
instance Monad (SF e) where
    return a = SF . const $ (a, return a)
    (>>=) sa f = SF {
        run =
            do
                (a, sa') <- run sa
                (b, _) <- run (f a)
                return (b, sa' >>= f)
    }

我们可以在其他地方寻找一些不同的东西。

功能Monad实例

您的newtype SF e a = SF { run :: e -> (a, SF e a) }非常接近ea的功能。对于Monad instance for functions,唯一明智的>>=是将参数传递给内部和外部函数。这就是我们已经提出的。让我们看看我们是否可以拿出别的东西。

<强> StateT

您的代码与应用于StateT monad transformerMonad instance for functions有些相似。不幸的是,这并没有产生我们正在寻找的东西。

考虑以下(StateT monad变换器):

newtype StateT s m a = StateT { runStateT :: s -> m (a, s)}

应用于带参数`e。

的函数的类型((->) e)

StateT s ((->) e) a有一个构造函数StateT { runStateT :: s -> e -> (a, s) }

这与您的类型的不同之处在于必须提供初始状态,并且显式跟踪状态,而不是已经包含在返回的下一个值中。让我们看看这个Monad实例是什么。 StateT的{​​{1}}实例是

Monad

结合instance (Monad m) => Monad (StateT s m) where return a = state $ \s -> (a, s) m >>= k = StateT $ \s -> do ~(a, s') <- runStateT m s runStateT (k a) s' state f = StateT (return . f)

的实例
(->) e

我们得到以下内容,其中instance Monad ((->) e) where return = const (>>=) x y z = y (x z) z 将工作转储到(( - &gt;)e)的实例上

do

这看起来很不一样。我们不会失去任何国家的历史。这里发生的是内部容器的状态从外部容器传递给它,并且两个容器必须具有相同的类型才能使其工作。这根本不是我们想要的。

新功能

如果我们尝试从您的类型中创建类似instance Monad (StateT s ((->) e) where return a = StateT (const . (\s -> (a, s))) m >>= k = StateT $ \s e -> let (a, s`) = runStateT m s e in runStateT (k a) s` e 的内容,会发生什么?我们希望能够传递StateT类型并获得与您类似的结构。我们会制作一个名为(->) e的内容,以使SFT m a具有与SFT ((-> e) a相同的结构。

SF e a

我们可以通过将newtype SF e a = SF { run :: e -> (a, SF e a) newtype SFT m a = SFT { unSFT :: m (a, SFT m a) } 应用于SFT(->) e)应用于SF

来替换所做的类型
e

这有一个构造函数

SF        e  a -- is replaced by
SFT ((->) e) a

这没有提供新的见解,我能想到的唯一SF { run :: e -> (a, SF e a) } SFT { unSFT :: e -> (a, SFT ((->) e) a) } 实例几乎与原始实例相同。

Monad