Объединение двух Enumeratees
Я пытаюсь обернуть голову вокруг библиотеки enumerator
и столкнулся с ситуацией, когда я хочу построить новый Enumeratee в терминах двух существующих Enumeratees. Скажем, у меня есть перечисления:
e1 :: Enumeratee x y m b
e2 :: Enumeratee y z m b
Я чувствую, что должен быть в состоянии объединить их в один enumeratee
e3 :: Enumeratee x z m b
но я не смог найти существующую функцию для этого в пакете. Я попытался написать такую функцию самостоятельно, но мое понимание итераций по-прежнему настолько ограничено, что я не мог понять, как можно совместить все сложные типы.
Я просто пропустил какой-то базовый комбинатор, или Enumeratees даже должен быть скомпонован друг с другом?
Ответы
Ответ 1
В теории они являются составными, но типы немного сложны. Трудность состоит в том, что конечный параметр b
первого перечисления не является фактически b
; это еще одна итерация!. Здесь тип оператора ><>
из iteratee, который включает перечисление:
Prelude Data.Iteratee> :t (><>)
(><>)
:: (Monad m, Nullable s1) =>
(forall x. Enumeratee s1 s2 m x)
-> Enumeratee s2 s3 m a -> Enumeratee s1 s3 m a
Обратите внимание на дополнительный forall
в первом перечислении; это указывает на то, что тип Rank-2 работает. Если автор enumerator
хочет поддерживать совместимость с H98 (я считаю, что это была одна из исходных целей), этот подход недоступен.
Можно написать подпись этого типа в форме, которая не требует типов Rank-2, но она либо длиннее, не ясна из того типа, который он на самом деле состоит из двух перечисляемых, или того и другого. Например, это тип ghc для (><>)
:
Prelude Data.Iteratee> :t (><>>)
(><>>)
:: (Monad m, Nullable s) =>
(b -> Iteratee s m (Iteratee s' m a1))
-> (a -> b) -> a -> Iteratee s m a1
Хотя эти типы предназначены для комбинаторов iteratee
, надеюсь, что достаточно информации, вы сможете применить их к enumerator
.
Ответ 2
Я столкнулся с этой проблемой некоторое время назад, вам нужно сначала иметь Iteratee (или Enumerator), чтобы создать состав Enumeratees.
Вы можете начать с этого:
module Main where
import Data.Enumerator
import qualified Data.Enumerator.List as EL
main :: IO ()
main = run_ (enum $$ EL.consume) >>= print
where
enum = (enumList 5 [1..] $= EL.isolate 100) $= EL.filter pairs
pairs = (==0) . (`mod` 2)
Предыдущий код составляет список перечислений вместе для создания нового счетчика, а затем он применяется к потреблению Iteratee.
($ =) служит для компоновки Enumerator и Enumeratee для создания нового счетчика, тогда как (= $) можно использовать для компоновки Iteratee с Enumeratee для создания новый Iteratee. Я рекомендую, чтобы последний дал, что типы не будут разбивать ваши мячи при составлении списка Enumeratees, используя (= $):
module Main where
import Data.Enumerator
import qualified Data.Enumerator.List as EL
main :: IO ()
main = run_ (enumList 5 [1..] $$ it) >>= print
where
it = foldr (=$)
EL.consume
[ EL.isolate 100
, EL.filter ((==0) . (`mod` 2))
]
Если вы попытаетесь реализовать ту же самую функцию выше, создав Enumerator вместо Iteratee, вы получите ошибку с бесконечным рекурсивным типом при использовании foldl' ($=) (enumList 5 [1..]) [list-of-enumeratees]
.
Надеюсь, что это поможет.