Skip to content

Commit 74a3874

Browse files
author
Simon Marlow
committed
Initial open source import
0 parents  commit 74a3874

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3928
-0
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.cabal-sandbox
2+
cabal.sandbox.config
3+
dist
4+
*~

Haxl/Core.hs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
-- Copyright (c) 2014, Facebook, Inc.
2+
-- All rights reserved.
3+
--
4+
-- This source code is distributed under the terms of a BSD license,
5+
-- found in the LICENSE file. An additional grant of patent rights can
6+
-- be found in the PATENTS file.
7+
8+
-- | Everything needed to define data sources and to invoke the
9+
-- engine. This module should not be imported by user code.
10+
module Haxl.Core
11+
( module Haxl.Core.Env
12+
, module Haxl.Core.Monad
13+
, module Haxl.Core.Types
14+
, module Haxl.Core.Exception
15+
, module Haxl.Core.StateStore
16+
, module Haxl.Core.Show1
17+
) where
18+
19+
import Haxl.Core.Env
20+
import Haxl.Core.Monad hiding (unsafeLiftIO {- Ask nicely to get this! -})
21+
import Haxl.Core.Types
22+
import Haxl.Core.Exception
23+
import Haxl.Core.Show1 (Show1(..))
24+
import Haxl.Core.StateStore

