Я думаю, что клиент похож на нормальный асинхронный запрос Ajax, но Вы ожидаете, что он займет много времени для возвращения.
сервер тогда похож на это.
while (!hasNewData())
usleep(50);
outputNewData();
Так, запрос Ajax переходит к серверу, вероятно, включая метку времени того, когда это было последнее обновление так, чтобы Ваш hasNewData()
знал, какие данные Вы уже получили. Сервер тогда находится в цикле, спя, пока новые данные не доступны. Все время Ваш запрос Ajax все еще соединен, просто зависнув там ожидающий данных. Наконец, когда новые данные доступны, сервер дает их Вашему запросу Ajax и закрывает соединение.
Вы можете определить последовательность как
sequence xs = foldr (liftM2 (:)) (return []) xs
Проблема с liftM2
, которую вы видели у вас нет возможности остановить m2
, что может быть launchTheMissiles
!
liftM2 :: (Monad m) => (a -> b -> c) -> m a -> m b -> m c
liftM2 f m1 m2 = do
x1 <- m1
x2 <- m2
return (f x1 x2)
Использование охранника
, как показано ниже, кажется привлекательным:
sequenceUntil p xs = foldr (myLiftM2 p (:)) (return []) xs
where myLiftM2 p f m1 m2 = do
x1 <- m1
guard $ p x1
x2 <- m2
return (f x1 x2)
Приведенный выше код не будет работать в вашем приложении, потому что монада ввода-вывода не является экземпляром MonadPlus .
Так что держите его за руку еще немного
module Main where
import Control.Monad
printx :: Int -> IO Int
printx x = do
print x
return x
sequenceUntil :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
sequenceUntil p xs = foldr (myLiftM2 (:) []) (return []) xs
where myLiftM2 f z m1 m2 = do
x1 <- m1
if p x1 then do x2 <- m2
return $ f x1 x2
else return z
main :: IO ()
main = do
let as :: [IO Int]
as = map printx [1..10]
ys <- sequenceUntil (< 4) as
print ys
Хотя как
- это список действий от 1 до 10, вывод будет
1
2
3
4
[1,2,3]
Отбрасывание результатов тогда тривиально:
sequenceUntil_ :: (Monad m) => (a -> Bool) -> [m a] -> m ()
sequenceUntil_ p xs = sequenceUntil p xs >> return ()
main :: IO ()
main = do
let as :: [IO Int]
as = map printx [1..]
sequenceUntil_ (< 4) as
Обратите внимание на использование [1 .. ]
, который показывает, что новый комбинатор поддерживает лень .
Вы можете предпочесть spanM
:
spanM :: (Monad m) => (a -> Bool) -> [m a] -> m ([a], [m a])
spanM _ [] = return ([], [])
spanM p (a:as) = do
x <- a
if p x then do (xs,bs) <- spanM p as
return (x:xs, bs)
else return ([x], as)
Обратите внимание, что он немного отличается от span тем, что он включает неисправный элемент в список результатов. Вторая пара - это оставшиеся действия. Например:
*Main> (xs,bs) <- spanM (< 4) as
1
2
3
4
*Main> xs
[1,2,3,4]
*Main> sequence bs
5
6
7
8
9
10
[5,6,7,8,9,10]
Еще одна альтернатива:
untilM :: Monad m => (a -> Bool) -> [m a] -> m ()
untilM p (x:xs) = do
y <- x
unless (p y) $ untilM p xs
Обратите внимание, что смысл предиката дополняется:
*Main> untilM (>= 4) as
1
2
3
4
Я не думаю, что в стандартной библиотеке есть что-то вроде takeWhileM
, но вы могли бы написать его самостоятельно, чтобы выполнялось столько операций ввода-вывода, сколько необходимо:
takeWhileM :: (Monad m) => (a -> Bool) -> [m a] -> m [a]
takeWhileM _ [] = return []
takeWhileM p (a:as) =
do v <- a
if p v
then do vs <- takeWhileM p as
return (v:vs)
else return []
Предоставленный список оценивается только до тех пор, пока не будет найден элемент, не соответствующий предикату:
*Main> takeWhileM (<4) (map f [1..5])
1
2
3
4
[1,2,3]
Правка: Теперь я понимаю, что вы ищете.
gbacon опубликовал красивую функцию sequenceWhile
, которая является почти «примитивом», который вам нужен.
На самом деле, поскольку вас интересуют только побочные эффекты, последовательности , хотя_
должно быть достаточно. Вот определение (опять же, вдохновленное gbacon, проголосуйте за него!):
sequenceWhile_ :: (Monad m) => (a -> Bool) -> [m a] -> m ()
sequenceWhile_ p xs = foldr (\mx my -> mx >>= \x -> when (p x) my)
(return ()) xs
Вы называете это так:
Prelude Control.Monad> sequenceWhile (<4) $ map f [1..]
Исходный ответ:
Вы не можете просто «снять» значения из IO
Монада для использования с takeWile
, но вы можете «поднять» takeWhile
для использования в монаде!
Функция liftM примет функцию (a -> b)
в функцию (ma -> mb)
, где m
- монада.
(В качестве примечания, вы можете найти такую функцию, выполнив поиск по ее типу в Hoogle , в данном случае выполнив поиск по: Monad m => (a -> b) -> (ma -> mb)
)
С помощью liftM
вы можете сделать это:
Prelude> :m + Control.Monad
Prelude Control.Monad> let f x = print x >> return x
Prelude Control.Monad> liftM (takeWhile (<4)) $ mapM f [0..5]
0
1
2
3
4
5
[0,1,2,3]
Возможно, это не то, что вам нужно. mapM
применяет функцию f
ко всему списку последовательно перед возвратом списка. Полученный список затем передается поднятой функции takeWhile
.
Если вы хотите остановить печать после третьего элемента, вам придется прекратить вызов print. Это означает, что не применяйте f
к такому элементу. Таким образом, вы получите что-то простое, например:
Prelude> mapM_ f (takeWhile (<4) [0..5])
Кстати, стоит ли вам задаться вопросом , почему mapM
сначала напечатает все, а затем вернет список.
Вы можете использовать один из пакета «Список» .
import Control.Monad.ListT (ListT)
import Data.List.Class (execute, fromList, joinM, takeWhile)
import Prelude hiding (takeWhile)
f x = print x >> return x
main =
execute . takeWhile (< 4) .
joinM $ fmap f (fromList [0..5] :: ListT IO Int)
fromList [0..5]
создает монадический список, содержащий 0..5, который не выполняет монадических действий fmap f
с этим списком приводит к ListT IO (IO Int)
, который по-прежнему не выполняет монадических действий, только содержит их . joinM
превращает это в ListT IO Int
. каждое содержащееся действие будет выполнено, когда элемент будет потреблен, и его результатом будет значение в списке. takeWhile
обобщен для любого списка
. И []
, и « Monad m => ListT m
» являются экземплярами List
. execute
использует монадический список, выполняя все его действия. «toList :: List m => ma -> ItemM m [a]»
(« ItemM (ListT IO)
» равно IO
). поэтому в данном случае это « toList :: ListT IO a -> IO [a]
». Еще лучше вы можете продолжать использовать функции высшего порядка, такие как scanl
и т. Д. Для обработки монадического списка во время его выполнения.