使用Elerea创建有状态信号类型的应用实例

时间:2014-10-09 02:35:11

标签: haskell frp elerea

我正在Helm中试图创建一个有状态信号类型,该信息类型将包含自上次采样以来值是否已更改的其他信息。我设法使新信号类型成为Functor的一个实例,但我很难定义<*> Applicative函数,这将允许我将多个信号提升到一个函数中。

到目前为止我定义的唯一信号是constant信号,初始采样时会有Changed x,之后会有Unchanged x。其他信号会更复杂,并使用Elerea.stateful来确定值是否已更改。

lift2无法在下面的代码中使用,因为<*>尚未定义。

import qualified FRP.Elerea.Simple as Elerea
import Control.Applicative

data Event a = Changed a | Unchanged a

instance Functor Event where
    fmap f (Changed a)   = Changed (f a)
    fmap f (Unchanged a) = Unchanged (f a)

instance Applicative Event where
    pure = Unchanged
    (Changed   f) <*> (Changed   x) = Changed (f x)
    (Changed   f) <*> (Unchanged x) = Changed (f x)
    (Unchanged f) <*> (Changed   x) = Changed (f x)
    (Unchanged f) <*> (Unchanged x) = Unchanged (f x)

data Signal a = Signal (Elerea.SignalGen (Elerea.Signal (Event a)))

instance Functor Signal where
    fmap f (Signal x) = Signal ((fmap . fmap . fmap) f x)

instance Applicative Signal where
    pure = Signal . pure . pure . pure
    --(<*>) = ??

{-| Creates a signal that never changes. -}
constant :: a -> Signal a
constant x = Signal $ Elerea.stateful (Changed x) (\_ -> Unchanged x)

lift :: (a -> b) -> Signal a -> Signal b
lift = fmap

lift2 :: (a -> b -> c) -> Signal a -> Signal b -> Signal c
lift2 f a b = fmap f a <*> b

我目前使用的完整代码位于Github

1 个答案:

答案 0 :(得分:1)

pure . pure . pure的签名是:

(Applicative f, Applicative f1, Applicative f2) => a -> f (f1 (f2 a))

所以你只想通过另外两个应用层提升<*>。您需要一个带签名的功能:

(Applicative f, Applicative f1, Applicative f2) => f (f1 (f2 (a -> b))) -> f (f1 (f2 a)) -> f (f1 (f2 b))

此功能可以通过两次应用liftA2来完成,因此请尝试以下操作:

(Signal f) <*> (Signal x) = Signal $ (liftA2 (liftA2 (<*>))) f x