-
Notifications
You must be signed in to change notification settings - Fork 28
/
2014-03-24-loops.hs
115 lines (94 loc) · 3.47 KB
/
2014-03-24-loops.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Applicative
import Data.IORef
import Control.Monad (join)
import Control.Concurrent.MVar
import Data.Map (Map)
import Data.Traversable (for)
import qualified Data.Map as Map
import Data.Functor.Compose
import Data.Foldable
getAllUsers :: IO [Bool]
getAllUsers = undefined
getUserById = undefined
doSomething = undefined
example1 :: IO ()
example1 = do
userIds <- getAllUsers
users <- for userIds $ \userId -> do
getUserById userId
doSomething users
data ExpandedEntity = ExpandedEntity Bool (Maybe Bool) (Maybe Bool)
getAllEntities :: IO [Bool]
getAllEntities = undefined
getEntityTypeById = undefined
getEntityOwnerById = undefined
entityTypeId :: Bool -> Int
entityTypeId = undefined
entityOwnerId :: Bool -> Int
entityOwnerId = undefined
better :: IO ()
better = do
entities <- getAllEntities
expandedEntities <- for entities $ \entity -> do
entityType <- getEntityTypeById (entityTypeId entity)
entityOwner <- getEntityOwnerById (entityOwnerId entity)
return $ ExpandedEntity entity entityType entityOwner
doSomething expandedEntities
getEntityTypesById = undefined
getEntityOwnersById = undefined
correct = do
entities <- getAllEntities
let entityTypeIds = map entityTypeId entities
entityOwnerIds = map entityOwnerId entities
entityTypes <- getEntityTypesById entityTypeIds
entityOwners <- getEntityOwnersById entityOwnerIds
doSomething $ flip map entities $ \entity ->
ExpandedEntity entity
(entityTypeId entity `lookup` entityTypes)
(entityOwnerId entity `lookup` entityOwners)
data Query k v = Query (IORef (Map k [MVar (Maybe v)]))
(Query keys) @? k = do
result <- newEmptyMVar
modifyIORef' keys (Map.insertWith (++) k [result])
return (takeMVar result)
getEntityById = undefined
ohNo :: IO ()
ohNo = do entity <- getEntityById 1
if entityOwnerId entity `mod` 2 == 0
then do owner <- getEntityOwnerById (entityOwnerId entity)
return (entity, Just owner)
else return (entity, Nothing)
return ()
newtype Querying a = Querying { unQuerying :: Compose IO IO a }
deriving (Functor, Applicative)
(@?!) :: (Ord k, Eq k) => Query k v -> k -> Querying (Maybe v)
(Query keys) @?! k = Querying $ Compose $ do
result <- newEmptyMVar
modifyIORef' keys (Map.insertWith (++) k [result])
return (takeMVar result)
--withQuery :: (Ord k, Eq k) => ([k] -> IO (Map.Map k v)) -> (Query k v -> Querying a) -> Querying a
withQuery runner k = Querying $ Compose $ do
-- Create a IORef to keep track of requested keys and result MVars
keysRef <- newIORef Map.empty
-- Run the first phase of the Querying action
getResponse <- getCompose $ unQuerying (k (Query keysRef))
-- Check which keys were requested and perform a query
keys <- readIORef keysRef
qResults <- runner (Map.keys keys)
-- Populate all MVars with results
flip Map.traverseWithKey keys $ \k mvars ->
for_ mvars $ \mvar ->
putMVar mvar (Map.lookup k qResults)
-- Return the IO action that reads from the MVar
return getResponse
runQuerying :: Querying a -> IO a
runQuerying (Querying (Compose io)) = join io
getUserAgesById :: [Int] -> IO (Map.Map Int Int)
getUserAgesById keys = do
putStrLn $ "Looking up " ++ show keys
return $ Map.fromList $ [(1, 1), (2, 2)]
example :: IO (Maybe Int)
example = runQuerying $
withQuery getUserAgesById $ \usersAgeById ->
liftA2 (+) <$> (usersAgeById @?! 1) <*> (usersAgeById @?! 2)