gpt4 book ai didi

haskell - 在 haskell 中传输状态变化的好的包装器有哪些?

转载 作者:行者123 更新时间:2023-12-02 13:51:44 27 4
gpt4 key购买 nike

为了我自己的兴趣,我正在尝试实现一个简单的 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 (名字错误,抱歉)

这两种方法都可以制作用于信号积分的折叠/积分::(a -> b -> b) -> b -> SF a b 组合器。

两种方法都有一个问题:似乎不可能创建有效的 ArrowApply/Monad 实例。

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

    • 如果我们将 headmapzipWith ($) 的结果,我们就会失去携带的箭头突变。
    • 如果我们让头箭头监听xs,我们将卡住第一个箭头的状态。
  • 显式状态方式:

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

    in (app, c)

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

有没有可能的方法来制作允许 ArrowApply 实例的SF

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

P.P.S:如果需要时间,他可以通过输入将其压缩。

最佳答案

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

单独

唯一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) }非常接近 e 中的函数至a 。对于Monad instance for functions唯一明智的>>=是将参数传递给内部函数和外部函数。这是我们已经想出的。让我们看看是否能想出其他办法。

状态T

您的代码有点类似于 StateT monad transformer应用于Monad 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 是什么?例如这样的情况。 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 a 的东西这样SFT ((-> 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创建的类型至(->) 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我能想到的例子,因为它几乎和原来的一样。

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)
}

关于haskell - 在 haskell 中传输状态变化的好的包装器有哪些?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22698363/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com