Haskell Pipes – получить возвращаемое значение последнего прокси в конвейере

Допустим, у меня есть два Proxy в Haskell Pipes. Они представляют внешние системные процессы.

produce :: MonadIO m => Producer ByteString m ExitCode
consume :: MonadIO m => Consumer ByteString m ExitCode

Поэтому я подключаю их к Effect, вот так:

effect :: Effect m ExitCode
effect = produce >-> consume

Этот Effect даст мне ExitCode из первого завершающегося Proxy. Обычно это будет produce, а не consume. Какой идиоматический способ Pipes получить возвращаемое значение consume, даже если оно не завершается первым?

До сих пор я думаю, что это невозможно без какой-то странной внутриполосной сигнализации, поэтому consume знает, что поток выполнен. Единственный способ завершить работу последнего прокси — это получить что-то от await, чтобы я мог послать ему пустой ByteString, чтобы сигнализировать об окончании потока. Это просто не кажется правильным. Теперь у меня есть отдельный MVar, который может предоставить значение выхода, но я думаю, что должен быть более идиоматический способ сделать это.


person Omari Norman    schedule 27.03.2015    source источник


Ответы (2)


Без внутриполосной сигнализации Consumer никогда не сможет иметь «возвращаемое значение», если Producer вернется первым. Если производитель returning, это означает, что Consumer должен быть заблокирован в ожидании запрошенного значения. Consumer больше никогда не запустится и, таким образом, никогда не будет возможности return, пока Consumer не получит внутриполосный сигнал с запрошенным значением.

Тот факт, что сигнализация является внутриполосной, не означает, что она должна быть «неприятной». Мы можем преобразовать Producer, который может вернуться, в Producer, который, как мы знаем, не вернет (тип возвращаемого значения — forall r' . r'), захватив return и перенаправив его вниз по течению. Мы делаем это forever на случай, если другой запрос вернется вверх по течению.

returnDownstream :: Monad m => Proxy a' a b' b m r -> Proxy a' a b' (Either r b) m r'
returnDownstream = (forever . respond . Left =<<) . (respond . Right <\\)

В конце Consumer вам нужно явно указать, что делать, когда значение requested, но вместо получения ответа (в Right) вы получаете возвращаемое значение вышестоящего производителя (в Left).

person Cirdec    schedule 27.03.2015

Спасибо. То, что я придумал, это что-то вроде

produce :: MonadIO m => Producer (Either ExitCode ByteString) m r
consume :: MonadIO m => Consumer (Either ExitCode ByteString) m (Maybe ExitCode, ExitCode)

так что, когда Эффект запускается, я получаю (Ничего, код), если нижестоящий процесс завершился, или (Только код1, код2), если вышестоящий процесс завершился первым. (Если нижестоящий процесс завершается первым, вышестоящему процессу ничего не остается делать, кроме как завершить его, поэтому предоставление кода выхода не имеет никакого смысла.)

person Omari Norman    schedule 28.03.2015