Is there some way to write a function f :: (a -> b -> ... -> t) -> (Monad m => m a -> m b -> ... -> m t)
, basically liftMn
for any n
?
(EDIT: fixed nonsensical example.)
I'm writing an FRP library, and thought it'd be neat if I could have code vaguely like:
main = do
input1 <- signalFromTextBoxTheUserMayTypeANumberInto
input2 <- signalFromAnotherTextBox
divided <- signal div input1 input2
signal (\x -> showTextAlert ("input1 `div` input2 is " ++ show x)) divided
I've been fiddling with type families to get it working, but I'm starting to think that it's actually not doable. I'm currently doing something like this:
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
This obviously doesn't work, as the unsafePerformIO gets evaluated once and then keeps that value, and if did work it'd still be ugly, hacky and generally evil. Is there a way to do this, or will I just have to let go of the idea?