2015-03-07 2 views
4

Я играю с пакетом netwire, пытаясь почувствовать FRP, и у меня есть быстрый вопрос.Haskell Netwire: провода проводов

Начиная со следующими простыми проводами, я могу испускать событие каждые 5 секунд (приблизительно)

myWire :: (Monad m, HasTime t s) => Wire s() m a Float 
myWire = timeF 

myWire' :: (Monad m, HasTime t s) => Wire s() m a Int 
myWire' = fmap round myWire 

myEvent :: (Monad m, HasTime t s) => Wire s() m a (Event Int) 
myEvent = periodic 5 . myWire' 

Это очень красиво и прямо вперед, но то, что я хочу делать дальше сопоставьте каждый событие, произведенное на проводе, после чего я могу посмотреть обновление. У меня есть агрегатная функция, как следующее:

eventList :: (Monad m, HasTime t s) 
      => Wire s() m a (Event [Wire s() m a Int]) 
eventList = accumE go [] . myEvent 
    where go soFar x = f x : soFar 
     f x = for 10 . pure x --> pure 0 

Я тогда ввести новый провод, который будет тормозить, пока eventList начинается не запуская события, например так:

myList :: (Monad m, HasTime t s) => Wire s() m a [Wire s() m a Int] 
myList = asSoonAs . eventList 

Так я пошел от событий провод, содержащий список проводов. Наконец, я ввожу провод к шагу каждый из этих проводов и составить список результатов:

myNums :: (Monad m, HasTime t s) => Wire s() m [Wire s() m a Int] [Int] 
myNums = mkGen $ \dt wires -> do 
    stepped <- mapM (\w -> stepWire w dt $ Right undefined) wires 
    let alive = [ (r, w) | (Right r, w) <- stepped ] 
    return (Right (map fst alive), myNums) 

myNumList :: (Monad m, HasTime t s) => Wire s() m a [Int] 
myNumList = myNums . myList 

И, наконец, у меня основная программа, чтобы проверить все это:

main = testWire clockSession_ myNumList 

Что ожидать чтобы увидеть это растущий список, где каждый элемент в списке покажет время его создания в течение 10 секунд, после чего элемент покажет нуль. Вместо этого я получаю растущий список статических значений. Например, то, что я ожидаю увидеть после нескольких этапов, -

[0] 
[5, 0] 
[10, 5, 0] 
[15, 10, 0, 0] 

и так далее. То, что я на самом деле вижу, это

[0] 
[5, 0] 
[10, 5, 0] 
[15, 10, 5, 0] 

Итак, я знаю, что моя функция аккумулятора работает: каждое созданное событие преобразуется в провод. Но я не вижу, что эти провода излучают разные значения с течением времени. Мое заявление for 10 . pure x --> pure 0 должно переключать их на излучение 0 по истечении времени.

Я еще новичок в FRP, так что я может быть в корне недоразумения что-то важное об этом (вероятно, так.)

+0

Я не думаю, что это возможно с netwire или любой из других современных библиотек FRP. Эти библиотеки прилагают совместные усилия, чтобы избежать утечки времени и пространства, избегая предоставления экземпляра «Монады» или чего-либо подобного. Это означает, что вообще невозможно включить «Wire ... (Wire ... a)» в «Wire a». – Cirdec

ответ

2

Проблема заключается в том, что провод, полученный от событий, не является постоянным. Данное значение типа Wire s e m a b фактически является экземпляром со временем функции, которая производит значение типа b со значения типа a. Так как Haskell использует неизменяемые значения, для того, чтобы шагать по проводам, вам нужно что-то сделать с полученным проводом от stepWire, иначе вы получите тот же результат для того же входа. Посмотрите на результатах от myList:

Event 1: [for 10 . pure 0 --> pure 0] 
Event 2: [for 10 . pure 5 --> pure 0, for 10 . pure 0 --> pure 0] 
Event 3: [for 10 . pure 10 --> pure 0, for 10 . pure 5 --> pure 0, for 10 . pure 0 --> pure 0] 
... etc 

