i'm trying write webscraper using pipes , i've come part of following scraped links. have process function downloads url, finds links, , yields them.
process :: pipe item item (statet cstate io) () .... (each links) yield .... now want how recursively follow these links, threading statet through. realize there doing more idiomatic using single pipe bulk of scraper (especially start adding more features), i'm open suggestions. i'm going have rethink design when consider multithreading w/ shared state anyways.
you can connect pipe b m r side-effect through m parameter, swaps out monad pipe operating over. can use requeue links connecting downstream end of pipe pipe sticks links in queue , connecting upstream end of pipe pipe reads links queue.
our goal write
import pipes loopleft :: monad m => pipe (either l a) (either l b) m r -> pipe b m r we'll take pipe downstream output, either l b, either left l send upstream or right b send downstream, , send ls in upstream input either l a, either queued left l or right a coming upstream. we'll connect left ls make pipe sees as coming upstream , yields bs headed downstream.
at downstream end we'll push ls left l onto stack. yield rs right r downstream.
import control.monad import control.monad.trans.state pushleft :: monad m => pipe (either l a) (statet [l] m) r pushleft = forever $ o <- await case o of right -> yield left l -> stack <- lift lift $ put (l : stack) at upstream end we'll on top of stack yield. if there isn't one, we'll await value upstream , yield it.
popleft :: monad m => pipe (either l a) (statet [l] m) r popleft = forever $ stack <- lift case stack of [] -> await >>= yield . right (x : xs) -> lift $ put xs yield (left x) now can write loopleft. compose upstream , downstream pipes pipe composition popleft >-> hoist lift p >-> pushleft. hoist lift turns pipe b m r pipe b (t m) r. distribute turns pipe b (t m) r t (pipe b m) r. pipe b m r run whole statet computation starting empty stack []. in pipes.lift there's nice name evalstatep combination of evalstatet , distribute.
import pipes.lift loopleft :: monad m => pipe (either l a) (either l b) m r -> pipe b m r loopleft p = flip evalstatet [] . distribute $ popleft >-> hoist lift p >-> pushleft
Comments
Post a Comment