2014-12-29 2 views
5

파이프에서 최대 50 개의 항목 그룹을 읽고 IO 작업으로 처리하려고합니다. (이것에 대한 사용 사례는 데이터베이스에 데이터를 삽입하려고하는데, 훨씬 효율적이기 때문에 전체 트랜잭션을 하나의 트랜잭션 내에서 수행하려고합니다.) 여기에 지금까지있어 무엇의 단순화 된 버전입니다 :파이프로 입력 끝을 감지하는 방법

삽입 할 항목의 수 (50)에 의해 분할 발생하지 않는 한 문제는, 내가 그리워하는거야 지금까지 내가 말할 수있다
type ExampleType = Int 

doSomething :: [ExampleType] -> IO() 
doSomething = undefined 

inGroupsOf50 :: Monad m => Producer ExampleType m() -> m() 
inGroupsOf50 input = 
    runEffect $ input >-> loop 
     where loop = do entries <- replicateM 50 await 
        lift $ doSomething entries --Insert a bunch all in one transaction 
        loop 

약간. replicateM 50 await 대신 실제로 원하는 것이 있다면 입력이 끝나면 최대 50 개 이하의 항목을 제공하지만 필자는이를 작성하는 방법을 알 수 없습니다.

저는 pipes-parse이 (가)보고있는 올바른 라이브러리 일 수 있다고 생각했습니다. draw는 유망한 서명이있는 것으로 보이지만 ... 지금까지 모든 비트가 내 머리 속에 들어 맞지 않습니다. 나는 producer을 가지고 있는데, 나는 consumer을 쓰고 있는데, 실제로는 그것이 parser이라는 개념과 관련이 없다.

답변

11

심지어 pipes-parse 이상이면 pipes-group을보고 싶을 것입니다. 특히,의 함수를 살펴 보자

-- this type is slightly specialized 
chunksOf 
    :: Monad m => 
    Int -> 
    Lens' (Producer a m x) (FreeT (Producer a m) m x) 

Lens' 비트는 아마도 무서운하지만 신속하게 제거 할 수있다 : 우리가 [0] 그래서

import Control.Lens (view) 

chunkIt :: Monad m => Int -> Producer a m x -> FreeT (Producer a m) m x 
chunkIt n = view (chunksOf n) 

지금 우리가 가지고 Producer a m xFreeT (Producer a m) m x에 변환 할 수 있다고 그 FreeT 비트로 무엇을 해야할지 알아냅니다. 특히, 우리는 free 패키지로 파고 기능 iterT

iterT 
    :: (Functor f, Monad m) => 
    (f (m a) -> m a) -> 
    (FreeT f m a -> m a) 

이 기능, iterT을 당겨의 우리 한 번에 FreeT 하나 "단계"를 "소비"수 있도록 할 것입니다. 이 문제를 이해하려면, 우리가 처음 특히 Producer a m

runChunk :: Monad m => 
      (Producer a m (m x)  -> m x) -> 
      (FreeT (Producer a m) m x -> m x) 
runChunk = iterT 

f을 대체 할 iterT의 유형을 전문으로합니다, runChunk가 할 수있는 "실행"는 Producer 가득 FreeT 그래서 우리가 어떻게 Producer a m (m x)를 변환하는 방법을 이야기로이야 m -action으로 이동하십시오. 은 더 익숙해 보일 수 있습니다. runChunk의 첫 번째 인수를 정의 할 때 Producer을 실행하기 만하면됩니다.이 경우에는 선택된 요소 수를 초과하지 않습니다.

그러나 유효한 반환 값은 무엇입니까 m x? '계속'입니다. 현재 다음에 오는 모든 청크는 입니다. 그래서, 예를 들어, 우리가 ProducerChar의의를 가지고 가정하자 우리는 3 자

후 인쇄 LINEBREAK 싶습니다
main :: IO() 
main = flip runChunk (chunkIt 3 input) $ \p -> _ 

이 시점에서 _ 구멍이 맥락에서의 pIO()을 입력있다 p :: Producer Char IO (IO())을 입력하십시오. for으로이 파이프를 소비하고 리턴 유형을 수집하고 (다시 계속), 개행을 내 보낸 다음 그 연속을 계속 실행할 수 있습니다.모든 조각이 어떻게 들어 맞는지 볼 일단

input :: Monad m => Producer Char m() 
input = each "abcdefghijklmnopqrstuvwxyz" 

main :: IO() 
main = flip runChunk (chunkIt 3 input) $ \p -> do 
    cont <- runEffect $ for p (lift . putChar) 
    putChar '\n' 
    cont 

내가 박람회의 조금 한 반면, 명확하게하기 위해


λ> main 
abc 
def 
ghi 
jkl 
mno 
pqr 
stu 
vwx 
yz 

을 원하는대로이 정확히 동작이 매우 간단 코드입니다. 전체 목록은 다음과 같습니다.

input :: Monad m => Producer Char m() 
input = each "abcdefghijklmnopqrstuvwxyz" 

main :: IO() 
main = flip iterT (input ^. chunksOf 3) $ \p -> do 
    cont <- runEffect $ for p $ \c -> do 
    lift (putChar c) 
    putChar '\n' 
    cont 

[0] 또한 약간 이상이지만 지금은 충분합니다.