Haxl/Core/DataCache.hs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
-- Copyright (c) 2014, Facebook, Inc.
2+
-- All rights reserved.
3+
--
4+
-- This source code is distributed under the terms of a BSD license,
5+
-- found in the LICENSE file. An additional grant of patent rights can
6+
-- be found in the PATENTS file.
7+
8+
{-# LANGUAGE ExistentialQuantification #-}
9+
{-# LANGUAGE RankNTypes #-}
10+
{-# OPTIONS_GHC -fno-warn-orphans #-}
11+
12+
-- | A cache mapping data requests to their results.
13+
module Haxl.Core.DataCache
14+
( DataCache
15+
, empty
16+
, insert
17+
, lookup
18+
, showCache
19+
) where
20+
21+
import Data.HashMap.Strict (HashMap)
22+
import Data.Hashable
23+
import Prelude hiding (lookup)
24+
import Unsafe.Coerce
25+
import qualified Data.HashMap.Strict as HashMap
26+
import Data.Typeable.Internal
27+
import Data.Maybe
28+
import Control.Applicative hiding (empty)
29+
import Control.Exception
30+
31+
import Haxl.Core.Types
32+
33+
-- | The 'DataCache' maps things of type @f a@ to @'ResultVar' a@, for
34+
-- any @f@ and @a@ provided @f a@ is an instance of 'Typeable'. In
35+
-- practice @f a@ will be a request type parameterised by its result.
36+
--
37+
-- See the definition of 'ResultVar' for more details.
38+
39+
newtype DataCache = DataCache (HashMap TypeRep SubCache)
40+
41+
-- | The implementation is a two-level map: the outer level maps the
42+
-- types of requests to 'SubCache', which maps actual requests to their
43+
-- results. So each 'SubCache' contains requests of the same type.
44+
-- This works well because we only have to store the dictionaries for
45+
-- 'Hashable' and 'Eq' once per request type.
46+
data SubCache =
47+
forall req a . (Hashable (req a), Eq (req a), Show (req a), Show a) =>
48+
SubCache ! (HashMap (req a) (ResultVar a))
49+
-- NB. the inner HashMap is strict, to avoid building up
50+
-- a chain of thunks during repeated insertions.
51+
52+
-- | A new, empty 'DataCache'.
53+
empty :: DataCache
54+
empty = DataCache HashMap.empty
55+
56+
-- | Inserts a request-result pair into the 'DataCache'.
57+
insert
58+
:: (Hashable (r a), Typeable (r a), Eq (r a), Show (r a), Show a)
59+
=> r a
60+
-- ^ Request
61+
-> ResultVar a
62+
-- ^ Result
63+
-> DataCache
64+
-> DataCache
65+
66+
insert req result (DataCache m) =
67+
DataCache $
68+
HashMap.insertWith fn (typeOf req)
69+
(SubCache (HashMap.singleton req result)) m
70+
where
71+
fn (SubCache new) (SubCache old) =
72+
SubCache (unsafeCoerce new `HashMap.union` old)
73+
74+
-- | Looks up the cached result of a request.
75+
lookup
76+
:: Typeable (r a)
77+
=> r a
78+
-- ^ Request
79+
-> DataCache
80+
-> Maybe (ResultVar a)
81+
82+
lookup req (DataCache m) =
83+
case HashMap.lookup (typeOf req) m of
84+
Nothing -> Nothing
85+
Just (SubCache sc) ->
86+
unsafeCoerce (HashMap.lookup (unsafeCoerce req) sc)
87+
88+
-- | Dumps the contents of the cache, with requests and responses
89+
-- converted to 'String's using 'show'. The entries are grouped by
90+
-- 'TypeRep'.
91+
--
92+
showCache
93+
:: DataCache
94+
-> IO [(TypeRep, [(String, Either SomeException String)])]
95+
96+
showCache (DataCache cache) = mapM goSubCache (HashMap.toList cache)
97+
where
98+
goSubCache
99+
:: (TypeRep,SubCache)
100+
-> IO (TypeRep,[(String, Either SomeException String)])
101+
goSubCache (ty, SubCache hmap) = do
102+
elems <- catMaybes <$> mapM go (HashMap.toList hmap)
103+
return (ty, elems)
104+
105+
go :: (Show (req a), Show a)
106+
=> (req a, ResultVar a)
107+
-> IO (Maybe (String, Either SomeException String))
108+
go (req, rvar) = do
109+
maybe_r <- tryReadResult rvar
110+
case maybe_r of
111+
Nothing -> return Nothing
112+
Just (Left e) -> return (Just (show req, Left e))
113+
Just (Right result) -> return (Just (show req, Right (show result)))

Haxl/Core/Env.hs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
-- Copyright (c) 2014, Facebook, Inc.
2+
-- All rights reserved.
3+
--
4+
-- This source code is distributed under the terms of a BSD license,
5+
-- found in the LICENSE file. An additional grant of patent rights can
6+
-- be found in the PATENTS file.
7+
8+
{-# LANGUAGE OverloadedStrings #-}
9+
10+
-- | The Haxl monad environment.
11+
module Haxl.Core.Env
12+
( Env(..)
13+
, emptyEnv
14+
, initEnv
15+
, initEnvWithData
16+
, caches
17+
) where
18+
19+
import Haxl.Core.DataCache as DataCache
20+
import Haxl.Core.StateStore
21+
import Haxl.Core.Types
22+
23+
import Data.IORef
24+
25+
-- | The data we carry around in the Haxl monad.
26+
data Env u = Env
27+
{ cacheRef :: IORef DataCache -- cached data fetches
28+
, memoRef :: IORef DataCache -- memoized computations
29+
, flags :: Flags
30+
, userEnv :: u
31+
, statsRef :: IORef Stats
32+
, states :: StateStore
33+
-- ^ Data sources and other components can store their state in
34+
-- here. Items in this store must be instances of 'StateKey'.
35+
}
36+
37+
type Caches = (IORef DataCache, IORef DataCache)
38+
39+
caches :: Env u -> Caches
40+
caches env = (cacheRef env, memoRef env)
41+
42+
-- | Initialize an environment with a 'StateStore', an input map, a
43+
-- preexisting 'DataCache', and a seed for the random number generator.
44+
initEnvWithData :: StateStore -> u -> Caches -> IO (Env u)
45+
initEnvWithData states e (cref, mref) = do
46+
sref <- newIORef emptyStats
47+
return Env
48+
{ cacheRef = cref
49+
, memoRef = mref
50+
, flags = defaultFlags
51+
, userEnv = e
52+
, states = states
53+
, statsRef = sref
54+
}
55+
56+
-- | Initializes an environment with 'DataStates' and an input map.
57+
initEnv :: StateStore -> u -> IO (Env u)
58+
initEnv states e = do
59+
cref <- newIORef DataCache.empty
60+
mref <- newIORef DataCache.empty
61+
initEnvWithData states e (cref,mref)
62+
63+
-- | A new, empty environment.
64+
emptyEnv :: u -> IO (Env u)
65+
emptyEnv = initEnv stateEmpty

0 commit comments

Comments
 (0)