packages feed

souffle-haskell 3.2.0 → 3.3.0

raw patch · 8 files changed

+185/−35 lines, 8 files

Files

CHANGELOG.md view
@@ -3,9 +3,20 @@ All notable changes to this project (as seen by library users) will be documented in this file. The CHANGELOG is available on [Github](https://github.com/luc-tielen/souffle-haskell.git/CHANGELOG.md). +## [3.3.0] - 2022-02-27++### Added++- New `DerivingVia`-style API for binding to a Datalog program.+ ## [3.2.0] - 2022-02-20 +### Added+ - Add `Analysis` type for composing multiple Datalog programs.++### Changed+ - souffle-haskell now supports Souffle version 2.2.  ## [3.1.0] - 2021-09-30
README.md view
@@ -43,8 +43,11 @@  ```haskell -- Enable some necessary extensions:-{-# LANGUAGE ScopedTypeVariables, DataKinds, TypeFamilies, DeriveGeneric #-}+{-# LANGUAGE DeriveGeneric, DeriveAnyClass, DerivingVia, DataKinds, UndecidableInstances #-} +-- NOTE: The usage of "deriving stock", "deriving anyclass" and "deriving via" in the+-- examples below matters in order for the library to work correctly!+ module Main ( main ) where  import Data.Foldable ( traverse_ )@@ -54,40 +57,33 @@ import qualified Language.Souffle.Compiled as Souffle  --- We define a data type representing our datalog program.+-- First, we define a data type representing our datalog program. data Path = Path+  -- By making Path an instance of Program, we provide Haskell with information+  -- about the datalog program. It uses this to perform compile-time checks to+  -- limit the amount of possible programmer errors to a minimum.+  deriving Souffle.Program+  via Souffle.ProgramOptions Path "path" '[Edge, Reachable]  -- Facts are represented in Haskell as simple product types, -- Numbers map to Int32, unsigned to Word32, floats to Float, -- symbols to Strings / Text.  data Edge = Edge String String-  deriving (Eq, Show, Generic)+  deriving stock (Eq, Show, Generic)+  -- For simple product types, we can automatically generate the+  -- marshalling/unmarshalling code of data between Haskell and datalog.+  deriving anyclass Souffle.Marshal+  -- By making a data type an instance of Fact, we give Haskell the+  -- necessary information to bind to the datalog fact.+  deriving Souffle.Fact+  via Souffle.FactOptions Edge "edge" 'Souffle.Input  data Reachable = Reachable String String-  deriving (Eq, Show, Generic)---- By making Path an instance of Program, we provide Haskell with information--- about the datalog program. It uses this to perform compile-time checks to--- limit the amount of possible programmer errors to a minimum.-instance Souffle.Program Path where-  type ProgramFacts Path = [Edge, Reachable]-  programName = const "path"---- By making a data type an instance of Fact, we give Haskell the--- necessary information to bind to the datalog fact.-instance Souffle.Fact Edge where-  type FactDirection Edge = 'Souffle.Input-  factName = const "edge"--instance Souffle.Fact Reachable where-  type FactDirection Reachable = 'Souffle.Output-  factName = const "reachable"---- For simple product types, we can automatically generate the--- marshalling/unmarshalling code of data between Haskell and datalog.-instance Souffle.Marshal Edge-instance Souffle.Marshal Reachable+  deriving stock (Eq, Show, Generic)+  deriving anyclass Souffle.Marshal+  deriving Souffle.Fact+  via Souffle.FactOptions Reachable "reachable" 'Souffle.Output   main :: IO ()
lib/Language/Souffle/Analysis.hs view
@@ -40,48 +40,62 @@            -> m b         -- ^ Function for retrieving the 'Analysis' results from Souffle.            -> Analysis m a b mkAnalysis f r g = Analysis f r (const g)+{-# INLINABLE mkAnalysis #-}  -- | Converts an 'Analysis' into an effectful function, so it can be executed. execAnalysis :: Applicative m => Analysis m a b -> (a -> m b) execAnalysis (Analysis f r g) a = f a *> r *> g a+{-# INLINABLE execAnalysis #-}  instance Functor m => Functor (Analysis m a) where   fmap func (Analysis f r g) =     Analysis f r (fmap func <$> g)+  {-# INLINABLE fmap #-}  instance Functor m => Profunctor (Analysis m) where   lmap fn (Analysis f r g) =     Analysis (lmap fn f) r (lmap fn g)+  {-# INLINABLE lmap #-}+   rmap = fmap+  {-# INLINABLE rmap #-}  instance (Monoid (m ()), Applicative m) => Applicative (Analysis m a) where   pure a = Analysis mempty mempty (const $ pure a)+  {-# INLINABLE pure #-}    Analysis f1 r1 g1 <*> Analysis f2 r2 g2 =     Analysis (f1 <> f2) (r1 <> r2) (\a -> g1 a <*> g2 a)+  {-# INLINABLE (<*>) #-}  instance (Semigroup (m ()), Semigroup (m b)) => Semigroup (Analysis m a b) where   Analysis f1 r1 g1 <> Analysis f2 r2 g2 =     Analysis (f1 <> f2) (r1 <> r2) (g1 <> g2)+  {-# INLINABLE (<>) #-}  instance (Monoid (m ()), Monoid (m b)) => Monoid (Analysis m a b) where   mempty = Analysis mempty mempty mempty+  {-# INLINABLE mempty #-}  instance (Monoid (m ()), Monad m) => Category (Analysis m) where   id = Analysis mempty mempty pure+  {-# INLINABLE id #-}    Analysis f1 r1 g1 . Analysis f2 r2 g2 = Analysis f r1 g     where       f = execAnalysis (Analysis f2 r2 g2) >=> f1       -- NOTE: lazyness avoids work here in g2 in cases where "const" is used       g = g2 >=> g1+  {-# INLINABLE (.) #-}  instance Functor m => Strong (Analysis m) where   first' (Analysis f r g) =     Analysis (f . fst) r $ \(b, d) -> (,d) <$> g b+  {-# INLINABLE first' #-}    second' (Analysis f r g) =     Analysis (f . snd) r $ \(d, b) -> (d,) <$> g b+  {-# INLINABLE second' #-}  instance Applicative m => Choice (Analysis m) where   left' (Analysis f r g) = Analysis f' r g'@@ -92,6 +106,7 @@       g' = \case         Left b -> Left <$> g b         Right d -> pure $ Right d+  {-# INLINABLE left' #-}    right' (Analysis f r g) = Analysis f' r g'     where@@ -101,27 +116,35 @@       g' = \case         Left d -> pure $ Left d         Right b -> Right <$> g b+  {-# INLINABLE right' #-}  instance (Monad m, Monoid (m ()), Category (Analysis m)) => Arrow (Analysis m) where   arr f = Analysis mempty mempty (pure . f)+  {-# INLINABLE arr #-}    first = first'+  {-# INLINABLE first #-}    second = second'+  {-# INLINABLE second #-}    Analysis f1 r1 g1 *** Analysis f2 r2 g2 =     Analysis (\(b, b') -> f1 b *> f2 b') (r1 <> r2) $ \(b, b') -> do       c <- g1 b       c' <- g2 b'       pure (c, c')+  {-# INLINABLE (***) #-}    Analysis f1 r1 g1 &&& Analysis f2 r2 g2 =     Analysis (f1 <> f2) (r1 <> r2) $ \b -> (,) <$> g1 b <*> g2 b+  {-# INLINABLE (&&&) #-}  instance (Monad m, Monoid (m ())) => ArrowChoice (Analysis m) where   left = left'+  {-# INLINABLE left #-}    right = right'+  {-# INLINABLE right #-}    Analysis f1 r1 g1 +++ Analysis f2 r2 g2 = Analysis f' (r1 <> r2) g'     where@@ -131,4 +154,5 @@       g' = \case         Left b -> Left <$> g1 b         Right b' -> Right <$> g2 b'+  {-# INLINABLE (+++) #-} 
lib/Language/Souffle/Class.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE DataKinds, UndecidableInstances, FlexibleContexts #-}-{-# LANGUAGE TypeFamilies, TypeOperators #-}+{-# LANGUAGE TypeFamilies, TypeOperators, TypeApplications #-}  -- | This module provides the top level API for Souffle related operations. --   It makes use of Haskell's powerful typesystem to make certain invalid states@@ -15,7 +15,9 @@ --   type safety and user-friendly error messages. module Language.Souffle.Class   ( Program(..)+  , ProgramOptions(..)   , Fact(..)+  , FactOptions(..)   , Marshal.Marshal(..)   , Direction(..)   , ContainsInputFact@@ -35,6 +37,7 @@ import Data.Proxy import Data.Kind import Data.Word+import GHC.TypeLits import qualified Language.Souffle.Marshal as Marshal import Type.Errors.Pretty @@ -115,6 +118,29 @@   --   This has to be the same as the name of the .dl file (minus the extension).   programName :: a -> String +-- | A helper data type, used in combination with the DerivingVia extension to+--   automatically generate code to bind Haskell to a Souffle Datalog program.+--+-- The following is an example how to bind to a Datalog program "path"+-- (saved as path.dl / path.cpp), that uses two facts called "edge" and+-- "reachable" (represented with the Edge and Reachable types):+--+-- @+-- data Path = Path+--   deriving Souffle.Program+--   via Souffle.ProgramOptions Path "path" '[Edge, Reachable]+-- @+--+-- See also: 'FactOptions'.+newtype ProgramOptions (prog :: Type) (progName :: Symbol) (facts :: [Type])+  = ProgramOptions prog++instance KnownSymbol progName => Program (ProgramOptions prog progName facts) where+  type ProgramFacts (ProgramOptions _ _ facts) = facts++  programName = const $ symbolVal (Proxy @progName)+  {-# INLINABLE programName #-}+ -- | A typeclass for data types representing a fact in datalog. -- -- Example usage:@@ -135,6 +161,41 @@   --   -- It uses a 'Proxy' to select the correct instance.   factName :: Proxy a -> String++-- | A helper data type, used in combination with the DerivingVia extension to+--   automatically generate code to bind a Haskell datatype to a Souffle+--   Datalog fact.+--+-- The following is an example how to bind to a Datalog fact "edge"+-- that contains two symbols (strings in Haskell) that is an input (from the+-- Datalog point of view):+--+-- @+-- data Edge = Edge String String+--   deriving (Eq, Show, Generic)+--   deriving anyclass Souffle.Marshal+--   deriving Souffle.Fact+--   via Souffle.FactOptions Edge "edge" 'Souffle.Input+-- @+--+-- See also: 'ProgramOptions'.+newtype FactOptions (fact :: Type) (factName :: Symbol) (dir :: Direction)+  = FactOptions fact++instance Marshal.Marshal fact => Marshal.Marshal (FactOptions fact name dir) where+  push (FactOptions fact) = Marshal.push fact+  {-# INLINABLE push #-}+  pop = FactOptions <$> Marshal.pop+  {-# INLINABLE pop #-}++instance ( Marshal.Marshal fact+         , KnownSymbol factName+         ) => Fact (FactOptions fact factName dir) where+  type FactDirection (FactOptions _ _ dir) = dir++  factName = const $ symbolVal (Proxy @factName)+  {-# INLINABLE factName #-}+  -- | A datatype describing which operations a certain fact supports. --   The direction is from the datalog perspective, so that it
lib/Language/Souffle/Compiled.hs view
@@ -15,7 +15,9 @@ --   C++ compilation times. module Language.Souffle.Compiled   ( Program(..)+  , ProgramOptions(..)   , Fact(..)+  , FactOptions(..)   , Marshal(..)   , Direction(..)   , ContainsInputFact
lib/Language/Souffle/Interpreted.hs view
@@ -12,7 +12,9 @@ --   the compiled alternative once the prototyping phase is finished. module Language.Souffle.Interpreted   ( Program(..)+  , ProgramOptions(..)   , Fact(..)+  , FactOptions(..)   , Marshal(..)   , Direction(..)   , ContainsInputFact
souffle-haskell.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.34.2.+-- This file has been generated from package.yaml by hpack version 0.34.6. -- -- see: https://github.com/sol/hpack  name:           souffle-haskell-version:        3.2.0+version:        3.3.0 synopsis:       Souffle Datalog bindings for Haskell description:    Souffle Datalog bindings for Haskell. category:       Logic Programming, Foreign Binding, Bindings@@ -88,7 +88,10 @@       Paths_souffle_haskell   hs-source-dirs:       lib-  default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables+  default-extensions:+      OverloadedStrings+      LambdaCase+      ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -Wno-prepositive-qualified-module -Wno-missing-safe-haskell-mode -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits   cxx-options: -std=c++17 -Wall   include-dirs:@@ -152,7 +155,7 @@     , mtl >=2.0 && <3     , process >=1.6 && <2     , profunctors >=5.6.2 && <6-    , template-haskell >=2 && <3+    , template-haskell ==2.*     , temporary >=1.3 && <2     , text >=1.0 && <2     , text-short >=0.1.3 && <1@@ -169,12 +172,18 @@   other-modules:       Test.Language.Souffle.AnalysisSpec       Test.Language.Souffle.CompiledSpec+      Test.Language.Souffle.DerivingViaSpec       Test.Language.Souffle.InterpretedSpec       Test.Language.Souffle.MarshalSpec       Paths_souffle_haskell+  autogen-modules:+      Paths_souffle_haskell   hs-source-dirs:       tests-  default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables+  default-extensions:+      OverloadedStrings+      LambdaCase+      ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -Wno-prepositive-qualified-module -Wno-missing-safe-haskell-mode -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits   cxx-options: -std=c++17 -D__EMBEDDED_SOUFFLE__   include-dirs:@@ -245,7 +254,7 @@     , process >=1.6 && <2     , profunctors >=5.6.2 && <6     , souffle-haskell-    , template-haskell >=2 && <3+    , template-haskell ==2.*     , temporary >=1.3 && <2     , text >=1.0 && <2     , text-short >=0.1.3 && <1@@ -261,9 +270,14 @@   main-is: bench.hs   other-modules:       Paths_souffle_haskell+  autogen-modules:+      Paths_souffle_haskell   hs-source-dirs:       benchmarks-  default-extensions: OverloadedStrings LambdaCase ScopedTypeVariables+  default-extensions:+      OverloadedStrings+      LambdaCase+      ScopedTypeVariables   ghc-options: -Wall -Weverything -Wno-safe -Wno-unsafe -Wno-implicit-prelude -Wno-missed-specializations -Wno-all-missed-specializations -Wno-missing-import-lists -Wno-type-defaults -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-deriving-strategies -Wno-prepositive-qualified-module -Wno-missing-safe-haskell-mode -optP-Wno-nonportable-include-path -fhide-source-paths -fno-show-valid-hole-fits -fno-sort-valid-hole-fits +RTS -N1 -RTS   cxx-options: -std=c++17 -D__EMBEDDED_SOUFFLE__ -std=c++17 -march=native   include-dirs:@@ -329,7 +343,7 @@     , process >=1.6 && <2     , profunctors >=5.6.2 && <6     , souffle-haskell-    , template-haskell >=2 && <3+    , template-haskell ==2.*     , temporary >=1.3 && <2     , text >=1.0 && <2     , text-short >=0.1.3 && <1
+ tests/Test/Language/Souffle/DerivingViaSpec.hs view
@@ -0,0 +1,40 @@+{-# LANGUAGE UndecidableInstances, DataKinds, DeriveGeneric, DeriveAnyClass, DerivingVia #-}++module Test.Language.Souffle.DerivingViaSpec+  ( module Test.Language.Souffle.DerivingViaSpec+  ) where++import Test.Hspec+import GHC.Generics+import Data.Maybe+import qualified Language.Souffle.Interpreted as Souffle+++data Path = Path+  deriving Souffle.Program+  via Souffle.ProgramOptions Path "path" '[Edge, Reachable]++data Edge = Edge String String+  deriving stock (Eq, Show, Generic)+  deriving anyclass Souffle.Marshal+  deriving Souffle.Fact+  via Souffle.FactOptions Edge "edge" 'Souffle.InputOutput++data Reachable = Reachable String String+  deriving stock (Eq, Show, Generic)+  deriving anyclass Souffle.Marshal+  deriving Souffle.Fact+  via Souffle.FactOptions Reachable "reachable" 'Souffle.Output+++spec :: Spec+spec = describe "Souffle DerivingVia-style API" $ do+  it "can get and put facts from souffle" $ do++    edges <- Souffle.runSouffle Path $ \handle -> do+      let prog = fromJust handle+      Souffle.addFacts prog [Edge "e" "f", Edge "f" "g"]+      Souffle.run prog+      Souffle.getFacts prog+    edges `shouldBe` [Edge "a" "b", Edge "b" "c", Edge "e" "f", Edge "f" "g"]+