我正在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。
答案 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