编写函数(a - > b - > ... - > t) - > (Monad m => m a - > m b - > ... - > m t)

时间:2012-03-22 18:34:35

标签: haskell types monads type-families

有没有办法为任何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和一般邪恶。有没有办法做到这一点,还是我必须放弃这个想法?

2 个答案:

答案 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 postApplicative接口没问题,但Monad接口没有。有几种解决方案可以防止时间泄漏,同时仍然允许相同的动态事件切换行为,包括an additional type parameter或其他monad(如sodium库中所见)。