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 l
s in upstream input either l a
, either queued left l
or right a
coming upstream. we'll connect left l
s make pipe sees a
s coming upstream , yields b
s headed downstream.
at downstream end we'll push l
s left l
onto stack. yield
r
s 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