Safe Haskell | None |
---|---|
Language | Haskell2010 |
The implementation of the Haxl
monad. Most users should
import Haxl.Core instead of importing this module directly.
Synopsis
- newtype GenHaxl u w a = GenHaxl {}
- data Result u w a
- data WriteTree w
- = NilWrites
- | SomeWrite w
- | MergeWrites (WriteTree w) (WriteTree w)
- tellWrite :: w -> GenHaxl u w ()
- tellWriteNoMemo :: w -> GenHaxl u w ()
- write :: WriteTree w -> GenHaxl u w ()
- writeNoMemo :: WriteTree w -> GenHaxl u w ()
- flattenWT :: WriteTree w -> [w]
- appendWTs :: WriteTree w -> WriteTree w -> WriteTree w
- mbModifyWLRef :: WriteTree w -> IORef (WriteTree w) -> IO ()
- mapWrites :: (w -> w) -> GenHaxl u w a -> GenHaxl u w a
- data Cont u w a
- toHaxl :: Cont u w a -> GenHaxl u w a
- newtype IVar u w a = IVar {
- ivarRef :: IORef (IVarContents u w a)
- data IVarContents u w a
- newIVar :: IO (IVar u w a)
- newFullIVar :: ResultVal a w -> IO (IVar u w a)
- withCurrentCCS :: IVar u w a -> IO (IVar u w a)
- getIVar :: IVar u w a -> GenHaxl u w a
- getIVarWithWrites :: IVar u w a -> GenHaxl u w a
- putIVar :: IVar u w a -> ResultVal a w -> Env u w -> IO ()
- data ResultVal a w
- = Ok a (WriteTree w)
- | ThrowHaxl SomeException (WriteTree w)
- | ThrowIO SomeException
- done :: Env u w -> ResultVal a w -> IO (Result u w a)
- eitherToResult :: Either SomeException a -> ResultVal a w
- eitherToResultThrowIO :: Either SomeException a -> ResultVal a w
- data CompleteReq u w = forall a. CompleteReq (ResultVal a w) !(IVar u w a) !Int64
- data Env u w = Env {
- dataCache :: !(HaxlDataCache u w)
- memoCache :: !(HaxlDataCache u w)
- memoKey :: !CallId
- flags :: !Flags
- userEnv :: u
- statsRef :: !(IORef Stats)
- statsBatchIdRef :: !(IORef Int)
- callIdRef :: !(IORef CallId)
- profCurrent :: ProfileCurrent
- profRef :: !(IORef Profile)
- states :: StateStore
- reqStoreRef :: !(IORef (RequestStore u))
- runQueueRef :: !(IORef (JobList u w))
- submittedReqsRef :: !(IORef ReqCountMap)
- completions :: !(TVar [CompleteReq u w])
- writeLogsRef :: !(IORef (WriteTree w))
- writeLogsRefNoMemo :: !(IORef (WriteTree w))
- dataCacheFetchFallback :: !(Maybe (DataCacheLookup w))
- data DataCacheItem u w a = DataCacheItem (IVar u w a) !CallId
- newtype DataCacheLookup w = DataCacheLookup (forall req a. Typeable (req a) => req a -> IO (Maybe (ResultVal a w)))
- type HaxlDataCache u w = DataCache (DataCacheItem u w)
- type Caches u w = (HaxlDataCache u w, HaxlDataCache u w)
- caches :: Env u w -> Caches u w
- initEnvWithData :: StateStore -> u -> Caches u w -> IO (Env u w)
- initEnv :: StateStore -> u -> IO (Env u w)
- emptyEnv :: u -> IO (Env u w)
- env :: (Env u w -> a) -> GenHaxl u w a
- withEnv :: Env u w -> GenHaxl u w a -> GenHaxl u w a
- nextCallId :: Env u w -> IO CallId
- sanitizeEnv :: Env u w -> IO (Env u w)
- data ProfileCurrent = ProfileCurrent {}
- data JobList u w
- appendJobList :: JobList u w -> JobList u w -> JobList u w
- lengthJobList :: JobList u w -> Int
- addJob :: Env u w -> GenHaxl u w b -> IVar u w b -> IVar u w a -> IO ()
- throw :: Exception e => e -> GenHaxl u w a
- raise :: Exception e => Env u w -> e -> IO (Result u w a)
- catch :: Exception e => GenHaxl u w a -> (e -> GenHaxl u w a) -> GenHaxl u w a
- catchIf :: Exception e => (e -> Bool) -> GenHaxl u w a -> (e -> GenHaxl u w a) -> GenHaxl u w a
- try :: Exception e => GenHaxl u w a -> GenHaxl u w (Either e a)
- tryToHaxlException :: GenHaxl u w a -> GenHaxl u w (Either HaxlException a)
- dumpCacheAsHaskell :: GenHaxl u w String
- dumpCacheAsHaskellFn :: String -> String -> String -> GenHaxl u w String
- unsafeLiftIO :: IO a -> GenHaxl u w a
- unsafeToHaxlException :: GenHaxl u w a -> GenHaxl u w a
The monad
newtype GenHaxl u w a Source #
The Haxl monad, which does several things:
- It is a reader monad for
Env
, which contains the current state of the scheduler, including unfetched requests and the run queue of computations. - It is a writer monad for
WriteTree
. These can be used to do arbitrary "logs" from any Haxl computation. These are better than doing arbitrary IO from a Haxl computation as these writes also get memoized if the Haxl computation associated with them is memoized. Now if this memoized computation is run again, you'll get the writes twice.
Instances
The result of a computation is either Done
with a value, Throw
with an exception, or Blocked
on the result of a data fetch with
a continuation.
Writes (for debugging only)
A tree of writes done during a Haxl computation. We could use a simple list, but this allows us to avoid multiple mappends when concatenating writes from two haxl computations.
Users should try to treat this data type as opaque, and prefer
to use flattenWT
to get a simple list of writes from a WriteTree
.
NilWrites | |
SomeWrite w | |
MergeWrites (WriteTree w) (WriteTree w) |
tellWriteNoMemo :: w -> GenHaxl u w () Source #
writeNoMemo :: WriteTree w -> GenHaxl u w () Source #
mapWrites :: (w -> w) -> GenHaxl u w a -> GenHaxl u w a Source #
Runs the Haxl computation, transforming the writes within using the provided function.
Memoization behavior is unchanged, meaning if a memoized computation is run
once inside mapWrites
and then once without, the writes from the second run
will NOT be transformed.
Cont
A data representation of a Haxl continuation. This is to avoid repeatedly traversing a left-biased tree in a continuation, leading O(n^2) complexity for some pathalogical cases - see the "seql" benchmark in tests/MonadBench.hs. See "A Smart View on Datatypes", Jaskelioff/Rivas, ICFP'15
IVar
A synchronisation point. It either contains a value, or a list of computations waiting for the value.
IVar | |
|
getIVarWithWrites :: IVar u w a -> GenHaxl u w a Source #
ResultVal
The contents of a full IVar. We have to distinguish exceptions thrown in the IO monad from exceptions thrown in the Haxl monad, so that when the result is fetched using getIVar, we can throw the exception in the right way.
Ok a (WriteTree w) | |
ThrowHaxl SomeException (WriteTree w) | |
ThrowIO SomeException |
eitherToResult :: Either SomeException a -> ResultVal a w Source #
eitherToResultThrowIO :: Either SomeException a -> ResultVal a w Source #
CompleteReq
data CompleteReq u w Source #
A completed request from a data source, containing the result,
and the IVar
representing the blocked computations. The job of a
data source is just to add these to a queue (completions
) using
putResult
; the scheduler collects them from the queue and unblocks
the relevant computations.
forall a. CompleteReq (ResultVal a w) !(IVar u w a) !Int64 |
Env
Env | |
|
data DataCacheItem u w a Source #
The data we carry around in the Haxl monad.
DataCacheItem (IVar u w a) !CallId |
newtype DataCacheLookup w Source #
DataCacheLookup (forall req a. Typeable (req a) => req a -> IO (Maybe (ResultVal a w))) |
type HaxlDataCache u w = DataCache (DataCacheItem u w) Source #
type Caches u w = (HaxlDataCache u w, HaxlDataCache u w) Source #
initEnvWithData :: StateStore -> u -> Caches u w -> IO (Env u w) Source #
Initialize an environment with a StateStore
, an input map, a
preexisting DataCache
, and a seed for the random number generator.
initEnv :: StateStore -> u -> IO (Env u w) Source #
Initializes an environment with StateStore
and an input map.
withEnv :: Env u w -> GenHaxl u w a -> GenHaxl u w a Source #
Returns a version of the Haxl computation which always uses the
provided Env
, ignoring the one specified by runHaxl
.
sanitizeEnv :: Env u w -> IO (Env u w) Source #
If you're using the env from a failed Haxl computation in a second Haxl computation, it is recommended to sanitize the Env to remove all empty IVars - especially if it's possible the first Haxl computation could've been interrupted via an async exception. This is because if the Haxl computation was interrupted by an exception, it's possible that there are entries in the cache which are still blocked, while the results from outgone fetches have been discarded.
Profiling
data ProfileCurrent Source #
JobList
A list of computations together with the IVar into which they should put their result.
This could be an ordinary list, but the optimised representation saves space and time.
lengthJobList :: JobList u w -> Int Source #
Exceptions
catch :: Exception e => GenHaxl u w a -> (e -> GenHaxl u w a) -> GenHaxl u w a Source #
Catch an exception in the Haxl monad
catchIf :: Exception e => (e -> Bool) -> GenHaxl u w a -> (e -> GenHaxl u w a) -> GenHaxl u w a Source #
Catch exceptions that satisfy a predicate
tryToHaxlException :: GenHaxl u w a -> GenHaxl u w (Either HaxlException a) Source #
Like try
, but lifts all exceptions into the HaxlException
hierarchy. Uses unsafeToHaxlException
internally. Typically
this is used at the top level of a Haxl computation, to ensure that
all exceptions are caught.
Dumping the cache
dumpCacheAsHaskell :: GenHaxl u w String Source #
Dump the contents of the cache as Haskell code that, when compiled and run, will recreate the same cache contents. For example, the generated code looks something like this:
loadCache :: GenHaxl u w () loadCache = do cacheRequest (ListWombats 3) (Right ([1,2,3])) cacheRequest (CountAardvarks "abcabc") (Right (2))
dumpCacheAsHaskellFn :: String -> String -> String -> GenHaxl u w String Source #
Dump the contents of the cache as Haskell code that, when compiled and run, will recreate the same cache contents. Does not take into account the writes done as part of the computation.
Takes the name and type for the resulting function as arguments.
Also takes the cacheFn to use, we can use either cacheRequest
or
dupableCacheRequest
.
CallGraph
Unsafe operations
unsafeLiftIO :: IO a -> GenHaxl u w a Source #
Under ordinary circumstances this is unnecessary; users of the Haxl monad should generally not perform arbitrary IO.
unsafeToHaxlException :: GenHaxl u w a -> GenHaxl u w a Source #
Convert exceptions in the underlying IO monad to exceptions in the Haxl monad. This is morally unsafe, because you could then catch those exceptions in Haxl and observe the underlying execution order. Not to be exposed to user code.
Note: this function does not catch async exceptions. This is a flaw in Haxl where it can sometimes leave the environment in a bad state when async exceptions are thrown (for example the cache may think a fetch is happening but the exception has stopped it). TODO would be to make Haxl async exception safe and then remove the rethrowAsyncExceptions below, but for now this is safer to avoid bugs. Additionally this would not protect you from async exceptions thrown while executing code in the scheduler, and so relying on this function to catch all async exceptions would be ambitious at best.