patternMinor
Chaining method calls and "bubbling" the results
Viewed 0 times
callsthemethodandchainingbubblingresults
Problem
I need a way to build up a sort of stack of places to search for an item.
What I would like to do is to have each layer searched for an item, and if it is found at that level, then it should be loaded by each higher level (successively).
What I have so far is just a basic framework, but I was wondering if I was missing some more apt abstraction here in terms of command flow. Does anyone have any suggestions?
To see this in action, it should be sufficient to load up ghci and enter:
What I would like to do is to have each layer searched for an item, and if it is found at that level, then it should be loaded by each higher level (successively).
What I have so far is just a basic framework, but I was wondering if I was missing some more apt abstraction here in terms of command flow. Does anyone have any suggestions?
type Result = String
type Query = String
type Search = Query -> Maybe Result
type Load = Result -> Maybe Result
data LoadFunc = LoadFunc {
search :: Search,
load :: Load }
data RunFunc = RunFunc { runner :: [RunFunc] -> Maybe Result }
mkRunFunc :: Query -> LoadFunc -> RunFunc
mkRunFunc q lf = RunFunc r
where r (x:xs) =
case search lf q of
Nothing -> runner x xs >>= load lf
a -> a
r [] = search lf q
inMem :: LoadFunc
inMem = LoadFunc { search = search, load = load }
where search q = if q == "hi" then return $ "Found in mem." else Nothing
load = addToLog "Loaded to mem."
renDisk :: LoadFunc
renDisk = LoadFunc { search = search, load = load }
where search q = if q == "foo" then return $ "Found rendered." else Nothing
load = addToLog "Added rendered."
onDisk :: LoadFunc
onDisk = LoadFunc { search = search, load = load }
where search q = if q == "bar" then return $ "Found on disk." else Nothing
load = addToLog "Now on disk."
addToLog :: Result -> Result -> Maybe Result
addToLog next str = return $ str ++ (' ':next)
tester :: [LoadFunc] -> Query -> Maybe Result
tester caches q = runner first rem
where caches' = mk `fmap` caches
first = head caches'
rem = tail caches'
mk = mkRunFunc qTo see this in action, it should be sufficient to load up ghci and enter:
tester "bar" [inMem,renDisk,onDisk]Solution
How about making
Run using
This should give you the control flow you seek - as well as enough room for extension by replacing the monad as required. You could for example have
inMem & co "bracket" functions? Like follows:import Control.Monad.Writer
type M a = Writer String a
type LoadFunc a b = (a -> M b) -> a -> M b
type Query = String
type Result = ()
inMem :: LoadFunc Query Result
inMem _ "hi" = tell "Found in mem. "
inMem f q = f q >> tell "Added to mem. "
renDisk :: LoadFunc Query Result
renDisk _ "foo" = tell "Found rendered. "
renDisk f q = f q >> tell "Added rendered. "
onDisk :: LoadFunc Query Result
onDisk _ "bar" = tell "Found on disk. "
onDisk f q = f q >> tell "Now on disk. "
tester :: [LoadFunc Query Result] -> Query -> M Result
tester = foldr ($) notFound
where notFound q = tell "Not found!"Run using
runWriter $ tester [inMem,renDisk,onDisk] "bar"This should give you the control flow you seek - as well as enough room for extension by replacing the monad as required. You could for example have
notFound throw an exception using an Error monad instead (which would probably make sense).Code Snippets
import Control.Monad.Writer
type M a = Writer String a
type LoadFunc a b = (a -> M b) -> a -> M b
type Query = String
type Result = ()
inMem :: LoadFunc Query Result
inMem _ "hi" = tell "Found in mem. "
inMem f q = f q >> tell "Added to mem. "
renDisk :: LoadFunc Query Result
renDisk _ "foo" = tell "Found rendered. "
renDisk f q = f q >> tell "Added rendered. "
onDisk :: LoadFunc Query Result
onDisk _ "bar" = tell "Found on disk. "
onDisk f q = f q >> tell "Now on disk. "
tester :: [LoadFunc Query Result] -> Query -> M Result
tester = foldr ($) notFound
where notFound q = tell "Not found!"Context
StackExchange Code Review Q#6947, answer score: 4
Revisions (0)
No revisions yet.