2014-12-29 18 views
5

Tôi đang cố gắng đọc một nhóm tối đa 50 mục từ một đường ống và xử lý chúng trong một hành động IO cùng một lúc. (Trường hợp sử dụng cho điều này là tôi đang cố gắng chèn dữ liệu vào một cơ sở dữ liệu và tôi muốn thực hiện toàn bộ lô trong một giao dịch vì nó hiệu quả hơn nhiều). Dưới đây là một phiên bản đơn giản của những gì tôi đã có cho đến thời điểm này:Làm thế nào để phát hiện kết thúc đầu vào với đường ống

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 

Vấn đề là tôi có thể nói, trừ khi số lượng mục chèn vào sẽ chia cho 50, tôi sẽ bỏ lỡ một số. Những gì tôi thực sự muốn thay vì replicateM 50 await là cái gì đó mang lại cho tôi lên đến 50 mục hoặc ít hơn nếu đầu vào kết thúc nhưng tôi không thể tìm ra cách để viết nó.

Tôi đã nghĩ rằng pipes-parse có thể là thư viện phù hợp để xem. draw có vẻ có chữ ký đầy hứa hẹn ... nhưng cho đến nay tất cả các bit không khớp với nhau trong đầu tôi. Tôi có một số producer, tôi đang viết một số consumer và tôi không thực sự nhận được cách liên quan đến khái niệm về một parser.

Trả lời

11

Thậm chí nhiều hơn pipes-parse bạn rất có thể muốn xem pipes-group. Đặc biệt, chúng ta hãy xem xét các chức năng

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

Bit Lens' có lẽ là đáng sợ nhưng có thể được loại bỏ một cách nhanh chóng: nó khẳng định rằng chúng tôi có thể chuyển đổi Producer a m x-FreeT (Producer a m) m x [0]

import Control.Lens (view) 

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

Vì vậy, bây giờ chúng ta phải tìm ra những gì cần làm với bit FreeT. Đặc biệt, chúng ta sẽ muốn thâm nhập vào các gói free và rút khỏi chức năng iterT

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

Chức năng này, iterT, hãy cho chúng tôi "tiêu thụ" một "bước" FreeT tại một thời điểm. Để hiểu điều này, đầu tiên chúng tôi sẽ chuyên các loại iterT để thay thế f với Producer a m

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

Đặc biệt, runChunk có thể "chạy" một FreeT đầy Producer s chừng nào chúng ta nói với nó như thế nào để chuyển đổi một Producer a m (m x) vào một số m -action. Điều này có thể bắt đầu trông quen thuộc hơn. Khi chúng ta xác định đối số đầu tiên của runChunk, chúng ta chỉ cần thực hiện một Producer, trong trường hợp này, sẽ không có nhiều hơn số lượng phần tử đã chọn.

Nhưng điều gì xảy ra với giá trị trả lại hiệu quả m x? Đó là "sự tiếp tục", ví dụ: tất cả các khối đến sau sau hiện tại bạn đang viết. Vì vậy, ví dụ, chúng ta hãy giả sử chúng ta đã có một Producer của Char s và chúng tôi muốn in và linebreak sau 3 ký tự

main :: IO() 
main = flip runChunk (chunkIt 3 input) $ \p -> _ 

Các _ lỗ vào thời điểm này có kiểu IO() với p trong bối cảnh tại nhập p :: Producer Char IO (IO()). Chúng ta có thể tiêu thụ đường ống này với for, thu thập kiểu trả về của nó (đó là sự tiếp tục, một lần nữa), phát ra một dòng mới, và sau đó chạy tiếp tục.

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 

Và đây cư xử chính xác như mong muốn

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

Để được rõ ràng, trong khi tôi đã làm một chút của triển lãm, đây là mã khá đơn giản khi bạn xem làm thế nào tất cả các mảnh phù hợp với nhau. Đây là toàn bộ danh sách:

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] Ngoài ra còn hơn một chút, nhưng hiện tại vẫn đủ.

Các vấn đề liên quan