-- | Support for multiple-output tasks.
module Build.Multi (Partition, multi) where

import Data.Maybe
import Build.Task

-- | Defines a set partition. For a function to be a valid partition,
--   if @f k == ks@, then:
--
-- * @k \in ks@
--
-- * @forall i \in ks . f i == ks@
type Partition k = k -> [k]

-- | Given a task description with individual multiple-output keys, compute its
-- "closure" supporting all possible combinations of keys.
multi :: Eq k => Partition k -> Tasks Applicative [k] [v] -> Tasks Applicative [k] [v]
multi partition tasks keys
    | k:_ <- keys, partition k == keys = tasks keys
    | otherwise = Just $ Task $ \fetch ->
        sequenceA [ select k <$> fetch (partition k) | k <- keys ]
  where
    select k = fromMaybe (error msg) . lookup k . zip (partition k)
    msg = "Partition invariants violated"