{-# LANGUAGE ImpredicativeTypes #-}

-- | A version of monadic tasks with some support for non-determinism.
module Build.Task.MonadPlus (random, computeND, correctBuildValue) where

import Control.Monad

import Build.Task
import Build.Store

-- | An example of a non-deterministic task: generate a random number from a
-- specified interval.
random :: (Int, Int) -> Task MonadPlus k Int
random :: forall k. (Int, Int) -> Task MonadPlus k Int
random (Int
low, Int
high) = f Int -> (k -> f Int) -> f Int
forall a b. a -> b -> a
const (f Int -> (k -> f Int) -> f Int) -> f Int -> (k -> f Int) -> f Int
forall a b. (a -> b) -> a -> b
$ [f Int] -> f Int
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([f Int] -> f Int) -> [f Int] -> f Int
forall a b. (a -> b) -> a -> b
$ (Int -> f Int) -> [Int] -> [f Int]
forall a b. (a -> b) -> [a] -> [b]
map Int -> f Int
forall a. a -> f a
forall (f :: * -> *) a. Applicative f => a -> f a
pure [Int
low..Int
high]

-- | Run a non-deterministic task with a pure lookup function, listing all
-- possible results.
computePureND :: Task MonadPlus k v -> (k -> v) -> [v]
computePureND :: forall k v. Task MonadPlus k v -> (k -> v) -> [v]
computePureND Task MonadPlus k v
task k -> v
store = (k -> [v]) -> [v]
Task MonadPlus k v
task (v -> [v]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (v -> [v]) -> (k -> v) -> k -> [v]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. k -> v
store)

-- | Run a task in a given store.
computeND :: Task MonadPlus k v -> Store i k v -> [v]
computeND :: forall k v i. Task MonadPlus k v -> Store i k v -> [v]
computeND Task MonadPlus k v
task Store i k v
store = Task MonadPlus k v -> (k -> v) -> [v]
forall k v. Task MonadPlus k v -> (k -> v) -> [v]
computePureND (k -> f v) -> f v
Task MonadPlus k v
task (k -> Store i k v -> v
forall k i v. k -> Store i k v -> v
`getValue` Store i k v
store)

-- | Given a description of @tasks@, an initial @store@, and a @result@ produced
-- by running a build system on a target @key@, this function returns 'True' if
-- the @key@'s value is a possible result of running the associated task.
correctBuildValue :: Eq v => Tasks MonadPlus k v -> Store i k v -> Store i k v -> k -> Bool
correctBuildValue :: forall v k i.
Eq v =>
Tasks MonadPlus k v -> Store i k v -> Store i k v -> k -> Bool
correctBuildValue Tasks MonadPlus k v
tasks Store i k v
store Store i k v
result k
k = case Tasks MonadPlus k v
tasks k
k of
    Maybe (Task MonadPlus k v)
Nothing   -> k -> Store i k v -> v
forall k i v. k -> Store i k v -> v
getValue k
k Store i k v
result v -> v -> Bool
forall a. Eq a => a -> a -> Bool
== k -> Store i k v -> v
forall k i v. k -> Store i k v -> v
getValue k
k Store i k v
store
    Just Task MonadPlus k v
task -> k -> Store i k v -> v
forall k i v. k -> Store i k v -> v
getValue k
k Store i k v
result v -> [v] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Task MonadPlus k v -> Store i k v -> [v]
forall k v i. Task MonadPlus k v -> Store i k v -> [v]
computeND (k -> f v) -> f v
Task MonadPlus k v
task Store i k v
store