문제

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

그래서 내 질문은 이렇습니다.Pipes의 소비자 부분에 대해 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