إذا كانت MonadPlus هي فئة "المولد"، فما هي فئة "المستهلك"؟
-
26-12-2019 - |
سؤال
أ Pipe
يمكن تقسيمها إلى قسمين:ال مولد كهرباء جزء (yield
) و ال مستهلك جزء (await
).
اذا كان لديك Pipe
الذي يستخدم نصف مولده فقط، ويعود فقط ()
(أو لا يعود أبدًا)، فيمكن تمثيله كـ "ListT
تم على أكمل وجه".لقد أتضح أن MonadPlus
يمكن استخدامه لتمثيل أي شيء مثل ListT-done-right.
http://www.reddit.com/r/haskell/comments/2bpsh7/a_simple_monadic_stream_library/cj7sqtw?context=3
لذا سؤالي هو هذا:هل هناك ثنائي لـ ListT وMonadPlus لجزء المستهلك من الأنابيب؟
متطلبات:
- الأنبوب الذي لا يستخدم أبدًا
yield
, ، ويعود فقط()
(أو لا يعود أبدًا)، لكنه يستخدمawait
يمكن تمثيله على أنه "ثنائي لـ ListT". - يمكن تعميم "المزدوج لـ ListT" على "المزدوج لـ MonadPlus"
المحلول
أعتقد أن الإجابة لا تتمثل في مضاعفة فئة النوع "الشبيهة بالمولد"، بل توسيعها بطريقة بسيطة 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
و (>~)
تكون مجرد وظائف تغلف نوعك بنوع جديد، استخدم Category
على سبيل المثال، ثم قم بإزالته، لكنني ما زلت أعمل على تحديد تفاصيل كيفية القيام بذلك.
يحرر:لقد وجدت الحل.ما عليك سوى تحديد النوع الجديد التالي:
{-# 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
نوع جديد.
ثم ستحصل على قيد مثل هذا في أي وقت استخدمته await
أو (>~)
:
cat :: (MonadPlus (t a m), Category (Consumer t m)) => t a m a
cat = await `mplus` cat