有没有办法为任何f :: (a -> b -> ... -> t) -> (Monad m => m a -> m b -> ... -> m t)
编写函数liftMn
,基本上n
?
(编辑:修复荒谬的例子。)
我正在写一个FRP库,并且认为如果我的代码含糊不清,它会很整洁:
main = do
input1 <- signalFromTextBoxTheUserMayTypeANumberInto
input2 <- signalFromAnotherTextBox
divided <- signal div input1 input2
signal (\x -> showTextAlert ("input1 `div` input2 is " ++ show x)) divided
我一直在摆弄类型家庭以使其发挥作用,但我开始认为它实际上是不可行的。我现在正在做这样的事情:
type Action a = IORef a -> IO ()
type Listener = IO ()
newtype Signal a = Signal (IORef (SigData a))
data SigData a = SigData {
trigger :: Action a,
output :: IORef a,
listeners :: [Listener]
}
class Sig a where
type S a
mkSig :: [AnySignal] -> a -> S a
instance Sig b => Sig (a -> b) where
type S (a -> b) = Signal a -> S b
mkSig dependencies f =
\s@(Signal sig) ->
let x = unsafePerformIO $ readIORef sig >>= readIORef . output
in mkSig (AnySignal s : dependencies) (f x)
instance Sig Int where
type S Int = IO (Signal Int)
out <- newIORef x
self <- Signal <$> (newIORef $ SigData {
trigger = \ref -> writeIORef ref $! x,
output = out,
listeners = []
})
mapM_ (self `listensTo`) deps
return self
这显然不起作用,因为unsafePerformIO被评估一次然后保持该值,如果确实有效,它仍然是丑陋,hacky和一般邪恶。有没有办法做到这一点,还是我必须放弃这个想法?
答案 0 :(得分:10)
我对这一切都不熟悉,所以请原谅我,如果这是一个愚蠢的答案,但这不是应用程序有效的原因吗?
申请人可以这样做:
f :: a -> b -> ... -> c
f2 :: Applicative p => p a -> p b ... -> p c
f2 x ... y = f <$> x <*> ... <*> y
如果我没有弄错的话。 (省略号是任意数量的类型/参数)
答案 1 :(得分:6)
Strathclyde Haskell Environment预处理器如何使用idiom brackets,这是应用函子的原始符号?这样,您就可以(| f a b c |)
使用f <$> a <*> b <*> c
。您的示例是(| input1 `div` input2 |)
。
顺便说一句,Signal
类型有一个Monad
个实例可能不是一个好主意;这导致众所周知(在FRP社区中)时间泄漏的问题;有关详细信息,请参阅this blog post。 Applicative
接口没问题,但Monad
接口没有。有几种解决方案可以防止时间泄漏,同时仍然允许相同的动态事件切换行为,包括an additional type parameter或其他monad(如sodium库中所见)。