haxl-2.4.0.0: A Haskell library for efficient, concurrent, and concise data access.
Safe HaskellNone
LanguageHaskell2010

Haxl.Core.Monad

Description

The implementation of the Haxl monad. Most users should import Haxl.Core instead of importing this module directly.

Synopsis

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.

Constructors

GenHaxl 

Fields

Instances

Instances details
Monad (GenHaxl u w) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

(>>=) :: GenHaxl u w a -> (a -> GenHaxl u w b) -> GenHaxl u w b #

(>>) :: GenHaxl u w a -> GenHaxl u w b -> GenHaxl u w b #

return :: a -> GenHaxl u w a #

Functor (GenHaxl u w) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

fmap :: (a -> b) -> GenHaxl u w a -> GenHaxl u w b #

(<$) :: a -> GenHaxl u w b -> GenHaxl u w a #

MonadFail (GenHaxl u w) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

fail :: String -> GenHaxl u w a #

Applicative (GenHaxl u w) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

pure :: a -> GenHaxl u w a #

(<*>) :: GenHaxl u w (a -> b) -> GenHaxl u w a -> GenHaxl u w b #

liftA2 :: (a -> b -> c) -> GenHaxl u w a -> GenHaxl u w b -> GenHaxl u w c #

(*>) :: GenHaxl u w a -> GenHaxl u w b -> GenHaxl u w b #

(<*) :: GenHaxl u w a -> GenHaxl u w b -> GenHaxl u w a #

MonadThrow (GenHaxl u w) Source #

Since: 0.3.1.0

Instance details

Defined in Haxl.Core.Monad

Methods

throwM :: Exception e => e -> GenHaxl u w a #

MonadCatch (GenHaxl u w) Source #

Since: 0.3.1.0

Instance details

Defined in Haxl.Core.Monad

Methods

catch :: Exception e => GenHaxl u w a -> (e -> GenHaxl u w a) -> GenHaxl u w a #

Fractional a => Fractional (GenHaxl u w a) Source # 
Instance details

Defined in Haxl.Prelude

Methods

(/) :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

recip :: GenHaxl u w a -> GenHaxl u w a #

fromRational :: Rational -> GenHaxl u w a #

Num a => Num (GenHaxl u w a) Source # 
Instance details

Defined in Haxl.Prelude

Methods

(+) :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

(-) :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

(*) :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

negate :: GenHaxl u w a -> GenHaxl u w a #

abs :: GenHaxl u w a -> GenHaxl u w a #

signum :: GenHaxl u w a -> GenHaxl u w a #

fromInteger :: Integer -> GenHaxl u w a #

IsString a => IsString (GenHaxl u w a) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

fromString :: String -> GenHaxl u w a #

Semigroup a => Semigroup (GenHaxl u w a) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

(<>) :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

sconcat :: NonEmpty (GenHaxl u w a) -> GenHaxl u w a #

stimes :: Integral b => b -> GenHaxl u w a -> GenHaxl u w a #

Monoid a => Monoid (GenHaxl u w a) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

mempty :: GenHaxl u w a #

mappend :: GenHaxl u w a -> GenHaxl u w a -> GenHaxl u w a #

mconcat :: [GenHaxl u w a] -> GenHaxl u w a #

u1 ~ u2 => IfThenElse (GenHaxl u1 w Bool) (GenHaxl u2 w a) Source # 
Instance details

Defined in Haxl.Prelude

Methods

ifThenElse :: GenHaxl u1 w Bool -> GenHaxl u2 w a -> GenHaxl u2 w a -> GenHaxl u2 w a Source #

data Result u w a Source #

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.

Constructors

Done a 
Throw SomeException 
forall b. Blocked !(IVar u w b) (Cont u w a)

The IVar is what we are blocked on; Cont is the continuation. This might be wrapped further if we're nested inside multiple >>=, before finally being added to the IVar. Morally b -> GenHaxl u w a, but see IVar,

Instances

Instances details
Show a => Show (Result u w a) Source # 
Instance details

Defined in Haxl.Core.Monad

Methods

showsPrec :: Int -> Result u w a -> ShowS #

show :: Result u w a -> String #

showList :: [Result u w a] -> ShowS #

Writes (for debugging only)

data WriteTree w Source #

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.

Instances

Instances details
Show w => Show (WriteTree w) Source # 
Instance details

Defined in Haxl.Core.Monad

tellWrite :: w -> GenHaxl u w () Source #

write :: 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

data Cont u w a Source #

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

Constructors

Cont (GenHaxl u w a) 
forall b. (Cont u w b) :>>= (b -> GenHaxl u w a) 
forall b. (b -> a) :<$> (Cont u w b) 
Return (IVar u w a) 

