Подтвердить что ты не робот

Если MonadPlus является классом "генератор", то что такое "потребительский" класс?

A Pipe можно разбить на две части: генераторную часть (yield) и потребительскую часть (await).

Если у вас есть Pipe, который использует генератор только наполовину и возвращает () (или никогда не возвращается), тогда он может быть представлен как "ListT done right". Оказывается, что MonadPlus можно использовать, чтобы представлять что-либо вроде ListT-done-right.

http://www.reddit.com/r/haskell/comments/2bpsh7/a_simple_monadic_stream_library/cj7sqtw?context=3

Итак, мой вопрос заключается в следующем: существует ли двойное соединение с ListT и MonadPlus для потребительской части Pipes?

Требования:

  • Труба, которая никогда не использует yield и возвращает только () (или никогда не возвращается), но использовать await можно как это "двойное к ListT".
  • "dual to ListT" можно обобщить на "dual of MonadPlus"
4b9b3361

Ответ 1

Я думаю, что ответ заключается не в том, чтобы дуализировать "тип типа генератора" типа, а скорее, чтобы расширить его с помощью простого экземпляра Category, эквивалентного категории await/(>~) pipes.

К сожалению, нет способа упорядочить переменные типа, чтобы это удовлетворяло всем трем типам классов (MonadPlus, MonadTrans и Category), поэтому я буду определять новый тип типа:

{-# LANGUAGE KindSignatures #-}

import Control.Monad
import Control.Monad.Trans.Class

class Consumer (t :: * -> (* -> *) -> * -> *) where
    await :: t a m a
    (>~)  :: t a m b -> t b m c -> t a m c

Законами для этого типа являются законы категории:

await >~ f = f

f >~ await = f

(f >~ g) >~ h = f >~ (g >~ h)

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

printer :: (Show a, Monad (t a IO), MonadTrans (t a), Consumer t) => t a IO r
printer = do
    a <- await
    lift (print a)
    printer
{-
printer :: Show a => Consumer a IO r
printer = do
    a <- await
    lift (print a)
    printer
-}

cat :: (MonadPlus (t a m), Consumer t) => t a m a
cat = await `mplus` cat
{-
cat :: Monad m => Pipe a a m r
cat = do
    a <- await
    yield a
    cat
-}

debug :: (Show a, MonadPlus (t a IO), MonadTrans (t a), Consumer t) => t a IO a
debug = do
    a <- await
    lift (print a)
    return a `mplus` debug
{-
debug :: Show a => Pipe a a IO r
debug = do
    a <- await
    lift (print a)
    yield a
    debug
-}

taker :: (Consumer t, MonadPlus (t a m)) => Int -> t a m a
taker 0 = mzero
taker n = do
    a <- await
    return a `mplus` taker (n - 1)
{-
taker :: Monad m => Int -> Pipe a a m ()
taker 0 = return ()
taker n = do
    a <- await
    yield a
    taker (n - 1)
-}

Жесткая часть выясняет, как это сделать, не добавляя новый класс типа к base. Я предпочел бы использовать исходный класс типа Category, если возможно, возможно имея await и (>~), просто быть функциями, которые обертывают ваш тип в newtype, используют экземпляр Category, а затем разворачивают его, но я 'все еще разрабатывает особенности того, как это сделать.

Изменить: я нашел решение. Просто определите следующий тип newtype:

{-# LANGUAGE KindSignatures, FlexibleContexts #-}

import Control.Category
import Prelude hiding ((.), id)

newtype Consumer t m a b = Consumer { unConsumer :: t a m b }

await :: Category (Consumer t m) => t a m a
await = unConsumer id

(>~) :: Category (Consumer t m) => t a m b -> t b m c -> t a m c
f >~ g = unConsumer (Consumer f >>> Consumer g)

Тогда любая библиотека может просто реализовать экземпляр Category для своего типа, завернутого в Consumer newtype.

Тогда вы получите ограничение, подобное этому, в любое время, когда вы использовали await или (>~):

cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat