Возможна ли труба с ограничением доходности на одного ожидания?
Я работаю с дудками-4.0.0. В этой библиотеке количество выходов в нисходящем направлении, которое делает канал, в общем случае не связано с количеством ожидающих в восходящем потоке.
Но предположим, что я хотел построить ограниченный канал, который обеспечивал выполнение одного и только одного выхода для каждого await, и в то же время был в состоянии упорядочить эти типы каналов, используя monadic (>>=).
Я заметил, что в двунаправленном случае каждое значение, запрашиваемое прокси-сервером в восходящем направлении, сопоставляется со значением, отправленным обратно. Так что, возможно, я ищу функцию типа Proxy a' a () b m r -> Pipe a (Either b a') m r
это "отражает" значения, идущие вверх по течению, превращая их в дополнительную доходность вниз по течению. Или, менее обычно, Client a' a -> Pipe a a'
, Возможна ли такая функция?
2 ответа
Вы определенно не хотите использовать pipes
за это. Но то, что вы можете сделать, это определить ограниченный тип, который делает это, выполнить все ваши соединения и логику в этом ограниченном типе, а затем преобразовать его в Pipe
когда вы сделали.
Тип вопроса, который вы хотите, это, который похож на netwire
Wire
:
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Trans.Free -- from the 'free' package
data WireF a b x = Pass (a -> (b, x)) deriving (Functor)
type Wire a b = FreeT (WireF a b)
Это автоматически монада и монадный трансформатор, так как он реализован с точки зрения FreeT
, Тогда вы можете реализовать эту удобную операцию:
pass :: (Monad m) => (a -> b) -> Wire a b m ()
pass f = liftF $ Pass (\a -> (f a, ()))
... и собирать пользовательские провода, используя монадический синтаксис:
example :: Wire Int Int IO ()
example = do
pass (+ 1)
lift $ putStrLn "Hi!"
pass (* 2)
Затем, когда вы закончите соединять вещи с этим ограниченным Wire
типа вы можете продвинуть его к Pipe
:
promote :: (Monad m) => Wire a b m r -> Pipe a b m r
promote w = do
x <- lift $ runFreeT w
case x of
Pure r -> return r
Free (Pass f) -> do
a <- await
let (b, w') = f a
yield b
promote w'
Обратите внимание, что вы можете определить идентификатор и состав провода и провода:
idWire :: (Monad m) => Wire a a m r
idWire = forever $ pass id
(>+>) :: (Monad m) => Wire a b m r -> Wire b c m r -> Wire a c m r
w1 >+> w2 = FreeT $ do
x <- runFreeT w2
case x of
Pure r -> return (Pure r)
Free (Pass f2) -> do
y <- runFreeT w1
case y of
Pure r -> return (Pure r)
Free (Pass f1) -> return $ Free $ Pass $ \a ->
let (b, w1') = f1 a
(c, w2') = f2 b
in (c, w1' >+> w2')
Я уверен, что они образуют Category
:
idWire >+> w = w
w >+> idWire = w
(w1 >+> w2) >+> w3 = w1 >+> (w2 >+> w3)
Кроме того, я уверен, что promote
подчиняется следующим законам функторов:
promote idWire = cat
promote (w1 >+> w2) = promote w1 >-> promote w2
У меня такое чувство, что это будет очень трудно сделать, если не сказать невозможно. Вы можете не только писать производителей и потребителей полными сложных циклов, но и монадический интерфейс определяет, что поток управления потребителя может зависеть от значений, которые он получает от производителя.
consumer = do
n <- await
for i in 1..n do
m <- await
print m
Будет очень трудно кодировать типы производителей: "это создает N + 1 чисел, где N - значение первого полученного числа".
Возвращаясь к теме, я думаю, что у вас может быть больше шансов, если вы будете использовать свои собственные комбинаторы вместо базового монадического интерфейса для каналов. Например, библиотека веб-маршрутов бумеранга использует набор комбинаторов для одновременной сборки кода, который выполняет преобразование (Route -> URL), и кода, который выполняет преобразование (URL -> Route), таким образом гарантируя, что они совместимы и обратны. друг друга.