toHaxl :: Cont u w a -> GenHaxl u w a Source #

IVar

newtype IVar u w a Source #

A synchronisation point. It either contains a value, or a list of computations waiting for the value.

Constructors

IVar 

Fields

data IVarContents u w a Source #

Constructors

IVarFull (ResultVal a w) 
IVarEmpty (JobList u w) 

newIVar :: IO (IVar u w a) Source #

newFullIVar :: ResultVal a w -> IO (IVar u w a) Source #

withCurrentCCS :: IVar u w a -> IO (IVar u w a) Source #

getIVar :: IVar u w a -> GenHaxl u w a Source #

putIVar :: IVar u w a -> ResultVal a w -> Env u w -> IO () Source #

ResultVal

data ResultVal a w Source #

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.

done :: Env u w -> ResultVal a w -> IO (Result u w a) 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.

Constructors

forall a. CompleteReq (ResultVal a w) !(IVar u w a) !Int64 

Env

data Env u w Source #

Constructors

Env 

Fields

  • dataCache :: !(HaxlDataCache u w)

    cached data fetches

  • memoCache :: !(HaxlDataCache u w)

    memoized computations

  • memoKey :: !CallId

    current running memo key

  • flags :: !Flags
     
  • userEnv :: u

    user-supplied data, retrievable with env

  • statsRef :: !(IORef Stats)

    statistics, collected according to the report level in flags.

  • statsBatchIdRef :: !(IORef Int)

    keeps track of a Unique ID for each batch dispatched with stats enabled, for aggregating after.

  • callIdRef :: !(IORef CallId)

    keeps track of a Unique ID for each fetch/memo.

  • profCurrent :: ProfileCurrent

    current profiling label, see withLabel

  • profRef :: !(IORef Profile)

    profiling data, collected according to the report level in flags.

  • states :: StateStore

    Data sources and other components can store their state in here. Items in this store must be instances of StateKey.

  • reqStoreRef :: !(IORef (RequestStore u))

    The set of requests that we have not submitted to data sources yet. Owned by the scheduler.

  • runQueueRef :: !(IORef (JobList u w))

    runnable computations. Things get added to here when we wake up a computation that was waiting for something. When the list is empty, either we're finished, or we're waiting for some data fetch to return.

  • submittedReqsRef :: !(IORef ReqCountMap)

    all outgone fetches which haven't yet returned. Entries are removed from this map as the fetches finish. This field is useful for tracking outgone fetches to detect downstream failures.

  • completions :: !(TVar [CompleteReq u w])

    Requests that have completed. Modified by data sources (via putResult) and the scheduler. Waiting for this list to become non-empty is how the scheduler blocks waiting for data fetches to return.

  • writeLogsRef :: !(IORef (WriteTree w))

    A log of all writes done as part of this haxl computation. Any haxl computation that needs to be memoized runs in its own environment so that we can get a hold of those writes and put them in the IVar associated with the compuatation.

  • writeLogsRefNoMemo :: !(IORef (WriteTree w))

    This is just a specialized version of writeLogsRef, where we put logs that user doesn't want memoized. This is a better alternative to doing arbitrary IO from a (memoized) Haxl computation.

  • dataCacheFetchFallback :: !(Maybe (DataCacheLookup w))

    Allows you to inject a DataCache lookup just before a dataFetch is dispatched. This is useful for injecting fetch results in testing.

data DataCacheItem u w a Source #

The data we carry around in the Haxl monad.

Constructors

DataCacheItem (IVar u w a) !CallId 

newtype DataCacheLookup w Source #

Constructors

DataCacheLookup (forall req a. Typeable (req a) => req a -> IO (Maybe (ResultVal a w))) 

type Caches u w = (HaxlDataCache u w, HaxlDataCache u w) Source #

caches :: Env u w -> Caches 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.

emptyEnv :: u -> IO (Env u w) Source #

A new, empty environment.

env :: (Env u w -> a) -> GenHaxl u w a Source #

Extracts data from the Env.

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

JobList

data JobList u w Source #

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.

Constructors

JobNil 
forall a. JobCons (Env u w) (GenHaxl u w a) !(IVar u w a) (JobList u w) 

addJob :: Env u w -> GenHaxl u w b -> IVar u w b -> IVar u w a -> IO () Source #

Exceptions

throw :: Exception e => e -> GenHaxl u w a Source #

Throw an exception in the Haxl monad

raise :: Exception e => Env u w -> e -> IO (Result u w a) Source #

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

try :: Exception e => GenHaxl u w a -> GenHaxl u w (Either e a) Source #

Returns Left e if the computation throws an exception e, or Right a if it returns a result a.

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.