【问题标题】:What are good wrappers to transfer state change in haskell?在 haskell 中传输状态更改的好的包装器是什么?
【发布时间】:2014-05-07 01:27:39
【问题描述】:

为了自己的兴趣,我正在尝试实现一个简单的 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) - 或者,unziped,列表对 (arrows, xs)

    • 如果我们将map head 转换为zipWith ($) 'em 的结果,我们将失去携带箭头突变。
    • 如果我们让head arrows 监听xs,我们将冻结第一个箭头的状态。
  • 显式状态方式:

    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:如果需要时间,他可以用输入压缩它。

【问题讨论】:

  • 对于第二种类型newtype SF a b = SF { run :: a -> (b, SF a b) },您可能会注意到Monad 实例总是会在每个事件的内部和外部容器中引发a 事件。因此,内容器的状态将无关紧要。
  • 您可能还对 J. Abrahamson 关于在 FRP 中实现 join 的问题感兴趣:stackoverflow.com/questions/20890439/behavior-now-in-frp/…
  • 我的印象是,当您根本无法创建一个明智的ArrowApply/Monad 实例时,箭头会特别亮。

标签: haskell frp stateful purely-functional


【解决方案1】:

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

本身

我能想到的唯一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)
    }

我们可以在别处寻找一些不同的东西。

函数单子实例

您的newtype SF e a = SF { run :: e -&gt; (a, SF e a) } 非常接近从ea 的函数。对于Monad instance for functions,唯一明智的&gt;&gt;= 是将参数传递给内部和外部函数。这是我们已经提出的。让我们看看能不能想出别的办法。

状态T

您的代码有点类似于应用于Monad instance for functionsStateT monad transformer。不幸的是,这并没有产生我们正在寻找的东西。

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

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

应用于接受参数`e的函数的类型((-&gt;) e)

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

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

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)

结合(-&gt;) e的实例

instance Monad ((->) e) where
    return = const
    (>>=) x y z = y (x z) z

我们会得到以下结果,do 将工作转储到 ((->) e) 的实例上

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 的内容会怎样?我们希望能够传入(-&gt;) e 类型并获得像您这样的结构。我们将创建一个名为SFT m a 的东西,使SFT ((-&gt; e) aSF 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 应用于(-&gt;) e) 的类型替换为应用于SFe

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

这只有一个构造函数

SF  { run   :: e -> (a, SF        e  a) }
SFT { unSFT :: e -> (a, SFT ((->) e) a) }

这并没有提供新的见解,我能想到的唯一 Monad 实例几乎与原始实例相同。

instance Monad m => Monad (SFT m) where
    return a = SFT . return $ (a, return a)
    (>>=) sa f = SFT {
        unSFT =
            do
                (a, sa') <- unSFT sa
                (b, _) <- unSFT (f a)
                return (b, sa' >>= f)
    }

【讨论】:

  • 问题更可能源于无法解构(和重构)a -&gt; b 类型的对象。谢谢你的回答。
猜你喜欢
  • 2018-02-19
  • 2020-01-27
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-04
  • 1970-01-01
  • 2023-03-18
相关资源
最近更新 更多