Когда вы шаг каждого из этих проводов, вы только собираетесь получить [.., 10, 5, 0] каждый раз, потому что вы повторно использовать первоначальную стоимость for 10 . pure x --> pure 0 проволоки. Посмотрите на подпись для stepWire:

stepWire :: Monad m => Wire s e m a b -> s -> Either e a -> m (Either e b, Wire s e m a b) 

Это означает, что для заявления таких как

(result, w') <- stepWire w dt (Right underfined) 

...w' следует использовать в следующий раз, когда вам нужно позвонить stepWire, потому что это поведение в следующем экземпляре во времени. Если у вас есть провод, который производит провода, тогда вам нужно разгрузить произведенные провода где-нибудь, чтобы они могли обрабатываться отдельно.

Для программы, которая (я считаю), дает вам поведение, которое вы хотите, пожалуйста, обратитесь к this code.

$ ghc -o test test.hs 
[1 of 1] Compiling Main    (test.hs, test.o) 
Linking test ... 
$ ./test 
[0] 
[5,0] 
[10,5,0] 
[15,10,0,0] 
[20,15,0,0,0] 
... 
2

Как Mokosha suggested, мы можем сохранить состояние проводов мы уже знаем о предыдущих событиях и ввести провода от нового события. Например, если мы уже знаем о каких-либо событиях и получаем список с одним проводом в нем, мы должны использовать новый провод.

 [] -- we already know about 
w0' : [] -- we got as input 
w0' : [] -- what we should keep 

Если мы уже знаем о проволоке или проволоке и узнать о еще более провода, мы должны держать провода мы уже знаем о том и добавить новые провода мы только что узнали о.

  w2 : w1 : w0 : [] -- we already know about 
w4' : w3' : w2' : w1' : w0' : [] -- we got as input 
w4' : w3' : w2 : w1 : w0 : [] -- what we should keep 

Этого легче определить из переднего края списка. Первый аргумент будет префиксом результата. Если есть оставшийся второй аргумент, мы добавим их в конец списка.

makePrefixOf :: [a] -> [a] -> [a] 
makePrefixOf [] ys = ys 
makePrefixOf xs [] = xs 
makePrefixOf (x:xs) (_:ys) = x:makePrefixOf xs ys 

Мы можем определить то же самое для заднего конца списка, изменив входные и выходные сигналы. Первый аргумент будет суффиксом результата, если есть какие-либо дополнительные из второго ажимента, они добавляются в начало списка.

makeSuffixOf :: [a] -> [a] -> [a] 
makeSuffixOf xs ys = reverse $ makePrefixOf (reverse xs) (reverse ys) 

Теперь мы можем реализовать myNums, отслеживающих которого oldWires у нас уже есть состояние для.

myNums :: (Monad m, HasTime t s) => Wire s() m [Wire s() m a b] [b] 
myNums = go [] 
    where 
    go oldWires = mkGen $ \dt newWires -> do 
     let wires = makeSuffixOf oldWires newWires 
     stepped <- mapM (\w -> stepWire w dt $ Right undefined) wires 
     let alive = [ (r, w) | (Right r, w) <- stepped ] 
     return (Right (map fst alive), go (map snd alive)) 

Если мы хотим быть педантичным, мы должны использовать список Maybe проводов, так что, когда провод уже нет в живых, мы не можем оставить позади Nothing на своем месте, так что списки проводов еще совпасть. Если мы это сделаем, даже с умным представлением для состояния, мы просочим пространство, когда провода погибнут. Их первоначальное определение по-прежнему будет висящим в списке, накапливаемом eventList.

Это дает желаемый выход

  [ 0] 
     [ 5, 0] 
     [10, 5, 0] 
    [15,10, 0, 0] 
[20,15, 0, 0, 0] 
Смежные вопросы