Frage

A Pipe kann in zwei Teile zerlegt werden:Die Generator Teil (yield) und das Verbraucher Teil (await).

Wenn Sie eine haben Pipe das nutzt nur die Generatorhälfte und gibt nur zurück () (oder nie zurückkehrt), dann kann es dargestellt werden als „ListT richtig gemacht".Es stellt sich heraus, dass MonadPlus kann verwendet werden, um etwas wie ListT-done-right darzustellen.

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

Meine Frage lautet also:Gibt es ein Dual zu ListT und zu MonadPlus für den Verbraucherteil von Pipes?

Anforderungen:

  • Eine Pfeife, die nie benutzt wird yield, und kehrt nur zurück () (oder kehrt nie zurück), wird aber verwendet await kann als „Dual zu ListT“ dargestellt werden.
  • Das „Dual zu ListT“ kann auf das „Dual von MonadPlus“ verallgemeinert werden.
War es hilfreich?

Lösung

Ich denke, die Antwort besteht nicht darin, die „generatorartige“ Typklasse zu dualisieren, sondern sie um eine einfache zu erweitern Category Instanz äquivalent zu await/(>~) Kategorie von pipes.

Leider gibt es keine Möglichkeit, die Typvariablen so anzuordnen, dass alle drei Typklassen erfüllt werden (MonadPlus, MonadTrans, Und Category), also werde ich eine neue Typklasse definieren:

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

Die Gesetze für diese Typklasse sind die Kategoriegesetze:

await >~ f = f

f >~ await = f

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

Dann können Sie beides umsetzen Consumers und Pipes, sobald Sie diese zusätzliche Typklasse haben:

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

Der schwierige Teil besteht darin, herauszufinden, wie man das macht, ohne eine neue Typklasse hinzuzufügen base.Ich würde lieber das Original wiederverwenden Category Typklasse wenn möglich, möglicherweise mit await Und (>~) Seien Sie einfach Funktionen, die Ihren Typ in einen neuen Typ einschließen. Verwenden Sie die Category Beispiel, und packen Sie es dann aus, aber ich arbeite immer noch an den Einzelheiten, wie das geht.

Bearbeiten:Ich habe die Lösung gefunden.Definieren Sie einfach den folgenden neuen Typ:

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

Dann kann jede Bibliothek einfach a implementieren Category Beispiel für ihren Typ, der in die eingebettet ist Consumer neuer Typ.

Dann würden Sie bei jeder Verwendung eine solche Einschränkung erhalten await oder (>~):

cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top