Domanda

Un Pipe può essere suddiviso in due parti: il generatore parte (yield) e consumatore parte (await).

Se si dispone di un Pipe che utilizza la sua metà del generatore, e restituisce solo () (o non ritorna mai), può essere rappresentato come un "ListT a destra". Si scopre che MonadPlus può essere utilizzato per rappresentare qualcosa come Listt-Done-Destra.

http://www.reddit.com/r/haskell/comments / 2BPSH7 / A_SIMPLE_MONEDIC_STREAM_LIBRARIA / CJ7SQTW? Context= 3

Quindi la mia domanda è questa: c'è un doppio da listere e a Monadplus per la porzione dei consumatori di tubi?

Requisiti:

    .
  • un tubo che non utilizza mai yield e restituisce solo () (o non ritorna mai), ma utilizza await può essere rappresentato come questo "Dual to Listt".
  • Il "Dual to Listt" può essere generalizzato al "Dual of Monadplus"
È stato utile?

Soluzione

Penso che la risposta non sia di dualizzare la classe di tipo "Generatore-simile", ma piuttosto per estenderla con una semplice istanza Category equivalente alla categoria await / (>~) di pipes.

Sfortunatamente, non c'è modo di organizzare le variabili di tipo per rendere questo soddisfare tutte e tre le classi di tipo (MonadPlus, MonadTrans e Category), quindi definirò una nuova classe di tipo:

{-# 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
.

Le leggi per questa classe di tipo sono le leggi sulla categoria:

await >~ f = f

f >~ await = f

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

Quindi è possibile implementare i generatori di Consumers e Pipes una volta che si dispone di questa classe di tipo aggiuntiva:

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)
-}
.

La parte difficile sta capire come farlo senza aggiungere una nuova classe di tipo a base. Preferirei riutilizzare la classe del tipo originale Category, eventualmente avendo generatori await e (>~) basta essere funzioni che avvolgono il tuo tipo in un newType, usa l'istanza Category, quindi non scaricarlo, ma sto ancora elaborando le specifiche di Come farlo.

Modifica: ho trovato la soluzione. Definisci solo la seguente 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)
.

Quindi qualsiasi libreria può semplicemente implementare un'istanza Category per il loro tipo avvolto nel Newtype Consumer.

Allora otterresti un vincolo come questo in qualsiasi momento hai usato await o (>~):

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top