Объединение двух 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].

Надеюсь, что это поможет.