4

为了我自己的利益,我正在尝试实现一个简单的 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)

    • 如果我们愿意得到 'emmap 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)会产生类似于其他实现的第二个无效行为。

有没有办法制作一个SF允许 ArrowApply 实例的方法?

PS:当分支长时间未使用时,流路在 ArrowChoice 中存在内存泄漏。目前,我无法解决这个问题。是否有可能制作它的无泄漏版本?

PPS:如果需要时间,他可以用输入压缩它。

4

1 回答 1

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 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 -> (a, SF e a) }非常接近从 to 的e功能a。对于函数的 Monad 实例,唯一明智>>=的做法是将参数同时传递给内部函数和外部函数。这是我们已经提出的。让我们看看能不能想出别的办法。

状态T

您的代码有点类似于应用于函数的 Monad 实例的StateT monad 转换器。不幸的是,这并没有产生我们正在寻找的东西。

考虑以下(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实例是什么。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)

结合一个实例(->) 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您的类型制作类似的东西会发生什么?我们希望能够传入类型(->) e并获得像您这样的结构。我们将创建一个名为SFT m asuch 的东西,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) }

我们可以用 apply SFTto替换(->) e)SFapplied toe

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)
    }
于 2014-03-28T00:49:09.077 回答