Обработка кодирования суммы в библиотеках потоковой передачи

Мотивация этого вопроса заключается в следующем сценарии: у нас есть поток значений, которые представлены кодировкой Sum. Предположим, что Either ByteString ByteString, где мы представляем потоки байтов в ошибочных и хороших состояниях соответственно. Теперь у нас есть еще одна функция, которая может сжимать поток ByteString. Можно ли запустить эту функцию на входном потоке Either ByteString ByteString и сжать любой из них (не только Right, но и Left в случае, если вместо Right получается Left). Подпись типа функции compress приведена ниже (я использую Streaming библиотека):

compress ::  MonadIO m 
         =>  Int 
         -- ^ Compression level.
         -> Stream (Of ByteString) m r
         -> Stream (Of ByteString) m r 

Наш входной поток имеет тип Stream (Of (Either ByteString ByteString)) m r. Итак, есть ли какая-то функция преобразования, которая может запускать compress во входном потоке и выводить поток типа Stream (Of (Either ByteString ByteString)) m r, где оба сжаты.

Мне кажется, что вместо этого я должен написать пользовательское compress, скажем, eitherCompress следующим образом:

eitherCompress :: MonadIO m 
             =>  Int 
             -- ^ Compression level.
             -> Stream (Of (Either ByteString ByteString)) m r
             -> Stream (Of (Either ByteString ByteString)) m r 

Это правильно? Если это так, как правильно написать eitherCompress с помощью приведенной ниже функции из zstd:

compress :: Int 
         -- ^ Compression level. Must be >= 1 and <= maxCLevel.
         -> IO Result    

Я написал stream производителей, используя yield, но я реализовал их для простых случаев, когда ввод является просто источником, а не потоком. Буду очень признателен за помощь в решении этой проблемы.


person Sal    schedule 11.03.2018    source источник


Ответы (1)


Обычный трюк для решения этих случаев состоит в том, чтобы поместить каждую ветвь суммы в разные монадические слои (так что будет два потоковых слоя), управлять каждым слоем отдельно, а затем либо потреблять их по отдельности, либо повторно объединять их в один слой.

Во-первых, две вспомогательные функции, использующие maps для преобразования в и из Sum< /a> композиция функторов:

toSum :: Monad m 
      => Stream (Of (Either ByteString ByteString)) m r 
      -> Stream (Sum (Of ByteString) (Of ByteString)) m r
toSum = maps $ \(eitherBytes :> x) -> 
    case eitherBytes of
        Left bytes -> InL (bytes :> x)
        Right bytes -> InR (bytes :> x)

fromSum :: Monad m 
        => Stream (Sum (Of ByteString) (Of ByteString)) m r 
        -> Stream (Of (Either ByteString ByteString)) m r
fromSum = maps $ \eitherBytes ->
    case eitherBytes of
        InL (bytes :> x) -> Left bytes :> x
        InR (bytes :> x) -> Right bytes :> x

Мы делаем это, чтобы иметь возможность использовать separate и unseparate функции.

Фактическая функция сжатия будет:

eitherCompress :: MonadIO m 
               => Int 
               -> Stream (Of (Either ByteString ByteString)) m r 
               -> Stream (Of (Either ByteString ByteString)) m r
eitherCompress level =
     fromSum . unseparate . hoist (compress level) . compress level . separate . toSum

hoist используется для монадический слой ниже самого верхнего.

person danidiaz    schedule 13.03.2018
comment
Идеально, это то, что я искал! Я видел кодировку sum в документации, но не смог собрать воедино различные части, пока вы не ответили на нее, указав подход toSum и fromSum. - person Sal; 14.03.2018
comment
вопрос - если compress специализировано на конкретный возвращаемый тип (например, Stream (Of ByteString) m () - вместо r у нас теперь ()), то hoist не будет работать из-за квантификатора forall. В таком случае, чем заменить hoist? - person Sal; 14.03.2018
comment
@Sal В этом случае я не знаю, как заменить hoist. - person danidiaz; 14.03.2018