Question

UN Pipe peut être divisé en deux parties :le Générateur partie (yield) et le consommateur partie (await).

Si tu as un Pipe qui n'utilise que la moitié de son générateur et ne renvoie que () (ou ne revient jamais), alors il peut être représenté comme un "ListT bien fait".Il se trouve que MonadPlus peut être utilisé pour représenter quelque chose comme ListT-done-right.

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

Voici donc ma question:Existe-t-il un dual vers ListT et MonadPlus pour la partie consommateur de Pipes ?

Exigences:

  • Une pipe qui ne sert jamais yield, et ne renvoie que () (ou ne revient jamais), mais utilise await peut être représenté comme ceci "dual à ListT".
  • Le "dual de ListT" peut être généralisé au "dual de MonadPlus"
Était-ce utile?

La solution

Je pense que la réponse n'est pas de dualiser la classe de types "de type générateur", mais plutôt de l'étendre avec un simple Category instance équivalente à la await/(>~) catégorie de pipes.

Malheureusement, il n'existe aucun moyen d'organiser les variables de type pour que cela satisfasse les trois classes de types (MonadPlus, MonadTrans, et Category), je vais donc définir une nouvelle classe de type :

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

Les lois pour cette classe de types sont les lois des catégories :

await >~ f = f

f >~ await = f

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

Ensuite, vous pouvez implémenter les deux Consumersable Pipes une fois que vous avez cette classe de type supplémentaire :

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

Le plus difficile est de trouver comment procéder sans ajouter une nouvelle classe de type à base.Je préférerais réutiliser l'original Category tapez la classe si possible, ayant éventuellement await et (>~) soyez simplement des fonctions qui enveloppent votre type dans un nouveau type, utilisez le Category par exemple, puis déballer-le, mais je travaille toujours sur les détails de la façon de procéder.

Modifier:J'ai trouvé la solution.Définissez simplement le nouveau type suivant :

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

Alors n'importe quelle bibliothèque peut simplement implémenter un Category exemple pour leur type enveloppé dans le Consumer nouveau genre.

Vous obtiendrez alors une contrainte comme celle-ci à chaque fois que vous utiliserez await ou (>~):

cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top