{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnicodeSyntax #-} {-| There are several tasks for which a user may wish to use LogicGrowsOnTrees, such as gathering up all the results in a tree or stopping as soon as the first result is found. Because almost all of the infrastructure required for these different modes is the same, rather than creating a different system for each mode we instead re-use the same system but pass around a mode parameter that dictates its behavior at various points as well as some of the types in the system. 'ExplorationMode' is defined using a GADT where each constructor has a different argument for `ExplorationMode`\'s type parameter; this was done so that type families can be used to specialized types depending on the constructor. -} module LogicGrowsOnTrees.Parallel.ExplorationMode ( -- * Types ExplorationMode(..) , AllMode , FirstMode , FoundModeUsingPull , FoundModeUsingPush -- * Type-classes , HasExplorationMode(..) -- * Type families , ResultFor , ProgressFor , FinalResultFor , WorkerIntermediateValueFor , WorkerFinishedProgressFor -- * Functions , checkpointFromIntermediateProgress , initialProgress , initialWorkerIntermediateValue ) where import Data.Monoid import LogicGrowsOnTrees.Checkpoint -------------------------------------------------------------------------------- ------------------------------------ Types ------------------------------------- -------------------------------------------------------------------------------- {-| A type indicating the mode of the exploration. Note that this is a GADT for which the type parameter is unique to each constructor in order to allow associated types to be specialized based on the value. Unfortunately Haddock does not seem to support documenting the constructors of a GADT, so the documentation for each constructor is located at the type it is tagged with, all of which are defined after the 'ExplorationMode' type. -} data ExplorationMode exploration_mode where AllMode :: Monoid result ⇒ ExplorationMode (AllMode result) FirstMode :: ExplorationMode (FirstMode result) FoundModeUsingPull :: Monoid result ⇒ (result → Bool) → ExplorationMode (FoundModeUsingPull result) FoundModeUsingPush :: Monoid result ⇒ (result → Bool) → ExplorationMode (FoundModeUsingPush result) {-| Explore the entire tree and sum the results in all of the leaves. -} data AllMode result {-| Explore the tree until a result is found, and if so then stop. -} data FirstMode result {-| Explore the tree, summing the results, until a condition has been met; `Pull` means that each worker's results will be kept and summed locally until a request for them has been received from the supervisor, which means that there might be a period of time where the collectively found results meet the condition but the system is unaware of this as they are scattered amongst the workers. NOTE: If you use this mode then you are responsible for ensuring that a global progress update happens on a regular basis in order to pull the results in from the workers and check to see if the condition has been met; if you do not do this then the run will not terminate until the tree has been fully explored. -} data FoundModeUsingPull result {-| Same as 'FoundModeUsingPull', but pushes each result to the supervisor as it is found rather than summing them in the worker until they are requested by the supervisor, which guarantees that the system will recognize when the condition has been met as soon as final result needed was found but has the downside that if there are a large number of results needed then sending each one could be much more costly then summing them locally and sending the current total on a regular basis to the supervisor. -} data FoundModeUsingPush result -------------------------------------------------------------------------------- --------------------------------- Type-classes --------------------------------- -------------------------------------------------------------------------------- {-| This class indicates that a monad has information about the current exploration mode tag type that can be extracted from it. -} class HasExplorationMode (monad :: * → *) where type ExplorationModeFor monad :: * -------------------------------------------------------------------------------- -------------------------------- Type families --------------------------------- -------------------------------------------------------------------------------- {- $families The type families in this section allow the types to be used at various places to be specialized based on the current exploration mode. -} {-| The result type of the tree, i.e. the type of values at the leaves. -} type family ResultFor exploration_mode :: * type instance ResultFor (AllMode result) = result type instance ResultFor (FirstMode result) = result type instance ResultFor (FoundModeUsingPull result) = result type instance ResultFor (FoundModeUsingPush result) = result {-| The type of progress, which keeps track of how much of the tree has already been explored. -} type family ProgressFor exploration_mode :: * type instance ProgressFor (AllMode result) = Progress result type instance ProgressFor (FirstMode result) = Checkpoint type instance ProgressFor (FoundModeUsingPull result) = Progress result type instance ProgressFor (FoundModeUsingPush result) = Progress result {-| The type of the final result of exploring the tree. -} type family FinalResultFor exploration_mode :: * type instance FinalResultFor (AllMode result) = result type instance FinalResultFor (FirstMode result) = Maybe (Progress result) type instance FinalResultFor (FoundModeUsingPull result) = Either result (Progress result) type instance FinalResultFor (FoundModeUsingPush result) = Either result (Progress result) {-| The type of the intermediate value being maintained internally by the worker. -} type family WorkerIntermediateValueFor exploration_mode :: * type instance WorkerIntermediateValueFor (AllMode result) = result type instance WorkerIntermediateValueFor (FirstMode result) = () type instance WorkerIntermediateValueFor (FoundModeUsingPull result) = result type instance WorkerIntermediateValueFor (FoundModeUsingPush result) = () {-| The progress type returned by a worker that has finished. -} type family WorkerFinishedProgressFor exploration_mode :: * type instance WorkerFinishedProgressFor (AllMode result) = Progress result type instance WorkerFinishedProgressFor (FirstMode result) = Progress (Maybe result) type instance WorkerFinishedProgressFor (FoundModeUsingPull result) = Progress result type instance WorkerFinishedProgressFor (FoundModeUsingPush result) = Progress result -------------------------------------------------------------------------------- ---------------------------------- Functions ----------------------------------- -------------------------------------------------------------------------------- {-| Extracts the 'Checkpoint' component from a progress value. -} checkpointFromIntermediateProgress :: ExplorationMode exploration_mode → ProgressFor exploration_mode → Checkpoint checkpointFromIntermediateProgress AllMode = progressCheckpoint checkpointFromIntermediateProgress FirstMode = id checkpointFromIntermediateProgress (FoundModeUsingPull _) = progressCheckpoint checkpointFromIntermediateProgress (FoundModeUsingPush _) = progressCheckpoint {-| The initial progress at the start of the exploration. -} initialProgress :: ExplorationMode exploration_mode → ProgressFor exploration_mode initialProgress AllMode = mempty initialProgress FirstMode = mempty initialProgress (FoundModeUsingPull _) = mempty initialProgress (FoundModeUsingPush _) = mempty {-| The initial intermediate value for the worker. -} initialWorkerIntermediateValue :: ExplorationMode exploration_mode → WorkerIntermediateValueFor exploration_mode initialWorkerIntermediateValue AllMode = mempty initialWorkerIntermediateValue FirstMode = () initialWorkerIntermediateValue (FoundModeUsingPull _) = mempty initialWorkerIntermediateValue (FoundModeUsingPush _) = ()