答えは、「ジェネレーターのような」型クラスを二重化することではなく、 の/カテゴリにCategory
相当する単純なインスタンスで拡張することだと思います。await
(>~)
pipes
MonadPlus
残念ながら、これを 3 つの型クラス ( 、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
s とs の両方を実装できます。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
可能であれば元の型クラスを再利用したいと思います。おそらく、型を newtype でラップし、インスタンスを使用してからアンラップする関数を持っawait
ているだけですが、それを行う方法の詳細についてはまだ取り組んでいます.(>~)
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)
次に、どのライブラリでも、 newtypeCategory
でラップされた型のインスタンスを実装できます。Consumer
await
次に、 orを使用するたびに、次のような制約が得られます(>~)
。
cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat