как решить проблемы с типом Moment t в реактивном банане?

Я пытаюсь написать такую ​​функцию:

module Main where

import Reactive.Banana
import Reactive.Banana.Frameworks


main = putStrLn "hello world"

type MIDIMessage = (Int, Int, Double)

startRBMidi f = do
    (addHandler, fire) <- newAddHandler :: IO (AddHandler MIDIMessage, Handler MIDIMessage)
    let
        networkDesc = do
            emidi <- fromAddHandler (addHandler :: AddHandler MIDIMessage)
            f emidi
    network <- compile networkDesc
    actuate network
    -- add fire to midi callbacks

но я не могу заставить его ввести проверку:

ghc --make -O2 test.hs 
[1 of 1] Compiling Main             ( test.hs, test.o )

test.hs:17:24:
    Couldn't match type ‘t’ with ‘t1’
      because type variable ‘t1’ would escape its scope
    This (rigid, skolem) type variable is bound by
      a type expected by the context: Frameworks t1 => Moment t1 ()
      at test.hs:17:16-34
    Expected type: Moment t1 ()
      Actual type: Moment t ()
    Relevant bindings include
      networkDesc :: Moment t () (bound at test.hs:14:9)
      f :: Event t MIDIMessage -> Moment t () (bound at test.hs:11:13)
      startRBMidi :: (Event t MIDIMessage -> Moment t ()) -> IO ()
        (bound at test.hs:11:1)
    In the first argument of ‘compile’, namely ‘networkDesc’
    In a stmt of a 'do' block: network <- compile networkDesc

Я пробовал разные вещи с ScopedTypeVariables и forall t. но я не могу заставить его работать. Как я могу проверить эту функцию?

[править 1]

Добавление подписи типа

{-# LANGUAGE Rank2Types #-}
module Main where
import Reactive.Banana
import Reactive.Banana.Frameworks

main = putStrLn "hello world"

type MIDIMessage = (Int, Int, Double)

startRBMidi :: (forall t. Event t MIDIMessage -> Moment t ()) -> IO ()
startRBMidi f = do
    (addHandler, fire) <- newAddHandler :: IO (AddHandler MIDIMessage, Handler MIDIMessage)
    let
        networkDesc = do
            emidi <- fromAddHandler (addHandler :: AddHandler MIDIMessage)
            f emidi
    network <- compile networkDesc
    actuate network

Я получил:

test.hs:18:22:
No instance for (Frameworks t0)
  arising from a use of ‘fromAddHandler’
The type variable ‘t0’ is ambiguous
Relevant bindings include
  networkDesc :: Moment t0 () (bound at test.hs:17:9)
Note: there is a potential instance available:
  instance Frameworks
             (reactive-banana-0.8.0.4:Reactive.Banana.Internal.Phantom.FrameworksD,
              t)
    -- Defined in ‘reactive-banana-0.8.0.4:Reactive.Banana.Internal.Phantom’
In a stmt of a 'do' block:
  emidi <- fromAddHandler (addHandler :: AddHandler MIDIMessage)
In the expression:
  do { emidi <- fromAddHandler
                  (addHandler :: AddHandler MIDIMessage);
       f emidi }
In an equation for ‘networkDesc’:
    networkDesc
      = do { emidi <- fromAddHandler
                        (addHandler :: AddHandler MIDIMessage);
             f emidi }

test.hs:20:24:
Couldn't match type ‘t0’ with ‘t’
  because type variable ‘t’ would escape its scope
This (rigid, skolem) type variable is bound by
  a type expected by the context: Frameworks t => Moment t ()
  at test.hs:20:16-34
Expected type: Moment t ()
  Actual type: Moment t0 ()
Relevant bindings include
  networkDesc :: Moment t0 () (bound at test.hs:17:9)
In the first argument of ‘compile’, namely ‘networkDesc’
In a stmt of a 'do' block: network <- compile networkDesc

Нелегко понять, что здесь происходит... это сильно отличается от "обычного" Haskell...

Окончательное решение

{-# LANGUAGE Rank2Types #-}
module Main where

import Reactive.Banana
import Reactive.Banana.Frameworks

main = putStrLn "hello world"

type MIDIMessage = (Int, Int, Double)

startRBMidi :: (forall t. Event t MIDIMessage -> Moment t ()) -> IO ()
startRBMidi f = do
    (addHandler, fire) <- newAddHandler :: IO (AddHandler MIDIMessage, Handler MIDIMessage)
    let
        networkDesc :: forall t. Frameworks t => Moment t ()
        networkDesc = do
            emidi <- fromAddHandler (addHandler :: AddHandler MIDIMessage)
            f emidi
    network <- compile networkDesc
    actuate network

person miguel.negrao    schedule 30.05.2015    source источник


Ответы (1)


Вам нужно указать явную сигнатуру типа для вашей функции startRBMidi, потому что она имеет тип ранга 2:

startRBMidi :: (forall t. Event t MIDIMessage -> Moment t ()) -> IO ()

Это похоже на тип функции compile.

По сути, это говорит о том, что функция аргумента f должна работать для любого времени начала t.

person Heinrich Apfelmus    schedule 31.05.2015
comment
Вам также необходимо добавить сигнатуру типа к networkDesc, потому что в противном случае GHC выведет сигнатуру мономорфного типа для локальной привязки let, а это не то, что вам нужно. Опять же, networkDesc должно работать для всех значений времени начала t. - person Heinrich Apfelmus; 01.06.2015
comment
Отлично, это получилось! Должен сказать, что для человека, не знакомого с Rank2Types и фантомными типами, практически невозможно понять, как правильно использовать фантомный параметр t. Для тех, кто не использует динамическое переключение событий, было бы полезно иметь более простую версию библиотеки, хотя это отклонение от курса потребовало бы больше работы и большего обслуживания. Спасибо за помощь ! - person miguel.negrao; 01.06.2015
comment
Справедливо. Я намерен отказаться от параметра типа t из-за таких проблем. См. проблему № 97. - person Heinrich Apfelmus; 04.06.2015