为了我自己的兴趣,我正在尝试实现一个简单的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:我们有一对对象列表(箭头,x) – 或者,unziped,这对列表(箭头,xs).
>如果我们将头部映射到zipWith($)’的结果,我们将松开随身携带的箭头变异.
>如果我们让头部箭头听xs,我们将冻结第一个箭头的状态.
>显式状态:
instance ArrowApply Behaviour where
app =
Behaviour $\(bf, a) ->
let (bf1, c) = bf `runBehaviour` a
in (app, c)
这里我们需要以某种方式有效地将bf1注入到返回的应用程序中,这是不可能的(并且实际注入(const bf1 *** id)会产生与其他实现中的第二个类似的无效行为.
是否有可能制作允许ArrowApply实例的SF?
P.S.:当一个分支长时间未使用时,Stream-way在ArrowChoice中有内存泄漏.现在,我无法解决这个问题.是否有可能制作无泄漏版本?
P.P.S:如果需要时间,他可以输入拉链.
最佳答案 我找不到任何可能不会简单地丢弃内部容器状态的实例.这并不奇怪,因为绑定到数据的纯函数的返回应该在每次调用输入时返回相同的内容,无论之前是否已经调用过,您在注释中提示.
通过它自己
我可以提出的唯一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,唯一合理的>> =是将参数传递给内部和外部函数.这就是我们已经提出的.让我们看看我们是否可以拿出别的东西.
StateT
你的代码有点类似于应用于Monad instance for functions的StateT monad transformer.不幸的是,这并没有产生我们想要的东西.
考虑以下(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的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
我们得到以下内容,其中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的东西,使得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) }
对于应用于e的SF,我们可以将通过应用SFT制作的类型替换为( – >)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)
}