Запуск мономорфного потребителя внутри трубы

Вопрос как запустить Consumer внутри Pipe уже задан, но ответ, который был предложен тогда, требует синонима полиморфного типа Consumer':

{-# LANGUAGE RankNTypes #-}
import Pipes

toPipe :: Monad m => Consumer' i m o -> Pipe i o m ()
toPipe consumer = consumer >>= yield

Теперь проблема, с которой я столкнулся, заключается в том, что в Pipes.Vector, toVector использует мономорфный синоним Consumer:

toVector :: (PrimMonad m, MVector (Mutable v) e) => Consumer e (ToVector v e m) r

Таким образом, функция toPipe из этого ответа не будет работать в этом случае:

{-# LANGUAGE RankNTypes #-}
module VectorPipe where

import Control.Monad.Primitive (PrimMonad)
import qualified Data.Vector.Generic as G
import Pipes
import Pipes.Vector

toPipe :: Monad m => Consumer' i m o -> Pipe i o m ()
toPipe consumer = consumer >>= yield

vectorPipe :: (PrimMonad m, G.Vector v a) => Pipe a (v a) m ()
vectorPipe = toPipe (runToVectorP toVector)

{-

VectorPipe.hs:13:35-42: Could not deduce (y' ~ ()) …
    from the context (PrimMonad m, G.Vector v a)
      bound by the type signature for
                 vectorPipe :: (PrimMonad m, G.Vector v a) => Pipe a (v a) m ()
      at /Users/casillas/GitHub/tau-sigma/VectorPipe.hs:12:15-62
      ‘y'’ is a rigid type variable bound by
           a type expected by the context: Proxy () a y' y m (v a)
           at /Users/casillas/GitHub/tau-sigma/VectorPipe.hs:13:14
    Expected type: Proxy () a y' y (ToVector v a m) r0
      Actual type: Consumer a (ToVector v a m) r0
    In the first argument of ‘runToVectorP’, namely ‘toVector’
    In the first argument of ‘toPipe’, namely ‘(runToVectorP toVector)’
VectorPipe.hs:13:35-42: Could not deduce (y ~ X) …
    from the context (PrimMonad m, G.Vector v a)
      bound by the type signature for
                 vectorPipe :: (PrimMonad m, G.Vector v a) => Pipe a (v a) m ()
      at /Users/casillas/GitHub/tau-sigma/VectorPipe.hs:12:15-62
      ‘y’ is a rigid type variable bound by
          a type expected by the context: Proxy () a y' y m (v a)
          at /Users/casillas/GitHub/tau-sigma/VectorPipe.hs:13:14
    Expected type: Proxy () a y' y (ToVector v a m) r0
      Actual type: Consumer a (ToVector v a m) r0
    In the first argument of ‘runToVectorP’, namely ‘toVector’
    In the first argument of ‘toPipe’, namely ‘(runToVectorP toVector)’
Compilation failed.

-}

Какие-либо предложения? Возможно, подпись toVector слишком узкая? (Я слишком большой любитель каналов, чтобы сказать... EDIT: я попытался изменить подпись в pipes-vector на Consumer'; код компилируется, но похоже, что vectorPipe никогда не дает результата.)


person Luis Casillas    schedule 22.05.2015    source источник
comment
Не могли бы вы немного объяснить свой вариант использования Pipe a (v a) m ()? Вам нужно сгруппировать входящие значения в поток векторов заданного размера или что-то в этом роде?   -  person danidiaz    schedule 22.05.2015
comment
@danidiaz: Хороший вопрос! Я не делаю никакой группировки, на самом деле я просто пытаюсь сделать канал, который выдает один вектор. Однако цель состоит в том, чтобы составить это с помощью конвейера, который будет создавать несколько элементов списка для этого вектора. В основном: 1. Считайте массу элементов данных в вектор; 2. Запустить несколько статистических анализов, требующих сразу весь вектор (для случайного доступа); 3. Выведите поток результатов этого анализа. Но, может быть, мне нужно, как сделать Pipe из Consumer и Producer, соединенных наоборот. Если нет вопроса на этот счет, я мог бы опубликовать его.   -  person Luis Casillas    schedule 22.05.2015
comment
Возможно, вам действительно не нужно делать все это в одном конвейере. Скомпонуйте потребителя toVector с производителем, который считывает данные, запускает полученный конвейер и извлекает результирующий вектор. Позже вы можете использовать вектор в других конвейерах, если хотите.   -  person danidiaz    schedule 22.05.2015


Ответы (1)


Ну, я придумал это после нескольких дней игры:

import Control.Monad

import Pipes
import Pipes.Core ((//>), (>\\), closed)


-- | Convert a 'Consumer' into a 'Pipe' that 'yield's the consumer's
-- final result.
fromConsumer :: Monad m => Consumer i m r -> Pipe i r m ()
fromConsumer c = c //> closed >>= yield

example1 :: MonadIO m => m ()
example1 = runEffect $ each "abcde" >-> fromConsumer (example' 3) >-> P.print
  where
    example' :: Monad m => Int -> Consumer a m [a]
    example' n = replicateM n await

-- λ> example1
-- "abc"


-- | Convert a 'Producer' into a 'Pipe' that ignores its upstream
-- and sends the producer's contents downstream.
fromProducer :: Monad m => Producer o m r -> Pipe i o m r
fromProducer p = closed >\\ p

example2 :: MonadIO m => m ()
example2 = runEffect $ P.stdinLn >-> fromProducer (each "abcde") >-> P.print

-- Ignores stdin:
--
-- λ> example2
-- 'a'
-- 'b'
-- 'c'
-- 'd'
-- 'e'
person Luis Casillas    schedule 31.05.2015