سؤال

أ 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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top