Написание функции (a -> b -> ... -> t) -> (Monad m => ma -> mb -> ... -> mt) - PullRequest
2 голосов
/ 22 марта 2012

Есть ли способ написать функцию 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 оценивается один раз, а затем сохраняет это значение, и если оно сработало, оно все равно будет уродливым, хакерским и, как правило, злым. Есть ли способ сделать это, или я просто должен отпустить идею?

Ответы [ 2 ]

10 голосов
/ 22 марта 2012

Я в некотором роде новичок во всем этом, так что простите, если это глупый ответ, но разве это не для чего аппликативные функторы?1003 *

f :: a -> b -> ... -> c

f2 :: Applicative p => p a -> p b ... -> p c
f2 x ... y = f <$> x <*> ... <*> y

если я не ошибаюсь.(Эллипсами являются любое количество типов / аргументов)

6 голосов
/ 22 марта 2012

Как насчет препроцессора Strathclyde Haskell Environment, который позволяет использовать идиоматические скобки , оригинальные обозначения для аппликативных функторов?Это позволяет вам использовать (| f a b c |) для f <$> a <*> b <*> c.Ваш пример будет (| input1 `div` input2 |).

Кстати, для вашего типа Signal, вероятно, плохая идея иметь экземпляр Monad;это вызывает хорошо известную (в сообществе FRP) проблему утечек времени ;см. этот блог для получения дополнительной информации.Интерфейс Applicative в порядке, а интерфейс Monad - нет.Существует несколько решений, которые предотвращают утечку времени, и в то же время допускают такое же поведение переключения динамических событий, включая такие вещи, как дополнительный параметр типа или другую монаду (как видно, например, из библиотеки odium ).

...