{-# LANGUAGE GADTs, RankNTypes, TypeFamilies, DataKinds, TypeOperators, ConstraintKinds #-}
{-# LANGUAGE UndecidableInstances, UndecidableSuperClasses, FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, DerivingVia, ScopedTypeVariables #-}
{-# LANGUAGE PolyKinds #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}
module Language.Souffle.Experimental
(
Predicate(..)
, Fragment
, Tuple
, DSL
, Head
, Body
, Term
, VarName
, UsageContext(..)
, Direction(..)
, ToPredicate
, FactMetadata(..)
, Metadata(..)
, StructureOpt(..)
, InlineOpt(..)
, predicateFor
, var
, __
, underscore
, (|-)
, (\/)
, not'
, (.<)
, (.<=)
, (.>)
, (.>=)
, (.=)
, (.!=)
, (.^)
, (.%)
, band
, bor
, bxor
, lor
, land
, max'
, min'
, cat
, contains
, match
, ord
, strlen
, substr
, to_number
, to_string
, runSouffleInterpretedWith
, runSouffleInterpreted
, embedProgram
, render
, renderIO
, Structure
, NoVarsInAtom
, SupportsArithmetic
) where
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Data.Int
import Data.Kind
import Data.List.NonEmpty (NonEmpty(..), toList)
import Data.Map ( Map )
import qualified Data.Map as Map
import Data.Maybe (fromMaybe, catMaybes, mapMaybe)
import Data.Proxy
import Data.String
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Data.Text.Lazy as TL
import Data.Word
import GHC.Generics
import GHC.TypeLits
import Language.Haskell.TH.Syntax (qRunIO, qAddForeignFilePath, Q, Dec, ForeignSrcLang(..))
import Language.Souffle.Class ( Program(..), Fact(..), ContainsFact, Direction(..) )
import Language.Souffle.Internal.Constraints (SimpleProduct)
import qualified Language.Souffle.Interpreted as I
import System.Directory
import System.FilePath
import System.IO.Temp
import System.Process
import Text.Printf (printf)
import Type.Errors.Pretty
newtype Predicate a
= Predicate (forall f ctx. Fragment f ctx => Tuple ctx (Structure a) -> f ctx ())
type VarMap = Map VarName Int
newtype DSL prog ctx a = DSL (StateT VarMap (Writer [AST]) a)
deriving (Functor, Applicative, Monad, MonadWriter [AST], MonadState VarMap)
via (StateT VarMap (Writer [AST]))
addDefinition :: AST -> DSL prog 'Definition ()
addDefinition dl = tell [dl]
runSouffleInterpreted
:: (MonadIO m, Program prog)
=> prog
-> DSL prog 'Definition ()
-> (Maybe (I.Handle prog) -> I.SouffleM a)
-> m a
runSouffleInterpreted program dsl f = liftIO $ do
tmpDir <- getCanonicalTemporaryDirectory
souffleHsDir <- createTempDirectory tmpDir "souffle-haskell"
defaultCfg <- I.defaultConfig
let cfg = defaultCfg { I.cfgDatalogDir = souffleHsDir
, I.cfgFactDir = Just souffleHsDir
, I.cfgOutputDir = Just souffleHsDir
}
runSouffleInterpretedWith cfg program dsl f <* removeDirectoryRecursive souffleHsDir
runSouffleInterpretedWith
:: (MonadIO m, Program prog)
=> I.Config
-> prog
-> DSL prog 'Definition ()
-> (Maybe (I.Handle prog) -> I.SouffleM a)
-> m a
runSouffleInterpretedWith config program dsl f = liftIO $ do
let progName = programName program
datalogFile = I.cfgDatalogDir config </> progName <.> "dl"
renderIO program datalogFile dsl
I.runSouffleWith config program f
embedProgram :: Program prog => prog -> DSL prog 'Definition () -> Q [Dec]
embedProgram program dsl = do
cppFile <- qRunIO $ do
tmpDir <- getCanonicalTemporaryDirectory
souffleHsDir <- createTempDirectory tmpDir "souffle-haskell"
let progName = programName program
datalogFile = souffleHsDir </> progName <.> "dl"
cppFile = souffleHsDir </> progName <.> "cpp"
renderIO program datalogFile dsl
callCommand $ printf "souffle -g %s %s" cppFile datalogFile
pure cppFile
qAddForeignFilePath LangCxx cppFile
pure []
runDSL :: Program prog => prog -> DSL prog 'Definition a -> DL
runDSL _ (DSL a) = Statements $ mapMaybe simplify $ execWriter (evalStateT a mempty) where
simplify = \case
Declare' name dir fields opts -> pure $ Declare name dir fields opts
Rule' name terms body -> Rule name terms <$> simplify body
Atom' name terms -> pure $ Atom name terms
And' exprs -> case mapMaybe simplify exprs of
[] -> Nothing
exprs' -> pure $ foldl1 And exprs'
Or' exprs -> case mapMaybe simplify exprs of
[] -> Nothing
exprs' -> pure $ foldl1 Or exprs'
Not' expr -> Not <$> simplify expr
Constrain' e -> pure $ Constrain e
var :: NoVarsInAtom ctx => VarName -> DSL prog ctx' (Term ctx ty)
var name = do
count <- fromMaybe 0 <$> gets (Map.lookup name)
modify $ Map.insert name (count + 1)
let varName = if count == 0 then name else name <> "_" <> T.pack (show count)
pure $ VarTerm varName
data Head ctx unused
= Head Name (NonEmpty SimpleTerm)
newtype Body ctx a = Body (Writer [AST] a)
deriving (Functor, Applicative, Monad, MonadWriter [AST])
via (Writer [AST])
(\/) :: Body ctx () -> Body ctx () -> Body ctx ()
body1 \/ body2 = do
let rules1 = And' $ runBody body1
rules2 = And' $ runBody body2
tell [Or' [rules1, rules2]]
not' :: Body ctx a -> Body ctx ()
not' body = do
let rules = And' $ runBody body
tell [Not' rules]
runBody :: Body ctx a -> [AST]
runBody (Body m) = execWriter m
data TypeInfo (a :: k) (ts :: [Type]) = TypeInfo
type ToPredicate prog a =
( Fact a
, FactMetadata a
, ContainsFact prog a
, SimpleProduct a
, Assert (Length (Structure a) <=? 10) BigTupleError
, KnownDLTypes (Structure a)
, KnownDirection (FactDirection a)
, KnownSymbols (AccessorNames a)
, ToTerms (Structure a)
)
class (Fact a, SimpleProduct a) => FactMetadata a where
factOpts :: Proxy a -> Metadata a
factOpts = const $ Metadata Automatic NoInline
data Metadata a
= Metadata (StructureOpt a) (InlineOpt (FactDirection a))
data StructureOpt (a :: Type) where
Automatic :: StructureOpt a
BTree :: StructureOpt a
Brie :: StructureOpt a
EqRel :: (IsBinaryRelation a, Structure a ~ '[t, t]) => StructureOpt a
type IsBinaryRelation a =
Assert (Length (Structure a) == 2)
("Equivalence relations are only allowed with binary relations" <> ".")
data InlineOpt (d :: Direction) where
Inline :: InlineOpt 'Internal
NoInline :: InlineOpt d
predicateFor :: forall a prog. ToPredicate prog a => DSL prog 'Definition (Predicate a)
predicateFor = do
let typeInfo = TypeInfo :: TypeInfo a (Structure a)
p = Proxy :: Proxy a
name = T.pack $ factName p
accNames = fromMaybe genericNames $ accessorNames p
opts = toSimpleMetadata $ factOpts p
genericNames = map (("t" <>) . T.pack . show) [1..]
tys = getTypes (Proxy :: Proxy (Structure a))
direction = getDirection (Proxy :: Proxy (FactDirection a))
fields = zipWith FieldData tys accNames
definition = Declare' name direction fields opts
addDefinition definition
pure $ Predicate $ toFragment typeInfo name
toSimpleMetadata :: Metadata a -> SimpleMetadata
toSimpleMetadata (Metadata struct inline) =
let structOpt = case struct of
Automatic -> AutomaticLayout
BTree -> BTreeLayout
Brie -> BrieLayout
EqRel -> EqRelLayout
inlineOpt = case inline of
Inline -> DoInline
NoInline -> DoNotInline
in SimpleMetadata structOpt inlineOpt
class KnownDirection a where
getDirection :: Proxy a -> Direction
instance KnownDirection 'Input where getDirection = const Input
instance KnownDirection 'Output where getDirection = const Output
instance KnownDirection 'InputOutput where getDirection = const InputOutput
instance KnownDirection 'Internal where getDirection = const Internal
(|-) :: Head 'Relation a -> Body 'Relation () -> DSL prog 'Definition ()
Head name terms |- body =
let rules = runBody body
relation = Rule' name terms (And' rules)
in addDefinition relation
infixl 0 |-
class Fragment f ctx where
toFragment :: ToTerms ts => TypeInfo a ts -> Name -> Tuple ctx ts -> f ctx ()
instance Fragment Head 'Relation where
toFragment typeInfo name terms =
let terms' = toTerms (Proxy :: Proxy 'Relation) typeInfo terms
in Head name terms'
instance Fragment Body 'Relation where
toFragment typeInfo name terms =
let terms' = toTerms (Proxy :: Proxy 'Relation) typeInfo terms
in tell [Atom' name terms']
instance Fragment (DSL prog) 'Definition where
toFragment typeInfo name terms =
let terms' = toTerms (Proxy :: Proxy 'Definition) typeInfo terms
in addDefinition $ Atom' name terms'
data RenderMode = Nested | TopLevel
renderIO :: Program prog => prog -> FilePath -> DSL prog 'Definition () -> IO ()
renderIO prog path = TIO.writeFile path . render prog
render :: Program prog => prog -> DSL prog 'Definition () -> T.Text
render prog = flip runReader TopLevel . f . runDSL prog where
f = \case
Statements stmts ->
T.unlines <$> traverse f stmts
Declare name dir fields metadata ->
let fieldPairs = map renderField fields
renderedFactOpts = renderMetadata metadata
renderedOpts = if T.null renderedFactOpts then "" else " " <> renderedFactOpts
in pure $ T.intercalate "\n" $ catMaybes
[ Just $ ".decl " <> name <> "(" <> T.intercalate ", " fieldPairs <> ")" <> renderedOpts
, renderDir name dir
]
Atom name terms -> do
let rendered = name <> "(" <> renderTerms (toList terms) <> ")"
end <- maybeDot
pure $ rendered <> end
Rule name terms body -> do
body' <- f body
let rendered =
name <> "(" <> renderTerms (toList terms) <> ") :-\n" <>
T.intercalate "\n" (map indent $ T.lines body')
pure rendered
And e1 e2 -> do
txt <- nested $ do
txt1 <- f e1
txt2 <- f e2
pure $ txt1 <> ",\n" <> txt2
end <- maybeDot
pure $ txt <> end
Or e1 e2 -> do
txt <- nested $ do
txt1 <- f e1
txt2 <- f e2
pure $ txt1 <> ";\n" <> txt2
end <- maybeDot
case end of
"." -> pure $ txt <> end
_ -> pure $ "(" <> txt <> ")"
Not e -> do
let maybeAddParens txt = case e of
And _ _ -> "(" <> txt <> ")"
_ -> txt
txt <- maybeAddParens <$> nested (f e)
end <- maybeDot
case end of
"." -> pure $ "!" <> txt <> end
_ -> pure $ "!" <> txt
Constrain t -> do
let t' = renderTerm t
end <- maybeDot
case end of
"." -> pure $ t' <> "."
_ -> pure t'
indent = (" " <>)
nested = local (const Nested)
maybeDot = ask >>= \case
TopLevel -> pure "."
Nested -> pure mempty
renderDir :: VarName -> Direction -> Maybe T.Text
renderDir name = \case
Input -> Just $ ".input " <> name
Output -> Just $ ".output " <> name
InputOutput -> Just $ T.intercalate "\n"
$ catMaybes [renderDir name Input, renderDir name Output]
Internal -> Nothing
renderField :: FieldData -> T.Text
renderField (FieldData ty accName) =
let txt = case ty of
DLNumber -> ": number"
DLUnsigned -> ": unsigned"
DLFloat -> ": float"
DLString -> ": symbol"
in accName <> txt
renderMetadata :: SimpleMetadata -> T.Text
renderMetadata (SimpleMetadata struct inline) =
let structTxt = case struct of
AutomaticLayout -> Nothing
BTreeLayout -> Just "btree"
BrieLayout -> Just "brie"
EqRelLayout -> Just "eqrel"
inlineTxt = case inline of
DoInline -> Just "inline"
DoNotInline -> Nothing
in T.intercalate " " $ catMaybes [structTxt, inlineTxt]
renderTerms :: [SimpleTerm] -> T.Text
renderTerms = T.intercalate ", " . map renderTerm
renderTerm :: SimpleTerm -> T.Text
renderTerm = \case
I x -> T.pack $ show x
U x -> T.pack $ show x
F x -> T.pack $ printf "%f" x
S s -> "\"" <> T.pack s <> "\""
V v -> v
Underscore -> "_"
BinOp' op t1 t2 -> renderTerm t1 <> " " <> renderBinOp op <> " " <> renderTerm t2
UnaryOp' op t1 -> renderUnaryOp op <> renderTerm t1
Func' name ts -> renderFunc name <> "(" <> renderTerms (toList ts) <> ")"
where
renderFunc = \case
Max -> "max"
Min -> "min"
Cat -> "cat"
Contains -> "contains"
Match -> "match"
Ord -> "ord"
StrLen -> "strlen"
Substr -> "substr"
ToNumber -> "to_number"
ToString -> "to_string"
renderBinOp = \case
Plus -> "+"
Mul -> "*"
Subtract -> "-"
Div -> "/"
Pow -> "^"
Rem -> "%"
BinaryAnd -> "band"
BinaryOr -> "bor"
BinaryXor -> "bxor"
LogicalAnd -> "land"
LogicalOr -> "lor"
LessThan -> "<"
LessThanOrEqual -> "<="
GreaterThan -> ">"
GreaterThanOrEqual -> ">="
IsEqual -> "="
IsNotEqual -> "!="
renderUnaryOp Negate = "-"
type Name = T.Text
type VarName = T.Text
type AccessorName = T.Text
data DLType
= DLNumber
| DLUnsigned
| DLFloat
| DLString
data FieldData = FieldData DLType AccessorName
data UsageContext
= Definition
| Relation
type family NoVarsInAtom (ctx :: UsageContext) :: Constraint where
NoVarsInAtom ctx = Assert (ctx == 'Relation) NoVarsInAtomError
type NoVarsInAtomError =
( "You tried to use a variable in a top level fact, which is not supported in Souffle."
% "Possible solutions:"
% " - Move the fact inside a rule body."
% " - Replace the variable in the fact with a string, number, unsigned or float constant."
)
data Term ctx ty where
VarTerm :: NoVarsInAtom ctx => VarName -> Term ctx ty
UnderscoreTerm :: Term ctx ty
NumberTerm :: Int32 -> Term ctx Int32
UnsignedTerm :: Word32 -> Term ctx Word32
FloatTerm :: Float -> Term ctx Float
StringTerm :: ToString ty => ty -> Term ctx ty
UnaryOp :: Num ty => Op1 -> Term ctx ty -> Term ctx ty
BinOp :: Num ty => Op2 -> Term ctx ty -> Term ctx ty -> Term ctx ty
Func :: FuncName -> NonEmpty SimpleTerm -> Term ctx ty2
data Op2
= Plus
| Mul
| Subtract
| Div
| Pow
| Rem
| BinaryAnd
| BinaryOr
| BinaryXor
| LogicalAnd
| LogicalOr
| LessThan
| LessThanOrEqual
| GreaterThan
| GreaterThanOrEqual
| IsEqual
| IsNotEqual
data Op1 = Negate
data FuncName
= Max
| Min
| Cat
| Contains
| Match
| Ord
| StrLen
| Substr
| ToNumber
| ToString
underscore :: Term ctx ty
underscore = UnderscoreTerm
__ :: Term ctx ty
__ = underscore
class ToString a where
toString :: a -> String
instance ToString String where toString = id
instance ToString T.Text where toString = T.unpack
instance ToString TL.Text where toString = TL.unpack
instance IsString (Term ctx String) where fromString = StringTerm
instance IsString (Term ctx T.Text) where fromString = StringTerm . T.pack
instance IsString (Term ctx TL.Text) where fromString = StringTerm . TL.pack
class Num ty => SupportsArithmetic ty where
fromInteger' :: Integer -> Term ctx ty
instance SupportsArithmetic Int32 where
fromInteger' = NumberTerm . fromInteger
instance SupportsArithmetic Word32 where
fromInteger' = UnsignedTerm . fromInteger
instance SupportsArithmetic Float where
fromInteger' = FloatTerm . fromInteger
instance (SupportsArithmetic ty, Num ty) => Num (Term ctx ty) where
fromInteger = fromInteger'
(+) = BinOp Plus
(*) = BinOp Mul
(-) = BinOp Subtract
negate = UnaryOp Negate
abs = error "'abs' is not supported for Souffle terms"
signum = error "'signum' is not supported for Souffle terms"
instance Fractional (Term ctx Float) where
fromRational = FloatTerm . fromRational
(/) = BinOp Div
(.^) :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
(.^) = BinOp Pow
(.%) :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
(.%) = BinOp Rem
(.<) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
(.<) = addConstraint LessThan
infix 1 .<
(.<=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
(.<=) = addConstraint LessThanOrEqual
infix 1 .<=
(.>) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
(.>) = addConstraint GreaterThan
infix 1 .>
(.>=) :: Num ty => Term ctx ty -> Term ctx ty -> Body ctx ()
(.>=) = addConstraint GreaterThanOrEqual
infix 1 .>=
(.=) :: Term ctx ty -> Term ctx ty -> Body ctx ()
(.=) = addConstraint IsEqual
infix 1 .=
(.!=) :: Term ctx ty -> Term ctx ty -> Body ctx ()
(.!=) = addConstraint IsNotEqual
infix 1 .!=
addConstraint :: Op2 -> Term ctx ty -> Term ctx ty -> Body ctx ()
addConstraint op e1 e2 =
let expr = BinOp' op (toTerm e1) (toTerm e2)
in tell [Constrain' expr]
band :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
band = BinOp BinaryAnd
bor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
bor = BinOp BinaryOr
bxor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
bxor = BinOp BinaryXor
land :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
land = BinOp LogicalAnd
lor :: (Num ty, Integral ty) => Term ctx ty -> Term ctx ty -> Term ctx ty
lor = BinOp LogicalOr
max' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
max' = func2 Max
min' :: Num ty => Term ctx ty -> Term ctx ty -> Term ctx ty
min' = func2 Min
cat :: ToString ty => Term ctx ty -> Term ctx ty -> Term ctx ty
cat = func2 Cat
contains :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()
contains a b =
let expr = toTerm $ func2 Contains a b
in tell [Constrain' expr]
match :: ToString ty => Term ctx ty -> Term ctx ty -> Body ctx ()
match p s =
let expr = toTerm $ func2 Match p s
in tell [Constrain' expr]
ord :: ToString ty => Term ctx ty -> Term ctx Int32
ord = func1 Ord
strlen :: ToString ty => Term ctx ty -> Term ctx Int32
strlen = func1 StrLen
substr :: ToString ty => Term ctx ty -> Term ctx Int32 -> Term ctx Int32 -> Term ctx ty
substr a b c = Func Substr $ toTerm a :| [toTerm b, toTerm c]
to_number :: ToString ty => Term ctx ty -> Term ctx Int32
to_number = func1 ToNumber
to_string :: ToString ty => Term ctx Int32 -> Term ctx ty
to_string = func1 ToString
func1 :: FuncName -> Term ctx ty -> Term ctx ty2
func1 name a = Func name $ toTerm a :| []
func2 :: FuncName -> Term ctx ty -> Term ctx ty -> Term ctx ty2
func2 name a b = Func name $ toTerm a :| [toTerm b]
data SimpleTerm
= V VarName
| I Int32
| U Word32
| F Float
| S String
| Underscore
| BinOp' Op2 SimpleTerm SimpleTerm
| UnaryOp' Op1 SimpleTerm
| Func' FuncName (NonEmpty SimpleTerm)
data SimpleMetadata = SimpleMetadata StructureOption InlineOption
data StructureOption
= AutomaticLayout
| BTreeLayout
| BrieLayout
| EqRelLayout
data InlineOption
= DoInline
| DoNotInline
data AST
= Declare' VarName Direction [FieldData] SimpleMetadata
| Rule' Name (NonEmpty SimpleTerm) AST
| Atom' Name (NonEmpty SimpleTerm)
| And' [AST]
| Or' [AST]
| Not' AST
| Constrain' SimpleTerm
data DL
= Statements [DL]
| Declare VarName Direction [FieldData] SimpleMetadata
| Rule Name (NonEmpty SimpleTerm) DL
| Atom Name (NonEmpty SimpleTerm)
| And DL DL
| Or DL DL
| Not DL
| Constrain SimpleTerm
class KnownDLTypes (ts :: [Type]) where
getTypes :: Proxy ts -> [DLType]
instance KnownDLTypes '[] where
getTypes _ = []
instance (KnownDLType t, KnownDLTypes ts) => KnownDLTypes (t ': ts) where
getTypes _ = getType (Proxy :: Proxy t) : getTypes (Proxy :: Proxy ts)
class KnownDLType t where
getType :: Proxy t -> DLType
instance KnownDLType Int32 where getType = const DLNumber
instance KnownDLType Word32 where getType = const DLUnsigned
instance KnownDLType Float where getType = const DLFloat
instance KnownDLType String where getType = const DLString
instance KnownDLType T.Text where getType = const DLString
instance KnownDLType TL.Text where getType = const DLString
type family AccessorNames a :: [Symbol] where
AccessorNames a = GetAccessorNames (Rep a)
type family GetAccessorNames (f :: Type -> Type) :: [Symbol] where
GetAccessorNames (a :*: b) = GetAccessorNames a ++ GetAccessorNames b
GetAccessorNames (C1 ('MetaCons _ _ 'False) _) = '[]
GetAccessorNames (S1 ('MetaSel ('Just name) _ _ _) a) = '[name] ++ GetAccessorNames a
GetAccessorNames (M1 _ _ a) = GetAccessorNames a
GetAccessorNames (K1 _ _) = '[]
class KnownSymbols (symbols :: [Symbol]) where
toStrings :: Proxy symbols -> [String]
instance KnownSymbols '[] where
toStrings = const []
instance (KnownSymbol s, KnownSymbols symbols) => KnownSymbols (s ': symbols) where
toStrings _ =
let sym = symbolVal (Proxy :: Proxy s)
symbols = toStrings (Proxy :: Proxy symbols)
in sym : symbols
accessorNames :: forall a. KnownSymbols (AccessorNames a) => Proxy a -> Maybe [T.Text]
accessorNames _ = case toStrings (Proxy :: Proxy (AccessorNames a)) of
[] -> Nothing
names -> Just $ T.pack <$> names
type Tuple ctx ts = TupleOf (MapType (Term ctx) ts)
class ToTerms (ts :: [Type]) where
toTerms :: Proxy ctx -> TypeInfo a ts -> Tuple ctx ts -> NonEmpty SimpleTerm
instance ToTerms '[t] where
toTerms _ _ a =
toTerm a :| []
instance ToTerms '[t1, t2] where
toTerms _ _ (a, b) =
toTerm a :| [toTerm b]
instance ToTerms '[t1, t2, t3] where
toTerms _ _ (a, b, c) =
toTerm a :| [toTerm b, toTerm c]
instance ToTerms '[t1, t2, t3, t4] where
toTerms _ _ (a, b, c, d) =
toTerm a :| [toTerm b, toTerm c, toTerm d]
instance ToTerms '[t1, t2, t3, t4, t5] where
toTerms _ _ (a, b, c, d, e) =
toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e]
instance ToTerms '[t1, t2, t3, t4, t5, t6] where
toTerms _ _ (a, b, c, d, e, f) =
toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f]
instance ToTerms '[t1, t2, t3, t4, t5, t6, t7] where
toTerms _ _ (a, b, c, d, e, f, g) =
toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g]
instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8] where
toTerms _ _ (a, b, c, d, e, f, g, h) =
toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g, toTerm h]
instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9] where
toTerms _ _ (a, b, c, d, e, f, g, h, i) =
toTerm a :| [toTerm b, toTerm c, toTerm d, toTerm e, toTerm f, toTerm g, toTerm h, toTerm i]
instance ToTerms '[t1, t2, t3, t4, t5, t6, t7, t8, t9, t10] where
toTerms _ _ (a, b, c, d, e, f, g, h, i, j) =
toTerm a :| [ toTerm b, toTerm c, toTerm d, toTerm e, toTerm f
, toTerm g, toTerm h, toTerm i, toTerm j
]
toTerm :: Term ctx t -> SimpleTerm
toTerm = \case
VarTerm v -> V v
StringTerm s -> S $ toString s
NumberTerm x -> I x
UnsignedTerm x -> U x
FloatTerm x -> F x
UnderscoreTerm -> Underscore
BinOp op t1 t2 -> BinOp' op (toTerm t1) (toTerm t2)
UnaryOp op t1 -> UnaryOp' op (toTerm t1)
Func name ts -> Func' name ts
type family MapType (f :: Type -> Type) (ts :: [Type]) :: [Type] where
MapType _ '[] = '[]
MapType f (t ': ts) = f t ': MapType f ts
type family Assert (c :: Bool) (msg :: ErrorMessage) :: Constraint where
Assert 'True _ = ()
Assert 'False msg = TypeError msg
type family (a :: k) == (b :: k) :: Bool where
a == a = 'True
_ == _ = 'False
type family Length (xs :: [Type]) :: Nat where
Length '[] = 0
Length (_ ': xs) = 1 + Length xs
type family Structure a :: [Type] where
Structure a = Collect (Rep a)
type family Collect (a :: Type -> Type) where
Collect (a :*: b) = Collect a ++ Collect b
Collect (M1 _ _ a) = Collect a
Collect (K1 _ ty) = '[ty]
type family a ++ b = c where
'[] ++ b = b
a ++ '[] = a
(a ': b) ++ c = a ': (b ++ c)
type family TupleOf (ts :: [Type]) = t where
TupleOf '[t] = t
TupleOf '[t1, t2] = (t1, t2)
TupleOf '[t1, t2, t3] = (t1, t2, t3)
TupleOf '[t1, t2, t3, t4] = (t1, t2, t3, t4)
TupleOf '[t1, t2, t3, t4, t5] = (t1, t2, t3, t4, t5)
TupleOf '[t1, t2, t3, t4, t5, t6] = (t1, t2, t3, t4, t5, t6)
TupleOf '[t1, t2, t3, t4, t5, t6, t7] = (t1, t2, t3, t4, t5, t6, t7)
TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8] = (t1, t2, t3, t4, t5, t6, t7, t8)
TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8, t9] = (t1, t2, t3, t4, t5, t6, t7, t8, t9)
TupleOf '[t1, t2, t3, t4, t5, t6, t7, t8, t9, t10] = (t1, t2, t3, t4, t5, t6, t7, t8, t9, t10)
TupleOf _ = TypeError BigTupleError
type BigTupleError =
( "The DSL only supports facts/tuples consisting of up to 10 elements."
% "If you need more arguments, please submit an issue on Github "
<> "(https://github.com/luc-tielen/souffle-haskell/issues)"
)