{-# LANGUAGE DataKinds, UndecidableInstances, FlexibleContexts #-}
{-# LANGUAGE TypeFamilies, TypeOperators #-}

-- | This module provides the top level API for Souffle related operations.
--   It makes use of Haskell's powerful typesystem to make certain invalid states
--   impossible to represent. It does this with a small type level DSL for
--   describing properties of the Datalog program (see the 'Program' and 'Fact'
--   typeclasses for more information).
--
--   The Souffle operations are exposed via 2 mtl-style interfaces
--   (see `MonadSouffle` and `MonadSouffleFileIO`) that allows them to be
--   integrated with existing monad transformer stacks.
--
--   This module also contains some helper type families for additional
--   type safety and user-friendly error messages.
module Language.Souffle.Class
  ( Program(..)
  , Fact(..)
  , Marshal.Marshal(..)
  , Direction(..)
  , ContainsInputFact
  , ContainsOutputFact
  , ContainsFact
  , MonadSouffle(..)
  , MonadSouffleFileIO(..)
  ) where

import Prelude hiding ( init )

import Control.Monad.Except
import Control.Monad.RWS.Strict
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Data.Proxy
import Data.Kind
import Data.Word
import qualified Language.Souffle.Marshal as Marshal
import Type.Errors.Pretty


-- | A helper type family for checking if a specific Souffle `Program` contains
--   a certain `Fact`. Additionally, it also checks if the fact is marked as
--   either `Input` or `InputOutput`. This constraint will generate a
--   user-friendly type error if these conditions are not met.
type family ContainsInputFact prog fact :: Constraint where
  ContainsInputFact prog fact = (ContainsFact prog fact, IsInput fact (FactDirection fact))

-- | A helper type family for checking if a specific Souffle `Program` contains
--   a certain `Fact`. Additionally, it also checks if the fact is marked as
--   either `Output` or `InputOutput`. This constraint will generate a
--   user-friendly type error if these conditions are not met.
type family ContainsOutputFact prog fact :: Constraint where
  ContainsOutputFact prog fact = (ContainsFact prog fact, IsOutput fact (FactDirection fact))

type family IsInput (fact :: Type) (dir :: Direction) :: Constraint where
  IsInput _ 'Input = ()
  IsInput _ 'InputOutput = ()
  IsInput fact dir = TypeError
    ( "You tried to use an " <> FormatDirection dir <> " fact of type " <> fact <> " as an input."
    % "Possible solution: change the FactDirection of " <> fact
      <> " to either 'Input' or 'InputOutput'."
    )

type family IsOutput (fact :: Type) (dir :: Direction) :: Constraint where
  IsOutput _ 'Output = ()
  IsOutput _ 'InputOutput = ()
  IsOutput fact dir = TypeError
    ( "You tried to use an " <> FormatDirection dir <> " fact of type " <> fact <> " as an output."
    % "Possible solution: change the FactDirection of " <> fact
      <> " to either 'Output' or 'InputOutput'."
    )

type family FormatDirection (dir :: Direction) where
  FormatDirection 'Output = "output"
  FormatDirection 'Input = "input"
  FormatDirection 'Internal = "internal"

-- | A helper type family for checking if a specific Souffle `Program` contains
--   a certain `Fact`. This constraint will generate a user-friendly type error
--   if this is not the case.
type family ContainsFact prog fact :: Constraint where
  ContainsFact prog fact =
    CheckContains prog (ProgramFacts prog) fact

type family CheckContains prog facts fact :: Constraint where
  CheckContains prog '[] fact =
    TypeError ("You tried to perform an action with a fact of type '" <> fact
    <> "' for program '" <> prog <> "'."
    % "The program contains the following facts: " <> ProgramFacts prog <> "."
    % "It does not contain fact: " <> fact <> "."
    % "You can fix this error by adding the type '" <> fact
    <> "' to the ProgramFacts type in the Program instance for " <> prog <> ".")
  CheckContains _ (a ': _) a = ()
  CheckContains prog (_ ': as) b = CheckContains prog as b

-- | A typeclass for describing a datalog program.
--
-- Example usage (assuming the program was generated from path.dl
-- and contains 2 facts: Edge and Reachable):
--
-- @
-- data Path = Path  -- Handle for the datalog program
--
-- instance Program Path where
--   type ProgramFacts Path = '[Edge, Reachable]
--   programName = const "path"
-- @
class Program a where
  -- | A type level list of facts that belong to this program.
  --   This list is used to check that only known facts are added to a program.
  type ProgramFacts a :: [Type]

  -- | Function for obtaining the name of a Datalog program.
  --   This has to be the same as the name of the .dl file (minus the extension).
  programName :: a -> String

-- | A typeclass for data types representing a fact in datalog.
--
-- Example usage:
--
-- @
-- instance Fact Edge where
--   type FactDirection Edge = 'Input
--   factName = const "edge"
-- @
class Marshal.Marshal a => Fact a where
  -- | The direction or "mode" a fact can be used in.
  --   This is used to perform compile-time checks that a fact is only used
  --   in valid situations. For more information, see the 'Direction' type.
  type FactDirection a :: Direction

  -- | Function for obtaining the name of a fact
  --   (has to be the same as described in the Datalog program).
  --
  -- It uses a 'Proxy' to select the correct instance.
  factName :: Proxy a -> String

-- | A datatype describing which operations a certain fact supports.
--   The direction is from the datalog perspective, so that it
--   aligns with ".decl" statements in Souffle.
data Direction
  = Input
  -- ^ Fact can only be stored in Datalog (using `addFact`/`addFacts`).
  | Output
  -- ^ Fact can only be read from Datalog (using `getFacts`/`findFact`).
  | InputOutput
  -- ^ Fact supports both reading from / writing to Datalog.
  | Internal
  -- ^ Supports neither reading from / writing to Datalog. This is used for
  --   facts that are only visible inside Datalog itself.

-- | A mtl-style typeclass for Souffle-related actions.
class Monad m => MonadSouffle m where
  -- | Represents a handle for interacting with a Souffle program.
  --
  --   The handle is used in all other functions of this typeclass to perform
  --   Souffle-related actions.
  type Handler m :: Type -> Type

  -- | Helper associated type constraint that allows collecting facts from
  --   Souffle in a list or vector. Only used internally.
  type CollectFacts m (c :: Type -> Type) :: Constraint
  -- | Helper associated type constraint that allows submitting facts to
  --   Souffle. Only used internally.
  type SubmitFacts m (a :: Type) :: Constraint

  -- | Runs the Souffle program.
  run :: Handler m prog -> m ()

  -- | Sets the number of CPU cores this Souffle program should use.
  setNumThreads :: Handler m prog -> Word64 -> m ()

  -- | Gets the number of CPU cores this Souffle program should use.
  getNumThreads :: Handler m prog -> m Word64

  -- | Returns all facts of a program. This function makes use of type inference
  --   to select the type of fact to return.
  getFacts :: (Fact a, ContainsOutputFact prog a, CollectFacts m c)
           => Handler m prog -> m (c a)

  -- | Searches for a fact in a program.
  --   Returns 'Nothing' if no matching fact was found; otherwise 'Just' the fact.
  --
  --   Conceptually equivalent to @List.find (== fact) \<$\> getFacts prog@,
  --   but this operation can be implemented much faster.
  findFact :: (Fact a, ContainsOutputFact prog a, Eq a, SubmitFacts m a)
           => Handler m prog -> a -> m (Maybe a)

  -- | Adds a fact to the program.
  addFact :: (Fact a, ContainsInputFact prog a, SubmitFacts m a)
          => Handler m prog -> a -> m ()

  -- | Adds multiple facts to the program. This function could be implemented
  --   in terms of 'addFact', but this is done as a minor optimization.
  addFacts :: (Foldable t, Fact a, ContainsInputFact prog a, SubmitFacts m a)
           => Handler m prog -> t a -> m ()

instance MonadSouffle m => MonadSouffle (ReaderT r m) where
  type Handler (ReaderT r m) = Handler m
  type CollectFacts (ReaderT r m) c = CollectFacts m c
  type SubmitFacts (ReaderT r m) a = SubmitFacts m a

  run :: Handler (ReaderT r m) prog -> ReaderT r m ()
run = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ())
-> (Handler m prog -> m ()) -> Handler m prog -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m ()
forall (m :: * -> *) prog. MonadSouffle m => Handler m prog -> m ()
run
  {-# INLINABLE run #-}
  setNumThreads :: Handler (ReaderT r m) prog -> Word64 -> ReaderT r m ()
setNumThreads prog :: Handler (ReaderT r m) prog
prog = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ())
-> (Word64 -> m ()) -> Word64 -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> Word64 -> m ()
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> Word64 -> m ()
setNumThreads Handler m prog
Handler (ReaderT r m) prog
prog
  {-# INLINABLE setNumThreads #-}
  getNumThreads :: Handler (ReaderT r m) prog -> ReaderT r m Word64
getNumThreads = m Word64 -> ReaderT r m Word64
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Word64 -> ReaderT r m Word64)
-> (Handler m prog -> m Word64)
-> Handler m prog
-> ReaderT r m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m Word64
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> m Word64
getNumThreads
  {-# INLINABLE getNumThreads #-}
  getFacts :: Handler (ReaderT r m) prog -> ReaderT r m (c a)
getFacts = m (c a) -> ReaderT r m (c a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (c a) -> ReaderT r m (c a))
-> (Handler m prog -> m (c a))
-> Handler m prog
-> ReaderT r m (c a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m (c a)
forall (m :: * -> *) a prog (c :: * -> *).
(MonadSouffle m, Fact a, ContainsOutputFact prog a,
 CollectFacts m c) =>
Handler m prog -> m (c a)
getFacts
  {-# INLINABLE getFacts #-}
  findFact :: Handler (ReaderT r m) prog -> a -> ReaderT r m (Maybe a)
findFact prog :: Handler (ReaderT r m) prog
prog = m (Maybe a) -> ReaderT r m (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe a) -> ReaderT r m (Maybe a))
-> (a -> m (Maybe a)) -> a -> ReaderT r m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m (Maybe a)
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a,
 SubmitFacts m a) =>
Handler m prog -> a -> m (Maybe a)
findFact Handler m prog
Handler (ReaderT r m) prog
prog
  {-# INLINABLE findFact #-}
  addFact :: Handler (ReaderT r m) prog -> a -> ReaderT r m ()
addFact fact :: Handler (ReaderT r m) prog
fact = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ()) -> (a -> m ()) -> a -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m ()
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> a -> m ()
addFact Handler m prog
Handler (ReaderT r m) prog
fact
  {-# INLINABLE addFact #-}
  addFacts :: Handler (ReaderT r m) prog -> t a -> ReaderT r m ()
addFacts facts :: Handler (ReaderT r m) prog
facts = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ()) -> (t a -> m ()) -> t a -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> t a -> m ()
forall (m :: * -> *) (t :: * -> *) a prog.
(MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> t a -> m ()
addFacts Handler m prog
Handler (ReaderT r m) prog
facts
  {-# INLINABLE addFacts #-}

instance (Monoid w, MonadSouffle m) => MonadSouffle (WriterT w m) where
  type Handler (WriterT w m) = Handler m
  type CollectFacts (WriterT w m) c = CollectFacts m c
  type SubmitFacts (WriterT w m) a = SubmitFacts m a

  run :: Handler (WriterT w m) prog -> WriterT w m ()
run = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ())
-> (Handler m prog -> m ()) -> Handler m prog -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m ()
forall (m :: * -> *) prog. MonadSouffle m => Handler m prog -> m ()
run
  {-# INLINABLE run #-}
  setNumThreads :: Handler (WriterT w m) prog -> Word64 -> WriterT w m ()
setNumThreads prog :: Handler (WriterT w m) prog
prog = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ())
-> (Word64 -> m ()) -> Word64 -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> Word64 -> m ()
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> Word64 -> m ()
setNumThreads Handler m prog
Handler (WriterT w m) prog
prog
  {-# INLINABLE setNumThreads #-}
  getNumThreads :: Handler (WriterT w m) prog -> WriterT w m Word64
getNumThreads = m Word64 -> WriterT w m Word64
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Word64 -> WriterT w m Word64)
-> (Handler m prog -> m Word64)
-> Handler m prog
-> WriterT w m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m Word64
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> m Word64
getNumThreads
  {-# INLINABLE getNumThreads #-}
  getFacts :: Handler (WriterT w m) prog -> WriterT w m (c a)
getFacts = m (c a) -> WriterT w m (c a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (c a) -> WriterT w m (c a))
-> (Handler m prog -> m (c a))
-> Handler m prog
-> WriterT w m (c a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m (c a)
forall (m :: * -> *) a prog (c :: * -> *).
(MonadSouffle m, Fact a, ContainsOutputFact prog a,
 CollectFacts m c) =>
Handler m prog -> m (c a)
getFacts
  {-# INLINABLE getFacts #-}
  findFact :: Handler (WriterT w m) prog -> a -> WriterT w m (Maybe a)
findFact prog :: Handler (WriterT w m) prog
prog = m (Maybe a) -> WriterT w m (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe a) -> WriterT w m (Maybe a))
-> (a -> m (Maybe a)) -> a -> WriterT w m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m (Maybe a)
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a,
 SubmitFacts m a) =>
Handler m prog -> a -> m (Maybe a)
findFact Handler m prog
Handler (WriterT w m) prog
prog
  {-# INLINABLE findFact #-}
  addFact :: Handler (WriterT w m) prog -> a -> WriterT w m ()
addFact fact :: Handler (WriterT w m) prog
fact = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ()) -> (a -> m ()) -> a -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m ()
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> a -> m ()
addFact Handler m prog
Handler (WriterT w m) prog
fact
  {-# INLINABLE addFact #-}
  addFacts :: Handler (WriterT w m) prog -> t a -> WriterT w m ()
addFacts facts :: Handler (WriterT w m) prog
facts = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ()) -> (t a -> m ()) -> t a -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> t a -> m ()
forall (m :: * -> *) (t :: * -> *) a prog.
(MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> t a -> m ()
addFacts Handler m prog
Handler (WriterT w m) prog
facts
  {-# INLINABLE addFacts #-}

instance MonadSouffle m => MonadSouffle (StateT s m) where
  type Handler (StateT s m) = Handler m
  type CollectFacts (StateT s m) c = CollectFacts m c
  type SubmitFacts (StateT s m) a = SubmitFacts m a

  run :: Handler (StateT s m) prog -> StateT s m ()
run = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ())
-> (Handler m prog -> m ()) -> Handler m prog -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m ()
forall (m :: * -> *) prog. MonadSouffle m => Handler m prog -> m ()
run
  {-# INLINABLE run #-}
  setNumThreads :: Handler (StateT s m) prog -> Word64 -> StateT s m ()
setNumThreads prog :: Handler (StateT s m) prog
prog = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ())
-> (Word64 -> m ()) -> Word64 -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> Word64 -> m ()
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> Word64 -> m ()
setNumThreads Handler m prog
Handler (StateT s m) prog
prog
  {-# INLINABLE setNumThreads #-}
  getNumThreads :: Handler (StateT s m) prog -> StateT s m Word64
getNumThreads = m Word64 -> StateT s m Word64
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Word64 -> StateT s m Word64)
-> (Handler m prog -> m Word64)
-> Handler m prog
-> StateT s m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m Word64
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> m Word64
getNumThreads
  {-# INLINABLE getNumThreads #-}
  getFacts :: Handler (StateT s m) prog -> StateT s m (c a)
getFacts = m (c a) -> StateT s m (c a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (c a) -> StateT s m (c a))
-> (Handler m prog -> m (c a))
-> Handler m prog
-> StateT s m (c a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m (c a)
forall (m :: * -> *) a prog (c :: * -> *).
(MonadSouffle m, Fact a, ContainsOutputFact prog a,
 CollectFacts m c) =>
Handler m prog -> m (c a)
getFacts
  {-# INLINABLE getFacts #-}
  findFact :: Handler (StateT s m) prog -> a -> StateT s m (Maybe a)
findFact prog :: Handler (StateT s m) prog
prog = m (Maybe a) -> StateT s m (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe a) -> StateT s m (Maybe a))
-> (a -> m (Maybe a)) -> a -> StateT s m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m (Maybe a)
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a,
 SubmitFacts m a) =>
Handler m prog -> a -> m (Maybe a)
findFact Handler m prog
Handler (StateT s m) prog
prog
  {-# INLINABLE findFact #-}
  addFact :: Handler (StateT s m) prog -> a -> StateT s m ()
addFact fact :: Handler (StateT s m) prog
fact = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ()) -> (a -> m ()) -> a -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m ()
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> a -> m ()
addFact Handler m prog
Handler (StateT s m) prog
fact
  {-# INLINABLE addFact #-}
  addFacts :: Handler (StateT s m) prog -> t a -> StateT s m ()
addFacts facts :: Handler (StateT s m) prog
facts = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ()) -> (t a -> m ()) -> t a -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> t a -> m ()
forall (m :: * -> *) (t :: * -> *) a prog.
(MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> t a -> m ()
addFacts Handler m prog
Handler (StateT s m) prog
facts
  {-# INLINABLE addFacts #-}

instance (MonadSouffle m, Monoid w) => MonadSouffle (RWST r w s m) where
  type Handler (RWST r w s m) = Handler m
  type CollectFacts (RWST r w s m) c = CollectFacts m c
  type SubmitFacts (RWST r w s m) a = SubmitFacts m a

  run :: Handler (RWST r w s m) prog -> RWST r w s m ()
run = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ())
-> (Handler m prog -> m ()) -> Handler m prog -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m ()
forall (m :: * -> *) prog. MonadSouffle m => Handler m prog -> m ()
run
  {-# INLINABLE run #-}
  setNumThreads :: Handler (RWST r w s m) prog -> Word64 -> RWST r w s m ()
setNumThreads prog :: Handler (RWST r w s m) prog
prog = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ())
-> (Word64 -> m ()) -> Word64 -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> Word64 -> m ()
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> Word64 -> m ()
setNumThreads Handler m prog
Handler (RWST r w s m) prog
prog
  {-# INLINABLE setNumThreads #-}
  getNumThreads :: Handler (RWST r w s m) prog -> RWST r w s m Word64
getNumThreads = m Word64 -> RWST r w s m Word64
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Word64 -> RWST r w s m Word64)
-> (Handler m prog -> m Word64)
-> Handler m prog
-> RWST r w s m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m Word64
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> m Word64
getNumThreads
  {-# INLINABLE getNumThreads #-}
  getFacts :: Handler (RWST r w s m) prog -> RWST r w s m (c a)
getFacts = m (c a) -> RWST r w s m (c a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (c a) -> RWST r w s m (c a))
-> (Handler m prog -> m (c a))
-> Handler m prog
-> RWST r w s m (c a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m (c a)
forall (m :: * -> *) a prog (c :: * -> *).
(MonadSouffle m, Fact a, ContainsOutputFact prog a,
 CollectFacts m c) =>
Handler m prog -> m (c a)
getFacts
  {-# INLINABLE getFacts #-}
  findFact :: Handler (RWST r w s m) prog -> a -> RWST r w s m (Maybe a)
findFact prog :: Handler (RWST r w s m) prog
prog = m (Maybe a) -> RWST r w s m (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe a) -> RWST r w s m (Maybe a))
-> (a -> m (Maybe a)) -> a -> RWST r w s m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m (Maybe a)
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a,
 SubmitFacts m a) =>
Handler m prog -> a -> m (Maybe a)
findFact Handler m prog
Handler (RWST r w s m) prog
prog
  {-# INLINABLE findFact #-}
  addFact :: Handler (RWST r w s m) prog -> a -> RWST r w s m ()
addFact fact :: Handler (RWST r w s m) prog
fact = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ()) -> (a -> m ()) -> a -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m ()
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> a -> m ()
addFact Handler m prog
Handler (RWST r w s m) prog
fact
  {-# INLINABLE addFact #-}
  addFacts :: Handler (RWST r w s m) prog -> t a -> RWST r w s m ()
addFacts facts :: Handler (RWST r w s m) prog
facts = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ())
-> (t a -> m ()) -> t a -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> t a -> m ()
forall (m :: * -> *) (t :: * -> *) a prog.
(MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> t a -> m ()
addFacts Handler m prog
Handler (RWST r w s m) prog
facts
  {-# INLINABLE addFacts #-}

instance MonadSouffle m => MonadSouffle (ExceptT e m) where
  type Handler (ExceptT e m) = Handler m
  type CollectFacts (ExceptT e m) c = CollectFacts m c
  type SubmitFacts (ExceptT e m) a = SubmitFacts m a

  run :: Handler (ExceptT e m) prog -> ExceptT e m ()
run = m () -> ExceptT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT e m ())
-> (Handler m prog -> m ()) -> Handler m prog -> ExceptT e m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m ()
forall (m :: * -> *) prog. MonadSouffle m => Handler m prog -> m ()
run
  {-# INLINABLE run #-}
  setNumThreads :: Handler (ExceptT e m) prog -> Word64 -> ExceptT e m ()
setNumThreads prog :: Handler (ExceptT e m) prog
prog = m () -> ExceptT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT e m ())
-> (Word64 -> m ()) -> Word64 -> ExceptT e m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> Word64 -> m ()
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> Word64 -> m ()
setNumThreads Handler m prog
Handler (ExceptT e m) prog
prog
  {-# INLINABLE setNumThreads #-}
  getNumThreads :: Handler (ExceptT e m) prog -> ExceptT e m Word64
getNumThreads = m Word64 -> ExceptT e m Word64
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m Word64 -> ExceptT e m Word64)
-> (Handler m prog -> m Word64)
-> Handler m prog
-> ExceptT e m Word64
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m Word64
forall (m :: * -> *) prog.
MonadSouffle m =>
Handler m prog -> m Word64
getNumThreads
  {-# INLINABLE getNumThreads #-}
  getFacts :: Handler (ExceptT e m) prog -> ExceptT e m (c a)
getFacts = m (c a) -> ExceptT e m (c a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (c a) -> ExceptT e m (c a))
-> (Handler m prog -> m (c a))
-> Handler m prog
-> ExceptT e m (c a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> m (c a)
forall (m :: * -> *) a prog (c :: * -> *).
(MonadSouffle m, Fact a, ContainsOutputFact prog a,
 CollectFacts m c) =>
Handler m prog -> m (c a)
getFacts
  {-# INLINABLE getFacts #-}
  findFact :: Handler (ExceptT e m) prog -> a -> ExceptT e m (Maybe a)
findFact prog :: Handler (ExceptT e m) prog
prog = m (Maybe a) -> ExceptT e m (Maybe a)
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m (Maybe a) -> ExceptT e m (Maybe a))
-> (a -> m (Maybe a)) -> a -> ExceptT e m (Maybe a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m (Maybe a)
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsOutputFact prog a, Eq a,
 SubmitFacts m a) =>
Handler m prog -> a -> m (Maybe a)
findFact Handler m prog
Handler (ExceptT e m) prog
prog
  {-# INLINABLE findFact #-}
  addFact :: Handler (ExceptT e m) prog -> a -> ExceptT e m ()
addFact fact :: Handler (ExceptT e m) prog
fact = m () -> ExceptT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT e m ()) -> (a -> m ()) -> a -> ExceptT e m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> a -> m ()
forall (m :: * -> *) a prog.
(MonadSouffle m, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> a -> m ()
addFact Handler m prog
Handler (ExceptT e m) prog
fact
  {-# INLINABLE addFact #-}
  addFacts :: Handler (ExceptT e m) prog -> t a -> ExceptT e m ()
addFacts facts :: Handler (ExceptT e m) prog
facts = m () -> ExceptT e m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT e m ()) -> (t a -> m ()) -> t a -> ExceptT e m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> t a -> m ()
forall (m :: * -> *) (t :: * -> *) a prog.
(MonadSouffle m, Foldable t, Fact a, ContainsInputFact prog a,
 SubmitFacts m a) =>
Handler m prog -> t a -> m ()
addFacts Handler m prog
Handler (ExceptT e m) prog
facts
  {-# INLINABLE addFacts #-}

-- | A mtl-style typeclass for Souffle-related actions that involve file IO.
class MonadSouffle m => MonadSouffleFileIO m where
  -- | Load all facts from files in a certain directory.
  loadFiles :: Handler m prog -> FilePath -> m ()

  -- | Write out all facts of the program to CSV files in a certain directory
  --   (as defined in the Souffle program).
  writeFiles :: Handler m prog -> FilePath -> m ()

instance MonadSouffleFileIO m => MonadSouffleFileIO (ReaderT r m) where
  loadFiles :: Handler (ReaderT r m) prog -> FilePath -> ReaderT r m ()
loadFiles prog :: Handler (ReaderT r m) prog
prog = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ())
-> (FilePath -> m ()) -> FilePath -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
loadFiles Handler m prog
Handler (ReaderT r m) prog
prog
  {-# INLINABLE loadFiles #-}
  writeFiles :: Handler (ReaderT r m) prog -> FilePath -> ReaderT r m ()
writeFiles prog :: Handler (ReaderT r m) prog
prog = m () -> ReaderT r m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ReaderT r m ())
-> (FilePath -> m ()) -> FilePath -> ReaderT r m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
writeFiles Handler m prog
Handler (ReaderT r m) prog
prog
  {-# INLINABLE writeFiles #-}

instance (Monoid w, MonadSouffleFileIO m) => MonadSouffleFileIO (WriterT w m) where
  loadFiles :: Handler (WriterT w m) prog -> FilePath -> WriterT w m ()
loadFiles prog :: Handler (WriterT w m) prog
prog = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ())
-> (FilePath -> m ()) -> FilePath -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
loadFiles Handler m prog
Handler (WriterT w m) prog
prog
  {-# INLINABLE loadFiles #-}
  writeFiles :: Handler (WriterT w m) prog -> FilePath -> WriterT w m ()
writeFiles prog :: Handler (WriterT w m) prog
prog = m () -> WriterT w m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> WriterT w m ())
-> (FilePath -> m ()) -> FilePath -> WriterT w m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
writeFiles Handler m prog
Handler (WriterT w m) prog
prog
  {-# INLINABLE writeFiles #-}

instance MonadSouffleFileIO m => MonadSouffleFileIO (StateT s m) where
  loadFiles :: Handler (StateT s m) prog -> FilePath -> StateT s m ()
loadFiles prog :: Handler (StateT s m) prog
prog = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ())
-> (FilePath -> m ()) -> FilePath -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
loadFiles Handler m prog
Handler (StateT s m) prog
prog
  {-# INLINABLE loadFiles #-}
  writeFiles :: Handler (StateT s m) prog -> FilePath -> StateT s m ()
writeFiles prog :: Handler (StateT s m) prog
prog = m () -> StateT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> StateT s m ())
-> (FilePath -> m ()) -> FilePath -> StateT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
writeFiles Handler m prog
Handler (StateT s m) prog
prog
  {-# INLINABLE writeFiles #-}

instance (MonadSouffleFileIO m, Monoid w) => MonadSouffleFileIO (RWST r w s m) where
  loadFiles :: Handler (RWST r w s m) prog -> FilePath -> RWST r w s m ()
loadFiles prog :: Handler (RWST r w s m) prog
prog = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ())
-> (FilePath -> m ()) -> FilePath -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
loadFiles Handler m prog
Handler (RWST r w s m) prog
prog
  {-# INLINABLE loadFiles #-}
  writeFiles :: Handler (RWST r w s m) prog -> FilePath -> RWST r w s m ()
writeFiles prog :: Handler (RWST r w s m) prog
prog = m () -> RWST r w s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> RWST r w s m ())
-> (FilePath -> m ()) -> FilePath -> RWST r w s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
writeFiles Handler m prog
Handler (RWST r w s m) prog
prog
  {-# INLINABLE writeFiles #-}

instance MonadSouffleFileIO m => MonadSouffleFileIO (ExceptT s m) where
  loadFiles :: Handler (ExceptT s m) prog -> FilePath -> ExceptT s m ()
loadFiles prog :: Handler (ExceptT s m) prog
prog = m () -> ExceptT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT s m ())
-> (FilePath -> m ()) -> FilePath -> ExceptT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
loadFiles Handler m prog
Handler (ExceptT s m) prog
prog
  {-# INLINABLE loadFiles #-}
  writeFiles :: Handler (ExceptT s m) prog -> FilePath -> ExceptT s m ()
writeFiles prog :: Handler (ExceptT s m) prog
prog = m () -> ExceptT s m ()
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (m () -> ExceptT s m ())
-> (FilePath -> m ()) -> FilePath -> ExceptT s m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Handler m prog -> FilePath -> m ()
forall (m :: * -> *) prog.
MonadSouffleFileIO m =>
Handler m prog -> FilePath -> m ()
writeFiles Handler m prog
Handler (ExceptT s m) prog
prog
  {-# INLINABLE writeFiles #-}