packages feed

DSH 0.8.2.3 → 0.10.0.0

raw patch · 113 files changed

+22237/−3180 lines, 113 filesdep +DSHdep +HDBC-postgresqldep +HUnitdep −FerryCoredep −HaXmldep −Pathfinderdep ~basedep ~bytestringdep ~containersnew-component:exe:manualnew-component:exe:vldotnew-uploader

Dependencies added: DSH, HDBC-postgresql, HUnit, QuickCheck, aeson, algebra-dag, algebra-sql, ansi-wl-pprint, dlist, either, kure, pretty, semigroups, set-monad, test-framework, test-framework-hunit, test-framework-quickcheck2

Dependencies removed: FerryCore, HaXml, Pathfinder, array, csv

Dependency ranges changed: base, bytestring, containers, template-haskell, text

Files

DSH.cabal view
@@ -1,5 +1,5 @@ Name:                DSH-Version:             0.8.2.3+Version:             0.10.0.0 Synopsis:            Database Supported Haskell Description:   This is a Haskell library for database-supported program execution. Using@@ -25,25 +25,27 @@   resident data with Haskell.   .   Note that this package is flagged experimental and therefore is not suited-  for production use. This is a proof of concept implementation only. To learn-  more about DSH, our paper entitled as "Haskell Boards the Ferry: Database--  Supported Program Execution for Haskell" [1] is a recommended reading. The-  package includes a couple of examples that demonstrate how to use DSH.+  for production use (we mean it!). This is a proof of concept implementation +  only. To learn more about DSH, our paper entitled as "Haskell Boards the Ferry: +  Database-Supported Program Execution for Haskell" [1] is a recommended reading.+  The package includes a couple of examples that demonstrate how to use DSH.   .-  The latest release implements new features described in our work-in-progress-  paper entitled as "Algebraic Data Types for Language-Integrated-  Queries" [3].+  In contrast to the DSH version described in [1], the current release does+  not rely anymore on the loop-lifting compilation technique together with +  the Pathfinder optimizer. Instead, it brings a completely rewritten query +  compiler based on Guy Blelloch's flattening transformation. This approach+  leads to a more robust compilation and produces more efficient query code.   .-  1. <http://db.inf.uni-tuebingen.de/files/giorgidze/ifl2010.pdf>+  Please read the release notes in 'README.md'.   .-  2. <http://db.inf.uni-tuebingen.de/files/giorgidze/haskell2011.pdf>+  1. <http://db.inf.uni-tuebingen.de/staticfiles/publications/ferryhaskell.pdf>   .-  3. <http://db.inf.uni-tuebingen.de/files/giorgidze/adtq.pdf>+  2. <http://db.inf.uni-tuebingen.de/staticfiles/publications/haskell2011.pdf>  License:             BSD3 License-file:        LICENSE-Author:              George Giorgidze, Alexander Ulrich, Tom Schreiber, Nils Schweinsberg and Jeroen Weijers-Maintainer:          giorgidze@gmail.com, jeroen.weijers@uni-tuebingen.de+Author:              George Giorgidze, Alexander Ulrich, Nils Schweinsberg and Jeroen Weijers+Maintainer:          alex@etc-network.de Stability:           Experimental Category:            Database Build-type:          Simple@@ -51,37 +53,197 @@ Extra-source-files:  examples/Example01.hs                      examples/Example02.hs                      examples/Example03.hs-                     examples/Makefile+                     examples/dshify-tpch.sql                      tests/Main.hs-                     tests/Makefile+		     tests/ComprehensionTests.hs+		     tests/DSHComprehensions.hs+		     tests/CombinatorTests.hs -Cabal-version:       >= 1.4+Cabal-version:       >= 1.8 +Flag debugcomp+  Description: Print debugging information for comprehension rewrites+  Default:     False++Flag debuggraph+  Description: Print debugging information for graph rewrites (VL, TA)+  Default:     False+ Library-  Build-depends:     base               >= 4.5 && < 5,-                     containers         >= 0.4,-                     array              >= 0.4,-                     bytestring         >= 0.9,-                     template-haskell   >= 2.7,+  Extensions:        CPP+  Build-depends:     base               >= 4.7 && < 5,+                     template-haskell   >= 2.9,+                     containers         >= 0.5,                      mtl                >= 2.1,-                     text               >= 0.11,+                     bytestring         >= 0.10,+                     text               >= 1.1,                      HDBC               >= 2.3,-                     HaXml              >= 1.23,-                     csv                >= 0.1,-                     Pathfinder         >= 0.5,-                     FerryCore          >= 0.4+                     HDBC-postgresql    >= 2.3,+                     pretty             >= 1.1,+                     aeson              >= 0.8,+                     kure               >= 2.16,+                     either             >= 4.0,+                     semigroups         >= 0.16,+                     ansi-wl-pprint     >= 0.6,+                     set-monad          >= 0.1,+                     dlist              >= 0.7, +                     algebra-dag        >= 0.1,+                     algebra-sql        >= 0.1+                        Hs-source-dirs:    src -  GHC-options:       -O3 -Wall -fno-warn-orphans+  if flag(debugcomp)+    CPP-Options:     -DDEBUGCOMP+  +  if flag(debuggraph)+    CPP-Options:     -DDEBUGGRAPH -  Exposed-modules:   Database.DSH.Interpreter+  GHC-Options:       -Wall -fno-warn-orphans++  Exposed-modules:   Database.DSH                      Database.DSH.Compiler-                     Database.DSH -  Other-modules:     Database.DSH.Internals-                     Database.DSH.Externals-                     Database.DSH.CSV+  Other-modules:     Database.DSH.Frontend.Internals+                     Database.DSH.Frontend.Schema+                     Database.DSH.Frontend.Externals+                     Database.DSH.Frontend.TH+                     Database.DSH.Frontend.TupleTypes+                     Database.DSH.Frontend.Funs+                     Database.DSH.Translate.Frontend2CL+                     Database.DSH.Execute.TH+                     Database.DSH.Execute.Sql+                     Database.DSH.Execute.Backend+                     Database.DSH.Common.Nat+                     Database.DSH.Common.Pretty+                     Database.DSH.Common.Type+                     Database.DSH.Common.Lang+                     Database.DSH.Common.QueryPlan+                     Database.DSH.Common.RewriteM+                     Database.DSH.Common.Kure+                     Database.DSH.Export+                     Database.DSH.CL.Lang+                     Database.DSH.CL.Kure+                     Database.DSH.CL.Primitives+                     Database.DSH.CL.Opt+                     Database.DSH.CL.Opt.Auxiliary+                     Database.DSH.CL.Opt.PostProcess+                     Database.DSH.CL.Opt.LoopInvariant+                     Database.DSH.CL.Opt.PredPushdown+                     Database.DSH.CL.Opt.Normalize+                     Database.DSH.CL.Opt.CompNormalization+                     Database.DSH.CL.Opt.PartialEval+                     Database.DSH.CL.Opt.FlatJoin+                     Database.DSH.CL.Opt.ThetaJoin+                     Database.DSH.CL.Opt.SemiJoin+                     Database.DSH.CL.Opt.AntiJoin+                     Database.DSH.CL.Opt.NestJoin+                     Database.DSH.CL.Opt.Resugar+                     Database.DSH.FKL.Lang+                     Database.DSH.FKL.Primitives+                     Database.DSH.FKL.Rewrite+                     Database.DSH.FKL.Kure+                     Database.DSH.NKL.Lang+                     Database.DSH.NKL.Kure+                     Database.DSH.NKL.Rewrite+                     Database.DSH.NKL.Primitives+                     Database.DSH.Translate.Algebra2Query+                     Database.DSH.Translate.CL2NKL+                     Database.DSH.Translate.FKL2VL+                     Database.DSH.Translate.NKL2FKL+                     Database.DSH.Translate.VL2Algebra++                     Database.DSH.VL.Lang+                     Database.DSH.VL.Render.Dot+                     Database.DSH.VL.Render.JSON+                     Database.DSH.VL.Vector+                     Database.DSH.VL.VectorAlgebra+                     Database.DSH.VL.VectorAlgebra.TA+                     Database.DSH.VL.Vectorize+                     Database.DSH.VL.Primitives+                      Database.DSH.Impossible-                     Database.DSH.Compile-                     Database.DSH.TH++                     Database.DSH.Optimizer.Common.Auxiliary+                     Database.DSH.Optimizer.Common.Rewrite++                     Database.DSH.Optimizer.VL.Properties.BottomUp+                     Database.DSH.Optimizer.VL.Properties.Card+                     Database.DSH.Optimizer.VL.Properties.Common+                     Database.DSH.Optimizer.VL.Properties.Const+                     Database.DSH.Optimizer.VL.Properties.Empty+                     Database.DSH.Optimizer.VL.Properties.NonEmpty+                     Database.DSH.Optimizer.VL.Properties.ReqColumns+                     Database.DSH.Optimizer.VL.Properties.TopDown+                     Database.DSH.Optimizer.VL.Properties.Types+                     Database.DSH.Optimizer.VL.Properties.VectorType++                     Database.DSH.Optimizer.TA.Properties.BottomUp+                     Database.DSH.Optimizer.TA.Properties.TopDown+                     Database.DSH.Optimizer.TA.Properties.Types+                     Database.DSH.Optimizer.TA.Properties.Cols+                     Database.DSH.Optimizer.TA.Properties.ICols+                     Database.DSH.Optimizer.TA.Properties.Use+                     Database.DSH.Optimizer.TA.Properties.Auxiliary+                     Database.DSH.Optimizer.TA.Properties.Empty+                     Database.DSH.Optimizer.TA.Properties.Card1+                     Database.DSH.Optimizer.TA.Properties.Keys+                     Database.DSH.Optimizer.TA.Properties.Order+                     Database.DSH.Optimizer.TA.Properties.Const+                     Database.DSH.Optimizer.TA.Rewrite.Basic+                     Database.DSH.Optimizer.TA.Rewrite.Common+                     Database.DSH.Optimizer.TA.OptimizeTA+		     +                     Database.DSH.Optimizer.Common.Rewrite+                     Database.DSH.Optimizer.VL.OptimizeVL+                     Database.DSH.Optimizer.VL.Rewrite.Common+                     Database.DSH.Optimizer.VL.Rewrite.Expressions+                     Database.DSH.Optimizer.VL.Rewrite.PruneEmpty+                     Database.DSH.Optimizer.VL.Rewrite.Redundant+                     Database.DSH.Optimizer.VL.Rewrite.Aggregation+                     Database.DSH.Optimizer.VL.Rewrite.Window+                     Database.DSH.Optimizer.VL.Rewrite.Unused++executable vldot+    Main-is: Database/DSH/Tools/VLDotGen.hs+    GHC-Options:      -Wall -fno-warn-orphans+    hs-source-dirs:   src+    build-depends:    base                >= 4.7 && < 5, +                      mtl                 >= 2.1, +                      pretty              >= 1.1, +                      aeson               >= 0.8, +                      containers          >= 0.5,+                      template-haskell    >= 2.9, +                      bytestring          >= 0.10,+                      ansi-wl-pprint      >= 0.6,+                      semigroups          >= 0.16,++                      algebra-dag          >= 0.1,+                      algebra-sql          >= 0.1+    GHC-Options: -Wall -fno-warn-orphans++executable manual+    hs-source-dirs: tests+    main-is:        Manual.hs+    build-depends:  base, DSH, HDBC-postgresql, text+    ghc-options:    -Wall -fno-warn-orphans++Test-Suite Flattening_TA+    type:       exitcode-stdio-1.0+    Hs-Source-Dirs : tests+    Main-is:       Main.hs+    Build-depends: base                       >= 4.7 && < 5,+                   QuickCheck                 >= 2.4,+                   containers                 >= 0.5,+                   text                       >= 1.1,+                   HDBC-postgresql            >= 2.3,+                   HDBC                       >= 2.3,+                   test-framework-quickcheck2 >= 0.2,+                   test-framework-hunit       >= 0.3,+                   test-framework             >= 0.6,+                   HUnit                      >= 1.2,++                   DSH                        >= 0.10+    cpp-options:   -DTESTSQL+    GHC-Options: -Wall -fno-warn-orphans+    Extensions: CPP
examples/Example01.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RebindableSyntax    #-} {-# LANGUAGE ViewPatterns        #-} +-- | Some simple example queries over (literal) integer lists. module Main where  import qualified Prelude as P@@ -10,26 +11,82 @@  import Database.HDBC.PostgreSQL +-- The 'toQ' combinator constructs a query that returns a given native+-- Haskell value. ints :: Q [Integer] ints = toQ [1 .. 10] +ints2 :: Q [Integer]+ints2 = toQ $ [1..3] P.++ [7..12]++-- Comprehensions are the main way to express queries query1 :: Q [(Integer,Integer)] query1 =  [ pair i1 i2           | i1 <- ints-          , i2 <- ints+          , i2 <- ints2+          , i1 == i2           ] +-- Pattern matching on tuples is supported using View Patterns. query2 :: Q [(Integer,Integer)] query2 =  [ pair i1 i2           | (view -> (i1,i2)) <- query1-          , i1 == i2+          , i1 > 3           ] +-- List combinators can be used freely.+query3 :: Q [(Integer, Integer)]+query3 = zip (drop 1 ints) ints++-- Existential quantification+query4 :: Q [Integer]+query4 = [ x | x <- ints, x `elem` ints2 ]++-- Existential quantification expressed using a boolean aggregate.+query5 :: Q [Integer]+query5 = [ x | x <- ints, or [ x == y | y <- ints2 ] ]++-- Existential and universal quantification+query6 :: Q [Integer]+query6 = [ x | x <- ints, or [ x == y | y <- ints2 ] ]+         +++         [ x | x <- ints, and [ not $ x == y | y <- ints2 ] ]++-- Query results may be nested.+query7 :: Q [[Integer]]+query7 = [ [ y | y <- ints, y < x ] | x <- ints2 ] ++-- Sorting+query8 :: Q [(Integer, Integer)]       +query8 = take 3 $ sortWith fst $ toQ [(3,4),(5,1),(9,12),(8,3),(6,15)]++xys :: Q [(Integer, Integer)]+xys = toQ [(3,5),(4,5),(3,8),(3,9),(5,6),(4,0)]++-- Grouping and aggregation+query9 :: Q [(Integer, Integer)]+query9 = [ pair k (sum [ snd ge | ge <- g ])+         | (view -> (k, g)) <- groupWithKey (\xy -> fst xy)  xys+         ]++-- To execute queries, a HDBC connection to a PostgreSQL database must+-- be supplied. getConn :: IO Connection-getConn = connectPostgreSQL "user = 'giorgidz' password = '' host = 'localhost' dbname = 'giorgidz'"+getConn = connectPostgreSQL "user = 'au' password = 'foobar' host = 'localhost' dbname = 'test'" -runQ :: (Show a,QA a) => Q a -> IO ()-runQ q = getConn P.>>= \conn -> (fromQ conn q P.>>= P.print) P.>> disconnect conn+-- Given a connection, queries are executed using the 'runQ'+-- combinator.+execQ :: (Show a,QA a) => Q a -> IO ()+execQ q = getConn P.>>= \conn -> (runQ conn q P.>>= P.print) P.>> disconnect conn  main :: IO ()-main = runQ ints P.>> runQ query1 P.>> runQ query2+main = execQ ints +       P.>> execQ query1 +       P.>> execQ query2 +       P.>> execQ query3+       P.>> execQ query4+       P.>> execQ query5+       P.>> execQ query6+       P.>> execQ query7+       P.>> execQ query8+       P.>> execQ query9
examples/Example02.hs view
@@ -13,29 +13,56 @@  employees :: Q [(Text, Text, Integer)] employees = toQ [ ("Simon",  "MS",   80)-							  , ("Erik",   "MS",   90)-							  , ("Phil",   "Ed",   40)-							  , ("Gordon", "Ed",   45)-							  , ("Paul",   "Yale", 60)-							  ]+                , ("Erik",   "MS",   90)+                , ("Phil",   "Ed",   40)+                , ("Gordon", "Ed",   45)+                , ("Paul",   "Yale", 60)+                ] +dept :: Q (Text, Text, Integer) -> Q Text+dept (view -> (_, d, _)) = d++sal :: Q (Text, Text, Integer) -> Q Integer+sal (view -> (_, _, s)) = s++name :: Q (Text, Text, Integer) -> Q Text+name (view -> (n, _, _)) = n++-- The duplicate-free list of departments. departments :: Q [Text]-departments = nub [ dept | (view -> (_name,dept,_salary)) <- employees]+departments = nub [ dept e | e <- employees] -deptSalary :: Q Text -> Q Integer-deptSalary dept = sum [ salary-                      | (view -> (_name,dept',salary)) <- employees-                      , dept == dept']+-- The total salary for a given department+deptSalary :: Q Text -> Q Double+deptSalary d = avg [ sal e | e <- employees , d == dept e ] -mainQuery :: Q [(Text,Integer)]-mainQuery = [ pair dept (deptSalary dept)-            | dept <- departments]+-- For each department, compute the total salary.+deptSalaries :: Q [(Text, Double)]+deptSalaries = [ pair d (deptSalary d)+               | d <- departments+               ] +-- Alternatively, employ the 'groupWithKey' combinator to express+-- grouping.+deptSalaries' :: Q [(Text, Double)]+deptSalaries' = [ pair d (avg [ sal ge | ge <- g ])+                | (view -> (d, g)) <- groupWithKey dept employees+                ]++-- Query with a nested result: For each department, compute the list+-- of employees.+employeesPerDept :: Q [(Text, [Text])]+employeesPerDept = [ pair d [ name e | e <- employees, dept e == d ]+                   | d <- departments+                   ]+ getConn :: IO Connection-getConn = connectPostgreSQL "user = 'giorgidz' password = '' host = 'localhost' dbname = 'giorgidz'"+getConn = connectPostgreSQL "user = 'au' password = 'foobar' host = 'localhost' dbname = 'test'" -runQ :: (Show a,QA a) => Q a -> IO ()-runQ q = getConn P.>>= \conn -> (fromQ conn q P.>>= P.print) P.>> disconnect conn+execQ :: (Show a,QA a) => Q a -> IO ()+execQ q = getConn P.>>= \conn -> (runQ conn q P.>>= P.print) P.>> disconnect conn  main :: IO ()-main = runQ mainQuery+main = execQ deptSalaries+       P.>> execQ deptSalaries'+       P.>> execQ employeesPerDept
examples/Example03.hs view
@@ -9,6 +9,13 @@ {-# LANGUAGE UndecidableInstances  #-} {-# LANGUAGE ViewPatterns          #-} +-- | A number of more complex DSH examples based on the TPC-H+-- benchmark schema. A data generator is available at+-- <http://www.tpc.org/tpch/>. Note that DSH currently does not+-- support temporal and decimal types. Those have to be mapped to+-- epoch integers and doubles, respectively. The script+-- 'examples/dshify-tpch.sql' modifies a PostgreSQL TPCH database+-- accordingly. module Main where  import qualified Prelude as P@@ -17,85 +24,132 @@  import Database.HDBC.PostgreSQL -data Employee = Prof  { name            :: Text-                      , chair           :: Text-                      , advisedStudents :: [Text]-                      }-              | Stud  { name    :: Text-                      , topic   :: Text-                      , advisor :: Text-                      }-              deriving (Eq,Ord,Show)+-- | We declare a flat record type that maps to the relational+-- schema. Names of record selectors should match attribute names in+-- the schema. Note that record selectors should be listed in+-- alphabetical order.+data LineItem = LineItem+    { l_comment       :: Text+    , l_commitdate    :: Integer+    , l_discount      :: Double+    , l_extendedprice :: Double+    , l_linenumber    :: Integer+    , l_linestatus    :: Text+    , l_orderkey      :: Integer+    , l_partkey       :: Integer+    , l_quantity      :: Double+    , l_receiptdate   :: Integer+    , l_returnflag    :: Text+    , l_shipdate      :: Integer+    , l_shipinstruct  :: Text+    , l_shipmode      :: Text+    , l_suppkey       :: Integer+    , l_tax           :: Double+    }+    deriving (Show) -deriveDSH ''Employee+-- A bit of Template Haskell code automatically derives instances and+-- infrastructure to use the record type in DSH queries. For each+-- record selector, we derive a variant that can be used in+-- queries. For example, we get a lifted record selector +-- 'l_commentQ :: Q LineItem -> Q Text'.+deriveDSH ''LineItem+deriveTA ''LineItem+generateTableSelectors ''LineItem -students :: Q [(Integer,Text,Text,Text)]-students = toQ  [ (1,"J","P","T")-                , (2,"A","Q","T")-                ]+-- | The 'table' combinator implements a database table scan.+lineitems :: Q [LineItem]+lineitems = table "lineitem" $ TableHints [Key ["l_orderkey", "l_linenumber"]] NonEmpty -professors :: Q [(Integer,Text,Text)]-professors = toQ  [ (0,"T","DB")]+data Order = Order+    { o_clerk         :: Text+    , o_comment       :: Text+    , o_custkey       :: Integer+    , o_orderdate     :: Integer+    , o_orderkey      :: Integer+    , o_orderpriority :: Text+    , o_orderstatus   :: Text+    , o_shippriority  :: Integer+    , o_totalprice    :: Double+    }+    deriving (Show) -employment :: Q [(Integer,Text,Text,Integer)]-employment = toQ  [ (0,"DB","professor",2008)-                  , (1,"DB","student",2010)-                  , (2,"DB","student",2011)-                  , (3,"PL","student",2012)-                  ]+deriveDSH ''Order+deriveTA ''Order+generateTableSelectors ''Order -salaries :: Q [(Integer,Integer)]-salaries = toQ  [ (0,4096)-                , (1,2048)-                , (2,2048)-                ]+orders :: Q [Order]+orders = table "orders" $ TableHints [Key ["o_orderkey"]] NonEmpty -employeesBySeniority :: Q [Employee]-employeesBySeniority = concat-  [ if eStatus == "student"-       then  [ stud sName sTopic sAdvisor-             | (view -> (sID, sName, sTopic, sAdvisor)) <- students-             , eID == sID-             ]-       else  [  prof pName pChair sAdvised-             |  (view -> (pID, pName, pChair)) <- professors-             ,  eID == pID-             ,  let sAdvised = [ sName-                               | (view -> (_, sName, _, sAdvisor)) <- students-                               , sAdvisor == pName-                               ]-             ]-  | (view -> (eID, _, eStatus, _)) <- sortWith (\(view -> (_, _, _, d)) -> d) employment-  ]+-------------------------------------------------------------------------------- -safeMinimum :: (Ord a, QA a) => Q [a] -> Q (Maybe a)-safeMinimum as = if null as then nothing else just (minimum as) +-- Select all lineitems that were shipped before a given date+ordersBefore :: Q Integer -> Q [LineItem]+ordersBefore date = [ li | li <- lineitems , l_shipdateQ li <= date ] -salPerDept :: Q [(Text, [Integer])]-salPerDept =-  [ pair dept [ salary-              | (view -> (sID,salary)) <- salaries-              , (view -> (dID,_,_,_)) <- deptMembers-              , sID == dID-              ]-  | (view -> (dept, deptMembers)) <- groupWithKey (\(view -> (_,d,_,_)) -> d) employment-  ]+fst9 :: (QA a, QA b, QA c, QA d, QA e, QA f, QA g, QA h, QA i) => Q (a, b, c, d, e, f, g, h, i) -> Q a+fst9 (view -> (a, _, _, _, _, _, _, _, _)) = a -minSalPerDept :: Q [(Text, Integer)]-minSalPerDept =  [ pair dept (elim (safeMinimum sals)-                                   0-                                   (\minSal -> minSal))-                 | (view -> (dept, sals)) <- salPerDept-                 ]+-- Compute the revenue of a single lineitem+revenue :: Q LineItem -> Q Double+revenue li = l_extendedpriceQ li * (1 - l_discountQ li) +-- TPC-H benchmark query Q1+q1 :: Q Integer +   -> Q [((Text, Text), Double, Double, Double, Double, Double, Double, Double, Integer)]+q1 maxDate = sortWith fst9 $ +     [ tup9+	  k+	  (sum $ map l_quantityQ lis)+	  (sum $ map l_extendedpriceQ lis)+	  (sum $ map revenue lis)+	  (sum $ map (\li -> revenue li * (1 + l_taxQ li)) lis)+	  (avg $ map l_quantityQ lis)+	  (avg $ map l_extendedpriceQ lis)+	  (avg $ map l_discountQ lis)+	  (length lis)+      | (view -> (k, lis)) <- groupWithKey (\li -> pair (l_returnflagQ li) (l_linestatusQ li)) +                              $ ordersBefore maxDate+      ]++--------------------------------------------------------------------------------++data Range = Range { start :: Integer, end :: Integer }++inside :: Q Integer -> Range -> Q Bool+inside d range = d >= toQ (start range) && d < toQ (end range)++lineItemsOf :: Q Order -> Q [LineItem]+lineItemsOf o = [ l | l <- lineitems,+                      l_orderkeyQ l == o_orderkeyQ o ]++-- Has at least one of the orders' items been delivered late?+hasLateItem :: Q Order -> Q Bool+hasLateItem o =+  or [ l_commitdateQ l < l_receiptdateQ l | l <- lineItemsOf o ]++-- Compute the number of delayed orders per priority level in the+-- given quarter (TPC-H benchmark query Q4).+q4 :: Range -> Q [(Text, Integer)]+q4 quarter = sortWith fst+  [ pair priority (length delays)+  | (view -> (priority, delays)) <- groupWithKey o_orderpriorityQ delayedOrders ]+  where+    delayedOrders = [ o | o <- orders+                    , o_orderdateQ o `inside` quarter+                    , hasLateItem o+                    ]++--------------------------------------------------------------------------------+ getConn :: IO Connection-getConn = connectPostgreSQL "user = 'giorgidz' password = '' host = 'localhost' dbname = 'giorgidz'"+getConn = connectPostgreSQL "user = 'au' password = 'foobar' host = 'localhost' dbname = 'tpchsmall'" -runQ :: (Show a,QA a) => Q a -> IO ()-runQ q = getConn P.>>= \conn -> (fromQ conn q P.>>= P.print) P.>> disconnect conn+execQ :: (Show a,QA a) => Q a -> IO ()+execQ q = getConn P.>>= \conn -> (runQ conn q P.>>= P.print) P.>> disconnect conn  main :: IO ()-main = sequence_  [ runQ employeesBySeniority-                  , runQ salPerDept-                  , runQ minSalPerDept+main = sequence_  [ execQ $ q1 904663977+                  -- Compute Q4 for a three-month interval+                  , execQ (q4 $ Range 741540777 749489577)                   ]
− examples/Makefile
@@ -1,8 +0,0 @@-all: clean-		ghc -Wall -O3 --make Example01.hs-		ghc -Wall -O3 --make Example02.hs-		ghc -Wall -O3 --make Example03.hs-		rm -rf *.hi *.o--clean:-		rm -rf *.hi *.o Example01 Example02 Example03
+ examples/dshify-tpch.sql view
@@ -0,0 +1,19 @@+alter table supplier alter column s_acctbal type real;++alter table lineitem alter column l_acctbal type real;++alter table customer alter column c_acctbal type real;++alter table lineitem alter column l_quantity type real;+alter table lineitem alter column l_extendedprice type real;+alter table lineitem alter column l_discount type real;+alter table lineitem alter column l_tax type real;++alter table orders alter column o_totalprice type real;+alter table orders alter column o_orderdate type int using extract(epoch from o_orderdate); ++alter table lineitem alter column l_shipdate type int using extract(epoch from l_shipdate); +alter table lineitem alter column l_commitdate type int using extract(epoch from l_commitdate); +alter table lineitem alter column l_receiptdate type int using extract(epoch from l_receiptdate); ++alter table part alter column p_retailprice type real;
src/Database/DSH.hs view
@@ -1,4 +1,4 @@--- |+-- |   -- This module is intended to be imported @qualified@, to avoid name clashes -- with "Prelude" functions. For example: --@@ -14,9 +14,9 @@ -- by Database.DSH.  module Database.DSH-  ( module Database.DSH.Externals-  , Q, QA, TA, Elim, elim, View, view-  , module Database.DSH.TH+  ( module Database.DSH.Frontend.Externals+  , Q, QA, TA, Elim, elim, View, view, Key(..), TableHints(..), Emptiness(..)+  , module Database.DSH.Frontend.TH   , module Data.String   , module Data.Text   , module Database.HDBC@@ -24,9 +24,9 @@   )   where -import Database.DSH.Externals-import Database.DSH.Internals (Q,QA,TA,Elim,elim,View,view)-import Database.DSH.TH+import Database.DSH.Frontend.Externals+import Database.DSH.Frontend.Internals (Q,QA,TA,Elim,elim,View,view,Key(..),TableHints(..), Emptiness(..))+import Database.DSH.Frontend.TH  import Data.String (IsString,fromString) import Data.Text (Text)@@ -35,12 +35,13 @@     not   , (&&)   , (||)-  , (==)-  , (/=)+  , (==) , (/=)   , (<)   , (<=)   , (>=)   , (>)+  , (++)+  , mod   , min   , max   , head@@ -86,4 +87,5 @@   , return   , (>>=)   , (>>)+  , div   )
+ src/Database/DSH/CL/Kure.hs view
@@ -0,0 +1,461 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE InstanceSigs          #-}++-- | Infrastructure for KURE-based rewrites on CL expressions+   +module Database.DSH.CL.Kure+    ( -- * Re-export relevant KURE modules+      module Language.KURE+    , module Language.KURE.Lens++      -- * The KURE monad+    , RewriteM, RewriteStateM, TransformC, RewriteC, LensC, freshName, freshNameT+    +      -- * Setters and getters for the translation state+    , get, put, modify+    +      -- * Changing between stateful and non-stateful transforms+    , statefulT, liftstateT++      -- * The KURE context+    , CompCtx(..), CrumbC(..), PathC, initialCtx, freeIn, boundIn+    , inScopeNames, bindQual, bindVar, withLocalPathT++      -- * Congruence combinators+    , tableT, appe1T, appe2T, binopT, ifT, litT, varT, compT, letT+    , tableR, appe1R, appe2R, binopR, ifR, litR, varR, compR, letR+    , unopR, unopT+    , bindQualT, guardQualT, bindQualR, guardQualR+    , qualsT, qualsR, qualsemptyT, qualsemptyR+    +      -- * The sum type+    , CL(..)+    ) where+    +       +import           Control.Monad+import           Data.Monoid+import qualified Data.Map as M+import qualified Data.Foldable as F+import           Text.PrettyPrint.ANSI.Leijen(text)++import           Language.KURE+import           Language.KURE.Lens+       +import           Database.DSH.Common.Pretty+import qualified Database.DSH.Common.Lang as L+import           Database.DSH.Common.RewriteM+import           Database.DSH.CL.Lang+                 +--------------------------------------------------------------------------------+-- Convenience type aliases++type TransformC a b = Transform CompCtx (RewriteM Int) a b+type RewriteC a     = TransformC a a+type LensC a b      = Lens CompCtx (RewriteM Int) a b++--------------------------------------------------------------------------------++data CrumbC = AppFun+            | AppArg+            | AppE1Arg+            | AppE2Arg1+            | AppE2Arg2+            | BinOpArg1+            | BinOpArg2+            | UnOpArg+            | LamBody+            | IfCond+            | IfThen+            | IfElse+            | CompHead+            | CompQuals+            | BindQualExpr+            | GuardQualExpr+            | QualsHead+            | QualsTail+            | QualsSingleton+            | NLConsTail+            -- One-based index into the list of element expressions+            | TupleElem Int+            | LetBind+            | LetBody+            deriving (Eq, Show)++instance Pretty CrumbC where+    pretty c = text $ show c++type AbsPathC = AbsolutePath CrumbC++type PathC = Path CrumbC++-- | The context for KURE-based CL rewrites+data CompCtx = CompCtx { cl_bindings :: M.Map L.Ident Type +                       , cl_path     :: AbsPathC+                       }+                       +instance ExtendPath CompCtx CrumbC where+    c@@n = c { cl_path = cl_path c @@ n }+    +instance ReadPath CompCtx CrumbC where+    absPath c = cl_path c++initialCtx :: CompCtx+initialCtx = CompCtx { cl_bindings = M.empty, cl_path = mempty }++-- | Record a variable binding in the context+bindVar :: L.Ident -> Type -> CompCtx -> CompCtx+bindVar n ty ctx = ctx { cl_bindings = M.insert n ty (cl_bindings ctx) }++-- | If the qualifier represents a generator, bind the variable in the context.+bindQual :: CompCtx -> Qual -> CompCtx+bindQual ctx (BindQ n e) = bindVar n (elemT $ typeOf e) ctx+bindQual ctx _           = ctx+         +inScopeNames :: CompCtx -> [L.Ident]+inScopeNames = M.keys . cl_bindings++boundIn :: L.Ident -> CompCtx -> Bool+boundIn n ctx = n `M.member` (cl_bindings ctx)++freeIn :: L.Ident -> CompCtx -> Bool+freeIn n ctx = n `M.notMember` (cl_bindings ctx)++-- | Generate a fresh name that is not bound in the current context.+freshNameT :: [L.Ident] -> TransformC a L.Ident+freshNameT avoidNames = do+    ctx <- contextT+    constT $ freshName (avoidNames ++ inScopeNames ctx)++-- | Perform a transform with an empty path, i.e. a path starting from+-- the current node.+withLocalPathT :: Monad m => Transform CompCtx m a b -> Transform CompCtx m a b+withLocalPathT t = transform $ \c a -> applyT t (c { cl_path = SnocPath [] }) a++--------------------------------------------------------------------------------+-- Support for stateful transforms++-- | Run a stateful transform with an initial state and turn it into a regular+-- (non-stateful) transform+statefulT :: s -> Transform CompCtx (RewriteStateM s) a b -> TransformC a (s, b)+statefulT s t = resultT (stateful s) t++-- | Turn a regular rewrite into a stateful rewrite+liftstateT :: Transform CompCtx (RewriteM Int) a b -> Transform CompCtx (RewriteStateM s) a b+liftstateT t = resultT liftstate t++--------------------------------------------------------------------------------+-- Congruence combinators for CL expressions++tableT :: Monad m => (Type -> String -> [L.Column] -> L.TableHints -> b)+                  -> Transform CompCtx m Expr b+tableT f = contextfreeT $ \expr -> case expr of+                      Table ty n cs hs -> return $ f ty n cs hs+                      _                -> fail "not a table node"+{-# INLINE tableT #-}                      +                      +tableR :: Monad m => Rewrite CompCtx m Expr+tableR = tableT Table+{-# INLINE tableR #-}+                                       +appe1T :: Monad m => Transform CompCtx m Expr a+                  -> (Type -> Prim1 -> a -> b)+                  -> Transform CompCtx m Expr b+appe1T t f = transform $ \c expr -> case expr of+                      AppE1 ty p e -> f ty p <$> applyT t (c@@AppE1Arg) e                  +                      _            -> fail "not a unary primitive application"+{-# INLINE appe1T #-}                      +                      +appe1R :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr+appe1R t = appe1T t AppE1+{-# INLINE appe1R #-}                      +                      +appe2T :: Monad m => Transform CompCtx m Expr a1+                  -> Transform CompCtx m Expr a2+                  -> (Type -> Prim2 -> a1 -> a2 -> b)+                  -> Transform CompCtx m Expr b+appe2T t1 t2 f = transform $ \c expr -> case expr of+                     AppE2 ty p e1 e2 -> f ty p <$> applyT t1 (c@@AppE2Arg1) e1 +                                                <*> applyT t2 (c@@AppE2Arg2) e2+                     _                -> fail "not a binary primitive application"+{-# INLINE appe2T #-}                      ++appe2R :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr+appe2R t1 t2 = appe2T t1 t2 AppE2+{-# INLINE appe2R #-}                      +                     +binopT :: Monad m => Transform CompCtx m Expr a1+                  -> Transform CompCtx m Expr a2+                  -> (Type -> L.ScalarBinOp -> a1 -> a2 -> b)+                  -> Transform CompCtx m Expr b+binopT t1 t2 f = transform $ \c expr -> case expr of+                     BinOp ty op e1 e2 -> f ty op <$> applyT t1 (c@@BinOpArg1) e1 +                                                  <*> applyT t2 (c@@BinOpArg2) e2+                     _                 -> fail "not a binary operator application"+{-# INLINE binopT #-}                      ++binopR :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr+binopR t1 t2 = binopT t1 t2 BinOp+{-# INLINE binopR #-}                      ++unopT :: Monad m => Transform CompCtx m Expr a+                 -> (Type -> L.ScalarUnOp -> a -> b)+                 -> Transform CompCtx m Expr b+unopT t f = transform $ \ctx expr -> case expr of+                     UnOp ty op e -> f ty op <$> applyT t (ctx@@UnOpArg) e+                     _            -> fail "not an unary operator application"+{-# INLINE unopT #-}++unopR :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr+unopR t = unopT t UnOp+{-# INLINE unopR #-}+                     +ifT :: Monad m => Transform CompCtx m Expr a1+               -> Transform CompCtx m Expr a2+               -> Transform CompCtx m Expr a3+               -> (Type -> a1 -> a2 -> a3 -> b)+               -> Transform CompCtx m Expr b+ifT t1 t2 t3 f = transform $ \c expr -> case expr of+                    If ty e1 e2 e3 -> f ty <$> applyT t1 (c@@IfCond) e1               +                                           <*> applyT t2 (c@@IfThen) e2+                                           <*> applyT t3 (c@@IfElse) e3+                    _              -> fail "not an if expression"+{-# INLINE ifT #-}                      +                    +ifR :: Monad m => Rewrite CompCtx m Expr+               -> Rewrite CompCtx m Expr+               -> Rewrite CompCtx m Expr+               -> Rewrite CompCtx m Expr+ifR t1 t2 t3 = ifT t1 t2 t3 If               +{-# INLINE ifR #-}                      +                    +litT :: Monad m => (Type -> L.Val -> b) -> Transform CompCtx m Expr b+litT f = contextfreeT $ \expr -> case expr of+                    Lit ty v -> return $ f ty v+                    _          -> fail "not a constant"+{-# INLINE litT #-}                      +                    +litR :: Monad m => Rewrite CompCtx m Expr+litR = litT Lit+{-# INLINE litR #-}                      +                    +varT :: Monad m => (Type -> L.Ident -> b) -> Transform CompCtx m Expr b+varT f = contextfreeT $ \expr -> case expr of+                    Var ty n -> return $ f ty n+                    _        -> fail "not a variable"+{-# INLINE varT #-}                      +                    +varR :: Monad m => Rewrite CompCtx m Expr+varR = varT Var+{-# INLINE varR #-}                      ++compT :: Monad m => Transform CompCtx m Expr a1+                 -> Transform CompCtx m (NL Qual) a2+                 -> (Type -> a1 -> a2 -> b)+                 -> Transform CompCtx m Expr b+compT t1 t2 f = transform $ \ctx expr -> case expr of+                    Comp ty e qs -> f ty <$> applyT t1 (F.foldl' bindQual (ctx@@CompHead) qs) e +                                         <*> applyT t2 (ctx@@CompQuals) qs+                    _            -> fail "not a comprehension"+{-# INLINE compT #-}                      +                    +compR :: Monad m => Rewrite CompCtx m Expr+                 -> Rewrite CompCtx m (NL Qual)+                 -> Rewrite CompCtx m Expr+compR t1 t2 = compT t1 t2 Comp                 +{-# INLINE compR #-}                      ++mkTupleT :: Monad m => Transform CompCtx m Expr a+                    -> (Type -> [a] -> b)+                    -> Transform CompCtx m Expr b+mkTupleT t f = transform $ \c expr -> case expr of+                   MkTuple ty es -> f ty <$> zipWithM (\e i -> applyT t (c@@TupleElem i) e) es [1..]+                   _             -> fail "not a tuple constructor"+{-# INLINE mkTupleT #-}++mkTupleR :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Expr+mkTupleR r = mkTupleT r MkTuple++letT :: Monad m => Transform CompCtx m Expr a1+                -> Transform CompCtx m Expr a2+                -> (Type -> L.Ident -> a1 -> a2 -> b) +                -> Transform CompCtx m Expr b+letT t1 t2 f = transform $ \c expr -> case expr of+                 Let ty x xs e -> f ty x <$> applyT t1 (c@@LetBind) xs +                                         <*> applyT t2 (bindVar x (typeOf xs) $ c@@LetBody) e+                 _             -> fail "not a let expression"++letR :: Monad m => Rewrite CompCtx m Expr +                -> Rewrite CompCtx m Expr +                -> Rewrite CompCtx m Expr+letR r1 r2 = letT r1 r2 Let++--------------------------------------------------------------------------------+-- Congruence combinators for qualifiers++bindQualT :: Monad m => Transform CompCtx m Expr a +                     -> (L.Ident -> a -> b) +                     -> Transform CompCtx m Qual b+bindQualT t f = transform $ \ctx expr -> case expr of+                BindQ n e -> f n <$> applyT t (ctx@@BindQualExpr) e+                _         -> fail "not a generator"+{-# INLINE bindQualT #-}                      +                +bindQualR :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Qual+bindQualR t = bindQualT t BindQ+{-# INLINE bindQualR #-}                      ++guardQualT :: Monad m => Transform CompCtx m Expr a +                      -> (a -> b) +                      -> Transform CompCtx m Qual b+guardQualT t f = transform $ \ctx expr -> case expr of+                GuardQ e -> f <$> applyT t (ctx@@GuardQualExpr) e+                _        -> fail "not a guard"+{-# INLINE guardQualT #-}                      +                +guardQualR :: Monad m => Rewrite CompCtx m Expr -> Rewrite CompCtx m Qual+guardQualR t = guardQualT t GuardQ+{-# INLINE guardQualR #-}                      ++--------------------------------------------------------------------------------+-- Congruence combinator for a qualifier list++qualsT :: Monad m => Transform CompCtx m Qual a1+                  -> Transform CompCtx m (NL Qual) a2+                  -> (a1 -> a2 -> b) +                  -> Transform CompCtx m (NL Qual) b+qualsT t1 t2 f = transform $ \ctx quals -> case quals of+                   q :* qs -> f <$> applyT t1 (ctx@@QualsHead) q +                                <*> applyT t2 (bindQual (ctx@@QualsTail) q) qs+                   S _     -> fail "not a nonempty cons"+{-# INLINE qualsT #-}                      +                   +qualsR :: Monad m => Rewrite CompCtx m Qual+                  -> Rewrite CompCtx m (NL Qual)+                  -> Rewrite CompCtx m (NL Qual)+qualsR t1 t2 = qualsT t1 t2 (:*)                  +{-# INLINE qualsR #-}                      ++                   +qualsemptyT :: Monad m => Transform CompCtx m Qual a+                       -> (a -> b)+                       -> Transform CompCtx m (NL Qual) b+qualsemptyT t f = transform $ \ctx quals -> case quals of+                      S q -> f <$> applyT t (ctx@@QualsSingleton) q+                      _   -> fail "not a nonempty singleton"+{-# INLINE qualsemptyT #-}                      +                      +qualsemptyR :: Monad m => Rewrite CompCtx m Qual+                       -> Rewrite CompCtx m (NL Qual)+qualsemptyR t = qualsemptyT t S                       +{-# INLINE qualsemptyR #-}                      ++--------------------------------------------------------------------------------+       +-- | The sum type of *nodes* considered for KURE traversals+data CL = ExprCL Expr+        | QualCL Qual+        | QualsCL (NL Qual)+        +instance Pretty CL where+    pretty (ExprCL e)   = pretty e+    pretty (QualCL q)   = pretty q+    pretty (QualsCL qs) = pretty qs+        +instance Injection Expr CL where+    inject                = ExprCL+    +    project (ExprCL expr) = Just expr+    project _             = Nothing++instance Injection Qual CL where+    inject             = QualCL+    +    project (QualCL q) = Just q+    project _          = Nothing+    +instance Injection (NL Qual) CL where+    inject               = QualsCL+    +    project (QualsCL qs) = Just qs+    project _            = Nothing++    +-- FIXME putting an INLINE pragma on allR would propably lead to good+-- things. However, with 7.6.3 it triggers a GHC panic.+instance Walker CompCtx CL where+    allR :: forall m. MonadCatch m => Rewrite CompCtx m CL -> Rewrite CompCtx m CL+    allR r = +        rewrite $ \c cl -> case cl of+            ExprCL expr -> inject <$> applyT allRexpr c expr+            QualCL q    -> inject <$> applyT allRqual c q+            QualsCL qs  -> inject <$> applyT allRquals c qs+    +      where+        allRquals = readerT $ \qs -> case qs of+            S{}    -> qualsemptyR (extractR r)+            (:*){} -> qualsR (extractR r) (extractR r)+        {-# INLINE allRquals #-}++        allRqual = readerT $ \q -> case q of+            GuardQ{} -> guardQualR (extractR r)+            BindQ{}  -> bindQualR (extractR r)+        {-# INLINE allRqual #-}++        allRexpr = readerT $ \e -> case e of+            Table{}   -> idR+            AppE1{}   -> appe1R (extractR r)+            AppE2{}   -> appe2R (extractR r) (extractR r)+            BinOp{}   -> binopR (extractR r) (extractR r)+            UnOp{}    -> unopR (extractR r)+            If{}      -> ifR (extractR r) (extractR r) (extractR r)+            Lit{}     -> idR+            Var{}     -> idR+            Comp{}    -> compR (extractR r) (extractR r)+            MkTuple{} -> mkTupleR (extractR r)+            Let{}     -> letR (extractR r) (extractR r)+        {-# INLINE allRexpr #-}+            +--------------------------------------------------------------------------------+-- A Walker instance for qualifier lists so that we can use the+-- traversal infrastructure on lists.+   +consT :: Monad m => Transform CompCtx m (NL Qual) b+                 -> (Qual -> b -> c)+                 -> Transform CompCtx m (NL Qual) c+consT t f = transform $ \ctx nl -> case nl of+                a :* as -> f a <$> applyT t (bindQual (ctx@@NLConsTail) a) as+                S _     -> fail "not a nonempty cons"+{-# INLINE consT #-}                      +                    +consR :: Monad m => Rewrite CompCtx m (NL Qual) +                 -> Rewrite CompCtx m (NL Qual)+consR t = consT t (:*)                 +{-# INLINE consR #-}                      ++singletonT :: Monad m => (Qual -> c)+                      -> Transform CompCtx m (NL Qual) c+singletonT f = contextfreeT $ \nl -> case nl of+                   S a    -> return $ f a+                   _ :* _ -> fail "not a nonempty singleton"+{-# INLINE singletonT #-}                      +               +singletonR :: Monad m => Rewrite CompCtx m (NL Qual)+singletonR = singletonT S                      +{-# INLINE singletonR #-}                      +                   +instance Walker CompCtx (NL Qual) where+    allR r = consR r <+ singletonR+    +--------------------------------------------------------------------------------+-- I find it annoying that Applicative is not a superclass of Monad.++(<$>) :: Monad m => (a -> b) -> m a -> m b+(<$>) = liftM+{-# INLINE (<$>) #-}++(<*>) :: Monad m => m (a -> b) -> m a -> m b+(<*>) = ap+{-# INLINE (<*>) #-}
+ src/Database/DSH/CL/Lang.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE PatternSynonyms       #-}++module Database.DSH.CL.Lang+    ( module Database.DSH.Common.Type+    , Expr(..)+    , NL(..), reverseNL, toList, fromList, fromListSafe, appendNL, toNonEmpty+    , Qual(..), isGuard, isBind+    , Typed(..)+    , Prim1(..)+    , Prim2(..)+    ) where++import           Control.Applicative          hiding (empty)++import qualified Data.Foldable                as F+import qualified Data.Traversable             as T+import           Data.List.NonEmpty           (NonEmpty((:|)))++import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>))+import qualified Text.PrettyPrint.ANSI.Leijen as PP+import           Text.Printf++import           Database.DSH.Common.Nat+import qualified Database.DSH.Common.Lang     as L+import           Database.DSH.Common.Type+import           Database.DSH.Common.Pretty+import           Database.DSH.Impossible++--------------------------------------------------------------------------------+-- A simple type of nonempty lists, used for comprehension+-- qualifiers. This type is used instead of Data.List.NonEmpty to have+-- a proper list spine for which Kure traversals can be defined+-- easily.++data NL a = a :* (NL a)+          | S a+          deriving (Eq, Ord)++infixr :*++instance Show a => Show (NL a) where+    show = show . toList++instance Pretty a => Pretty (NL a) where+    pretty = pretty . toList++instance Functor NL where+    fmap f (a :* as) = (f a) :* (fmap f as)+    fmap f (S a)     = S (f a)++instance F.Foldable NL where+    foldr f z (a :* as) = f a (F.foldr f z as)+    foldr f z (S a)     = f a z++instance T.Traversable NL where+    traverse f (a :* as) = (:*) <$> (f a) <*> (T.traverse f as)+    traverse f (S a)     = S <$> (f a)++toList :: NL a -> [a]+toList (a :* as) = a : toList as+toList (S a)     = [a]++fromList :: [a] -> Maybe (NL a)+fromList [] = Nothing+fromList as = Just $ aux as+  where+    aux :: [a] -> NL a+    aux (x : []) = S x+    aux (x : xs) = x :* aux xs+    aux []       = $impossible++fromListSafe :: a -> [a] -> NL a+fromListSafe a [a1]      = a :* S a1+fromListSafe a []        = S a+fromListSafe a (a1 : as) = a :* fromListSafe a1 as++toNonEmpty :: NL a -> NonEmpty a+toNonEmpty (a :* as) = a :| toList as+toNonEmpty (S a)     = a :| []++reverseNL :: NL a -> NL a+reverseNL (a :* as) = F.foldl (flip (:*)) (S a) as+reverseNL (S a)     = S a++appendNL :: NL a -> NL a -> NL a+appendNL (a :* as) bs = a :* appendNL as bs+appendNL (S a)     bs = a :* bs++--------------------------------------------------------------------------------+-- CL primitives++data Prim1 = Singleton+           | Length +           | Concat+           | Null+           | Sum +           | Avg +           | The +           | Head +           | Tail+           | Minimum +           | Maximum+           | Reverse +           | And +           | Or+           | Init +           | Last +           | Nub+           | Number +           | Guard+           | Reshape Integer+           | Transpose+           | TupElem TupleIndex+           deriving (Eq)++instance Show Prim1 where+  show Singleton       = "sng"+  show Length          = "length"+  show Concat          = "concat"+  show Null            = "null"+  show Sum             = "sum"+  show Avg             = "avg"+  show The             = "the"+  show Head            = "head"+  show Minimum         = "minimum"+  show Maximum         = "maximum"+  show Tail            = "tail"+  show Reverse         = "reverse"+  show And             = "and"+  show Or              = "or"+  show Init            = "init"+  show Last            = "last"+  show Nub             = "nub"+  show Number          = "number"+  show Guard           = "guard"+  show Transpose       = "transpose"+  show (Reshape n)     = printf "reshape(%d)" n+  -- tuple access is pretty-printed in a special way+  show TupElem{}       = $impossible++data Prim2 = Sort+           | Group+           | Append+           | Index+           | Zip +           | CartProduct+           | NestProduct+           | ThetaJoin (L.JoinPredicate L.JoinExpr)+           | NestJoin (L.JoinPredicate L.JoinExpr)+           | SemiJoin (L.JoinPredicate L.JoinExpr)+           | AntiJoin (L.JoinPredicate L.JoinExpr)+           deriving (Eq)++instance Show Prim2 where+  show Group        = "group"+  show Sort         = "sort"+  show Append       = "append"+  show Index        = "index"+  show Zip          = "zip"+  show CartProduct  = "⨯"+  show NestProduct  = "▽"+  show (ThetaJoin p) = printf "⨝_%s" (pp p)+  show (NestJoin p)  = printf "△_%s" (pp p)+  show (SemiJoin p)  = printf "⋉_%s" (pp p)+  show (AntiJoin p)  = printf "▷_%s" (pp p)++--------------------------------------------------------------------------------+-- CL expressions++data Qual = BindQ L.Ident Expr+          | GuardQ Expr+          deriving (Eq, Show)++isGuard :: Qual -> Bool+isGuard (GuardQ _)   = True+isGuard (BindQ _ _)  = False++isBind :: Qual -> Bool+isBind (GuardQ _)   = False+isBind (BindQ _ _)  = True++data Expr  = Table Type String [L.Column] L.TableHints+           | AppE1 Type Prim1 Expr+           | AppE2 Type Prim2 Expr Expr+           | BinOp Type L.ScalarBinOp Expr Expr+           | UnOp Type L.ScalarUnOp Expr+           | If Type Expr Expr Expr+           | Lit Type L.Val+           | Var Type L.Ident+           | Comp Type Expr (NL Qual)+           | MkTuple Type [Expr]+           | Let Type L.Ident Expr Expr+           deriving (Show)++instance Pretty Expr where+    pretty (AppE1 _ (TupElem n) e1) = +        parenthize e1 <> dot <> int (tupleIndex n)+    pretty (MkTuple _ es)     = tupled $ map pretty es+    pretty (Table _ n _ _)    = text "table" <> parens (text n)+    pretty (AppE1 _ p1 e)     = (text $ show p1) <+> (parenthize e)+    pretty (AppE2 _ p1 e1@(Comp _ _ _) e2) = (text $ show p1) <+> (align $ (parenthize e1) PP.<$> (parenthize e2))+    pretty (AppE2 _ p1 e1 e2@(Comp _ _ _)) = (text $ show p1) <+> (align $ (parenthize e1) PP.<$> (parenthize e2))+    pretty (AppE2 _ p1 e1 e2) = (text $ show p1) <+> (align $ (parenthize e1) </> (parenthize e2))+    pretty (BinOp _ o e1 e2)  = (parenthize e1) <+> (pretty o) <+> (parenthize e2)+    pretty (UnOp _ o e)       = pretty o <> parens (pretty e)+    pretty (If _ c t e)       = text "if"+                             <+> pretty c+                             <+> text "then"+                             <+> (parenthize t)+                             <+> text "else"+                             <+> (parenthize e)+    pretty (Lit _ v)          = pretty v+    pretty (Var _ s)          = text s++    pretty (Comp _ e qs) = encloseSep lbracket rbracket empty docs+                         where docs = (char ' ' <> pretty e <> char ' ') : qsDocs+                               qsDocs =+                                 case qs of+                                   q :* qs' -> (char '|' <+> pretty q)+                                               : [ char ',' <+> pretty q' | q' <- toList qs' ]++                                   S q      -> [char '|' <+> pretty q]+    pretty (Let _ x e1 e)     = +        align $ text "let" <+> text x <+> char '=' <+> pretty e1+                </>+                text "in" <+> pretty e++parenthize :: Expr -> Doc+parenthize e =+    case e of+        Var _ _               -> pretty e+        Lit _ _               -> pretty e+        Table _ _ _ _         -> pretty e+        Comp _ _ _            -> pretty e+        AppE1 _ (TupElem _) _ -> pretty e+        _                     -> parens $ pretty e++instance Pretty Qual where+    pretty (BindQ i e) = text i <+> text "<-" <+> pretty e+    pretty (GuardQ e)  = pretty e++-- Binary relational operators are pretty-printed different from other+-- combinators+isRelOp :: Prim2 -> Bool+isRelOp o =+    case o of+        ThetaJoin _  -> True+        NestJoin _   -> True+        SemiJoin _   -> True+        AntiJoin _   -> True+        _            -> False++++deriving instance Eq Expr++instance Typed Expr where+    typeOf (Table t _ _ _) = t+    typeOf (AppE1 t _ _)   = t+    typeOf (AppE2 t _ _ _) = t+    typeOf (If t _ _ _)    = t+    typeOf (BinOp t _ _ _) = t+    typeOf (UnOp t _ _)    = t+    typeOf (Lit t _)       = t+    typeOf (Var t _)       = t+    typeOf (Comp t _ _)    = t+    typeOf (MkTuple t _)   = t+    typeOf (Let t _ _ _)   = t++
+ src/Database/DSH/CL/Opt.hs view
@@ -0,0 +1,114 @@+-- | This module performs optimizations on the Comprehension Language+-- (CL).+module Database.DSH.CL.Opt+  ( optimizeComprehensions+  ) where++import           Control.Arrow++import           Database.DSH.Common.Kure++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang++import           Database.DSH.CL.Opt.Auxiliary+import           Database.DSH.CL.Opt.CompNormalization+import           Database.DSH.CL.Opt.FlatJoin+import           Database.DSH.CL.Opt.LoopInvariant+import           Database.DSH.CL.Opt.NestJoin+import           Database.DSH.CL.Opt.Normalize+import           Database.DSH.CL.Opt.PartialEval+import           Database.DSH.CL.Opt.PostProcess+import           Database.DSH.CL.Opt.PredPushdown+import           Database.DSH.CL.Opt.Resugar++--------------------------------------------------------------------------------+-- Rewrite Strategy: Rule Groups++-- | Comprehension normalization rules 1 to 3.+compNormEarlyR :: RewriteC CL+compNormEarlyR = m_norm_1R +                 <+ m_norm_2R+                 <+ m_norm_3R+                 -- Does not lead to good code. See lablog entry (24.11.2014)+                 -- <+ invariantguardR+                 <+ ifgeneratorR+                 <+ identityCompR++-- | Comprehension normalization rules 4 and 5. Beware: these rewrites+-- should propably occur late in the chain, as they might prohibit+-- semijoin/antijoin introduction+compNormLateR :: RewriteC CL+compNormLateR = m_norm_4R <+ m_norm_5R++-- | Nestjoin/Nestproduct rewrites are applied bottom-up. Innermost+-- nesting opportunities must be dealt with first in order to produce+-- trees of nesting operators.+buUnnestR :: RewriteC CL+buUnnestR =+    zipCorrelatedR+    <+ repeatR nestjoinR+    -- If the inverse M-Norm-3 succeeds, try to unnest the new+    -- generator+    <+ (nestingGenR >>> pathR [CompQuals, QualsSingleton, BindQualExpr] nestjoinR)++-- | Normalize unnested comprehensions. To avoid nested iterators+-- after desugaring whenever possible, consecutive generators that do+-- not depend on each other are mapped to cartesian products. After+-- that, we try to push guards down into product inputs.+postProcessCompR :: RewriteC CL+postProcessCompR = do+    ExprCL Comp{} <- idR+    (guardpushbackR+        >+> repeatR introduceCartProductsR+        >+> repeatR predpushdownR)++postProcessR :: RewriteC CL+postProcessR = repeatR $ anybuR postProcessCompR++--------------------------------------------------------------------------------+-- Rewrite Strategy++-- | Perform a top-down traversal of a query expression, looking for+-- rewrite opportunities on comprehensions and other expressions.+descendR :: RewriteC CL+descendR = readerT $ \cl -> case cl of++    ExprCL Comp{} -> optCompR++    -- On non-comprehensions, try to apply partial evaluation rules+    -- before descending+    ExprCL _      -> repeatR partialEvalR+                     >+> repeatR normalizeExprR+                     >+> anyR descendR++    -- We are looking only for expressions. On non-expressions, simply descend.+    _             -> anyR descendR+++-- | Optimize single comprehensions during a top-down traversal+optCompR :: RewriteC CL+optCompR = do+    Comp{} <- promoteT idR+    -- debugPretty "optCompR at" c++    repeatR (compNormEarlyR+             <+ predpushdownR+             <+ flatjoinsR+             <+ anyR descendR+             ) >>> debugShow "after comp"++applyOptimizationsR :: RewriteC CL+applyOptimizationsR = descendR >+> anytdR loopInvariantR >+> anybuR buUnnestR++optimizeR :: RewriteC CL+optimizeR = resugarR >+>+            normalizeOnceR >+>+            repeatR applyOptimizationsR >+>+            postProcessR++optimizeComprehensions :: Expr -> Expr+optimizeComprehensions expr = debugOpt "CL" expr optimizedExpr+  where+    optimizedExpr = applyExpr (optimizeR >>> projectT) expr+    -- optimizedExpr = applyExpr projectT expr
+ src/Database/DSH/CL/Opt/AntiJoin.hs view
@@ -0,0 +1,251 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE PatternSynonyms #-}++module Database.DSH.CL.Opt.AntiJoin+    ( antijoinR+    ) where++import           Control.Arrow+import           Data.List.NonEmpty         (NonEmpty ((:|)))+import qualified Data.List.NonEmpty as NL+import           Data.Semigroup+import qualified Data.Traversable as T+import           Data.List++import           Database.DSH.Common.Lang+import           Database.DSH.Common.Kure+import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary+import qualified Database.DSH.CL.Primitives as P++--------------------------------------------------------------------------------+-- Universal quantification with and without range predicates++-- | Turn universal quantification with range and quantifier predicates into an+-- antijoin. We use the classification of queries in Claussen et al.: Optimizing+-- Queries with Universal Quantification in Object-Oriented and+-- Object-Relational Databases (VLDB 1995).++pattern PAnd xs <- AppE1 _ And xs+pattern PNot e <- UnOp _ (SUBoolOp Not) e++negateRelOp :: BinRelOp -> BinRelOp+negateRelOp op = case op of+    Eq  -> NEq+    NEq -> Eq+    GtE -> Lt+    LtE -> Gt+    Lt  -> GtE+    Gt  -> LtE++-- | Quantifier predicates that reference inner and outer relation+-- appear negated on the antijoin. The transform results in a+-- non-empty list of join conjuncts extracted from the negated+-- quantifier predicate. In addition, it returns a (possibly empty)+-- list of conjuncts that only reference the inner variable and can be+-- evaluated on the inner source.+quantifierPredicateT :: Ident +                     -> Ident +                     -> TransformC CL (NonEmpty (JoinConjunct JoinExpr), [Expr])+quantifierPredicateT x y = readerT $ \q -> case q of+    -- If the quantifier predicate is already negated, take its+    -- non-negated form.+    ExprCL (PNot _) -> do+        conjs <- childT UnOpArg conjunctsT++        -- Separate predicate parts that only depend on the inner+        -- variable.+        let (nonCorrExprs, corrExprs) = partition (\e -> freeVars e == [y]) $ NL.toList conjs++        -- Note: We can't be sure that there actually is at least one+        -- predicate that is correlated. As the caller only checks+        -- that x and y occur in the combined predicate, we might run+        -- into the following freak case: p1 x && p2 y. In this case,+        -- fail the rewrite completely.+        corrExprs' <- case corrExprs of+                          c : cs -> return $ c :| cs+                          []     -> fail "no correlated predicates for the join"++        corrPreds <- constT (return corrExprs') >>> mapT (splitJoinPredT x y)+        return (corrPreds, nonCorrExprs)++    -- If the predicate is a simple relational operator, but+    -- non-negated, try to negate the operator itself.+    ExprCL (BinOp t (SBRelOp op) e1 e2) -> do+        let e' = BinOp t (SBRelOp $ negateRelOp op) e1 e2+        q' <- constT (return e') >>> splitJoinPredT x y+        return (q' :| [], [])+        +    _                          -> fail "can't handle predicate"++mkUniversalQuantOnlyAntiJoinT :: (Ident, Expr) +                              -> (Ident, Expr) +                              -> Expr +                              -> TransformC (NL Qual) Qual+mkUniversalQuantOnlyAntiJoinT (x, xs) (y, ys) q = do+    (qPred, nonCorrPreds) <- constT (return q) >>> injectT >>> quantifierPredicateT x y+    +    let yst = typeOf ys+        yt  = elemT yst++    let innerQuals = case nonCorrPreds of+                         p : ps -> BindQ y ys :* fmap GuardQ (fromListSafe p ps)+                         []     -> S $ BindQ y ys++    -- Filter the inner source with the range+    -- predicates. Additionally, filter it with the non-correlated+    -- predicates extracted from the quantifier predicate.+    -- [ y | y <- ys, ps ++ nonCorrPreds ]+    let ys' = Comp yst (Var yt y) innerQuals++    return $ BindQ x (P.antijoin xs ys' $ JoinPred $ qPred)++universalQualR :: RewriteC (NL Qual)+universalQualR = readerT $ \quals -> case quals of+    -- Special case: no range predicate+    -- [ ... | ..., x <- xs, and [ q | y <- ys ]]+    BindQ x xs :* (S (GuardQ (PAnd (Comp _ q (S (BindQ y ys)))))) -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        antijoinGen <- mkUniversalQuantOnlyAntiJoinT (x, xs) (y, ys) q+        return $ S antijoinGen++    -- Special case: no range predicate+    -- [ ... | ..., x <- xs, and [ q | y <- ys ], ... ]+    BindQ x xs :* (GuardQ (PAnd (Comp _ q (S (BindQ y ys))))) :* qs -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        antijoinGen <- mkUniversalQuantOnlyAntiJoinT (x, xs) (y, ys) q+        return $ antijoinGen :* qs++    -- [ ... | ..., x <- xs, and [ q | y <- ys, ps ], ... ]+    BindQ x xs :* GuardQ (PAnd (Comp _ q (BindQ y ys :* ps))) :* qs -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        antijoinGen <- mkUniversalRangeAntiJoinT (x, xs) (y, ys) ps q+        return $ antijoinGen :* qs++    -- [ ... | ..., x <- xs, and [ q | y <- ys, ps ]]+    BindQ x xs :* (S (GuardQ (PAnd (Comp _ q (BindQ y ys :* ps))))) -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        antijoinGen <- mkUniversalRangeAntiJoinT (x, xs) (y, ys) ps q+        return $ S $ antijoinGen+    _ -> fail "no and pattern"++mkUniversalRangeAntiJoinT :: (Ident, Expr) +                     -> (Ident, Expr)+                     -> NL Qual+                     -> Expr+                     -> TransformC (NL Qual) Qual+mkUniversalRangeAntiJoinT (x, xs) (y, ys) ps q = do+    psExprs <- constT $ T.mapM fromGuard ps+    let psFVs = sort $ nub $ concatMap freeVars $ toList psExprs+        qFVs  = sort $ nub $ freeVars q++    let xy = sort [x, y]++    debugMsg $ show psFVs+    debugMsg $ show qFVs+    debugMsg $ show xy++    case (psFVs, qFVs) of+        -- Class 12: p(y), q(x, y)+        ([y'], qsvs@[_, _]) | y == y' && qsvs == xy -> do+            (qPred, nonCorrPreds) <- constT (return q) >>> injectT >>> quantifierPredicateT x y+            mkClass12AntiJoinT (x, xs) (y, ys) psExprs (JoinPred qPred) nonCorrPreds++        -- Class 15: p(x, y), q(y)+        (psvs@[_, _], [y']) | psvs == xy && y == y' -> do+            psConjs <- constT (return psExprs) >>> mapT (splitJoinPredT x y)+            let psPred = JoinPred $ toNonEmpty psConjs+            mkClass15AntiJoinT (x, xs) (y, ys) psPred q++        -- Class 16: p(x, y), q(x, y)+        (psvs@[_, _], qsvs@[_, _]) | psvs == xy && qsvs == xy -> do+            psConjs <- constT (return psExprs) >>> mapT (splitJoinPredT x y)++            -- Even if q itself references x and y, there might be+            -- parts of the predicate (conjuncts) which only reference+            -- y. These parts can (and should) be evaluated on ys.+            (qPred, nonCorrPreds) <- constT (return q) >>> injectT >>> quantifierPredicateT x y++            mkClass16AntiJoinT (x, xs) (y, ys) (toNonEmpty psConjs) qPred nonCorrPreds++        _ -> fail "FIXME"+++mkClass12AntiJoinT :: (Ident, Expr)               -- ^ Generator variable and expression for the outer+                   -> (Ident, Expr)+                   -> NL Expr+                   -> JoinPredicate JoinExpr+                   -> [Expr]+                   -> TransformC (NL Qual) Qual+mkClass12AntiJoinT (x, xs) (y, ys) ps qs nonCorrPreds = do+    let yst = typeOf ys+        yt  = elemT yst++    -- Filter the inner source with the range+    -- predicates. Additionally, filter it with the non-correlated+    -- predicates extracted from the quantifier predicate.  +    -- [ y | y <- ys, ps ++ nonCorrPreds ]+    let innerPreds = case nonCorrPreds of+                         c : cs -> appendNL ps (fromListSafe c cs)+                         []     -> ps++    let ys' = Comp yst (Var yt y) (BindQ y ys :* fmap GuardQ innerPreds)++    -- xs ▷_ps [ y | y <- ys, not qs ]+    return $ BindQ x (P.antijoin xs ys' qs)++-- This rewrite implements plan 14 for Query Class 15 in Claussen et al.,+-- Optimizing Queries with Universal Quantification... (VLDB, 1995).  Class 15+-- contains queries in which the range predicate ranges over both relations,+-- i.e. x and y occur free. The quantifier predicate on the other hand ranges+-- only over the inner relation:+-- p(x, y), q(y)+mkClass15AntiJoinT :: (Ident, Expr)               -- ^ Generator variable and expression for the outer+                   -> (Ident, Expr)+                   -> JoinPredicate JoinExpr+                   -> Expr+                   -> TransformC (NL Qual) Qual+mkClass15AntiJoinT (x, xs) (y, ys) ps qs = do+    let yst = typeOf ys+        yt  = elemT yst++    -- [ y | y <- ys, not q ]+    let ys' = Comp yst (Var yt y) (BindQ y ys :* S (GuardQ $ P.not qs))++    -- xs ▷_not(qs) [ y | y <- ys, ps ]+    return $ BindQ x (P.antijoin xs ys' ps)++mkClass16AntiJoinT :: (Ident, Expr)+                   -> (Ident, Expr)+                   -> NonEmpty (JoinConjunct JoinExpr) +                   -> NonEmpty (JoinConjunct JoinExpr)+                   -> [Expr]+                   -> TransformC (NL Qual) (Qual)+mkClass16AntiJoinT (x, xs) (y, ys) ps qs nonCorrPreds = do+    -- Prepare a comprehension that filters the inner input by the+    -- non-correlated predicates extracted from the quantifier+    -- predicate.+    let yst = typeOf ys+        yt  = elemT yst++    let ys' = case nonCorrPreds of+                  c : cs -> let quals = BindQ y ys :* fmap GuardQ (fromListSafe c cs)+                            in Comp yst (Var yt y) quals+                  []     -> ys++    -- xs ▷_(p && not q) ys+    return $ BindQ x (P.antijoin xs ys' $ JoinPred $ ps <> qs)++antijoinR :: RewriteC CL+antijoinR = do+    Comp _ _ _ <- promoteT idR+    childR CompQuals (promoteR $ onetdR universalQualR)
+ src/Database/DSH/CL/Opt/Auxiliary.hs view
@@ -0,0 +1,406 @@+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE MultiWayIf            #-}+{-# LANGUAGE TemplateHaskell       #-}++-- | Common tools for rewrites+module Database.DSH.CL.Opt.Auxiliary+    ( applyExpr+    , applyInjectable+      -- * Monad rewrites with additional state+    , TuplifyM+      -- * Converting predicate expressions into join predicates+    , toJoinExpr+    , splitJoinPredT+    , joinConjunctsT+    , conjunctsT+    -- * Pushing guards towards the front of a qualifier list+    , isThetaJoinPred+    , isSemiJoinPred+    , isAntiJoinPred+      -- * Free and bound variables+    , freeVars+    , boundVars+    , compBoundVars+      -- * Substituion+    , substR+    , tuplifyR+      -- * Combining generators and guards+    , insertGuard+      -- * Generic iterator to merge guards into generators+    , Comp(..)+    , MergeGuard+    , mergeGuardsIterR+      -- * Classification of expressions+    , complexPrim1+    , complexPrim2+    , fromGuard+    , fromQual+    , fromGen+      -- * NL spine traversal+    , onetdSpineT+    ) where++import           Control.Arrow+import           Data.Either+import qualified Data.Foldable              as F+import           Data.List+import qualified Data.Set                   as S+import           Data.List.NonEmpty         (NonEmpty ((:|)))+import           Data.Semigroup             hiding (First)++import           Language.KURE++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.Common.Lang+import           Database.DSH.Common.Nat+import           Database.DSH.Common.RewriteM+import           Database.DSH.Impossible++-- | A version of the CompM monad in which the state contains an additional+-- rewrite. Use case: Returning a tuplify rewrite from a traversal over the+-- qualifier list so that it can be applied to the head expression.+type TuplifyM = RewriteStateM (RewriteC CL)++-- | Run a translate on an expression without context+applyExpr :: TransformC CL b -> Expr -> Either String b+applyExpr f e = runRewriteM $ applyT f initialCtx (inject e)++-- | Run a translate on any value which can be injected into CL+applyInjectable :: Injection a CL => TransformC CL b -> a -> Either String b+applyInjectable t e = runRewriteM $ applyT t initialCtx (inject e)+++--------------------------------------------------------------------------------+-- Rewrite general expressions into equi-join predicates++toJoinBinOp :: Monad m => ScalarBinOp -> m JoinBinOp+toJoinBinOp (SBNumOp o)     = return $ JBNumOp o+toJoinBinOp (SBStringOp o)  = return $ JBStringOp o+toJoinBinOp (SBRelOp _)     = fail "toJoinBinOp: join expressions can't contain relational ops"+toJoinBinOp (SBBoolOp _)    = fail "toJoinBinOp: join expressions can't contain boolean ops"++toJoinUnOp :: Monad m => ScalarUnOp -> m JoinUnOp+toJoinUnOp (SUNumOp o)  = return $ JUNumOp o+toJoinUnOp (SUCastOp o) = return $ JUCastOp o+toJoinUnOp (SUTextOp o) = return $ JUTextOp o+toJoinUnOp (SUBoolOp _) = fail "toJoinUnOp: join expressions can't contain boolean ops"+toJoinUnOp SUDateOp     = $unimplemented++toJoinExpr :: Ident -> TransformC Expr JoinExpr+toJoinExpr n = do+    e <- idR++    case e of+        AppE1 _ (TupElem i) _ -> do+            appe1T (toJoinExpr n) (\t _ e1 -> JTupElem t i e1)+        BinOp _ o _ _ -> do+            o' <- constT $ toJoinBinOp o+            binopT (toJoinExpr n) (toJoinExpr n) (\t _ e1 e2 -> JBinOp t o' e1 e2)+        UnOp _ o _ -> do+            o' <- constT $ toJoinUnOp o+            unopT (toJoinExpr n) (\t _ e1 -> JUnOp t o' e1)+        Lit t v       -> do+            return $ JLit t v+        Var t x       -> do+            guardMsg (n == x) "toJoinExpr: wrong name"+            return $ JInput t+        _             -> do+            fail "toJoinExpr: can't translate to join expression"++flipRelOp :: BinRelOp -> BinRelOp+flipRelOp Eq  = Eq+flipRelOp NEq = NEq+flipRelOp Gt  = Lt+flipRelOp Lt  = Gt+flipRelOp GtE = LtE+flipRelOp LtE = GtE++-- | Try to transform an expression into a thetajoin predicate. This+-- will fail if either the expression does not have the correct shape+-- (relational operator with simple projection expressions on both+-- sides) or if one side of the predicate has free variables which are+-- not the variables of the qualifiers given to the function.+splitJoinPredT :: Ident -> Ident -> TransformC Expr (JoinConjunct JoinExpr)+splitJoinPredT x y = do+    BinOp _ (SBRelOp op) e1 e2 <- idR++    [x'] <- return $ freeVars e1+    [y'] <- return $ freeVars e2++    if | x == x' && y == y' -> binopT (toJoinExpr x)+                                      (toJoinExpr y)+                                      (\_ _ e1' e2' -> JoinConjunct e1' op e2')+       | y == x' && x == y' -> binopT (toJoinExpr y)+                                      (toJoinExpr x)+                                      (\_ _ e1' e2' -> JoinConjunct e2' (flipRelOp op) e1')+       | otherwise          -> fail "splitJoinPredT: not a theta-join predicate"++-- | Split a conjunctive combination of join predicates.+joinConjunctsT :: Ident -> Ident -> TransformC CL (NonEmpty (JoinConjunct JoinExpr))+joinConjunctsT x y = conjunctsT >>> mapT (splitJoinPredT x y)++-- | Split a combination of logical conjunctions into its sub-terms.+conjunctsT :: TransformC CL (NonEmpty Expr)+conjunctsT = readerT $ \e -> case e of+    -- For a logical AND, turn the left and right arguments into lists+    -- of join predicates and combine them.+    ExprCL (BinOp _ (SBBoolOp Conj) _ _) -> do+        leftConjs  <- childT BinOpArg1 conjunctsT+        rightConjs <- childT BinOpArg2 conjunctsT+        return $ leftConjs <> rightConjs++    -- For a non-AND expression, try to transform it into a join+    -- predicate.+    ExprCL expr -> return $ expr :| []++    _ -> $impossible+++--------------------------------------------------------------------------------+-- Distinguish certain kinds of guards++-- | An expression qualifies for a thetajoin predicate if both sides+-- are scalar expressions on exactly one of the join candidate+-- variables.+isThetaJoinPred :: Ident -> Ident -> Expr -> Bool+isThetaJoinPred x y (BinOp _ (SBRelOp _) e1 e2) =+    isFlatExpr e1 && isFlatExpr e1+    && ([x] == freeVars e1 && [y] == freeVars e2+        || [x] == freeVars e2 && [y] == freeVars e1)+isThetaJoinPred _ _ _ = False++-- | Does the predicate look like an existential quantifier?+isSemiJoinPred :: Ident -> Expr -> Bool+isSemiJoinPred x (AppE1 _ Or (Comp _ p+                                     (S (BindQ y _)))) = isThetaJoinPred x y p+isSemiJoinPred _  _                                    = False++-- | Does the predicate look like an universal quantifier?+isAntiJoinPred :: Ident -> Expr -> Bool+isAntiJoinPred x (AppE1 _ And (Comp _ p+                                      (S (BindQ y _)))) = isThetaJoinPred x y p+isAntiJoinPred _  _                                     = False++isFlatExpr :: Expr -> Bool+isFlatExpr expr =+    case expr of+        AppE1 _ (TupElem _) e -> isFlatExpr e+        UnOp _ _ e            -> isFlatExpr e+        BinOp _ _ e1 e2       -> isFlatExpr e1 && isFlatExpr e2+        Var _ _               -> True+        Lit _ _               -> True+        _                     -> False++--------------------------------------------------------------------------------+-- Computation of free variables++freeVarsT :: TransformC CL [Ident]+freeVarsT = fmap nub $ crushbuT $ promoteT $ do (ctx, Var _ v) <- exposeT+                                                guardM (v `freeIn` ctx)+                                                return [v]++-- | Compute free variables of the given expression+freeVars :: Expr -> [Ident]+freeVars = either error id . applyExpr freeVarsT++-- | Compute all identifiers bound by a qualifier list+compBoundVars :: F.Foldable f => f Qual -> [Ident]+compBoundVars qs = F.foldr aux [] qs+  where+    aux :: Qual -> [Ident] -> [Ident]+    aux (BindQ n _) ns = n : ns+    aux (GuardQ _) ns  = ns++boundVarsT :: TransformC CL [Ident]+boundVarsT = fmap nub $ crushbuT $ promoteT $ readerT $ \expr -> case expr of+     Comp _ _ qs -> return $ compBoundVars qs+     Let _ v _ _ -> return [v]+     _           -> return []++-- | Compute all names that are bound in the given expression. Note+-- that the only binding forms in NKL are comprehensions or 'let'+-- bindings.+boundVars :: Expr -> [Ident]+boundVars = either error id . applyExpr boundVarsT++--------------------------------------------------------------------------------+-- Substitution++-- | /Exhaustively/ substitute term 's' for a variable 'v'.+substR :: Ident -> Expr -> RewriteC CL+substR v s = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    ExprCL (Var _ n) | n == v                          -> return $ inject s++    -- If a let-binding shadows the name we substitute, only descend+    -- into the bound expression.+    ExprCL (Let _ n _ _) | n == v    -> tryR $ childR LetBind (substR v s)+    ExprCL (Let _ n _ _) | otherwise ->+        if n `elem` freeVars s+        -- If the let-bound name occurs free in the substitute,+        -- alpha-convert the binding to avoid capturing the name.+        then $unimplemented >>> tryR (anyR (substR v s))+        else tryR $ anyR (substR v s)++    -- If some generator shadows v, we must not substitute in the comprehension+    -- head. However, substitute in the qualifier list. The traversal on+    -- qualifiers takes care of shadowing generators.+    -- FIXME in this case, rename the shadowing generator to avoid+    -- name-capturing (see lambda case)+    ExprCL (Comp _ _ qs) | v `elem` compBoundVars qs   -> tryR $ childR CompQuals (substR v s)+    ExprCL _                                           -> tryR $ anyR $ substR v s++    -- Don't substitute past shadowing generators+    QualsCL ((BindQ n _) :* _) | n == v                -> tryR $ childR QualsHead (substR v s)+    QualsCL _                                          -> tryR $ anyR $ substR v s+    QualCL _                                           -> tryR $ anyR $ substR v s+++--------------------------------------------------------------------------------+-- Tuplifying variables++-- | Turn all occurences of two identifiers into accesses to one tuple variable.+-- tuplifyR z c y e = e[fst z/x][snd z/y]+tuplifyR :: Ident -> (Ident, Type) -> (Ident, Type) -> RewriteC CL+tuplifyR v (v1, t1) (v2, t2) = substR v1 v1Rep >+> substR v2 v2Rep+  where+    (v1Rep, v2Rep) = tupleVars v t1 t2++tupleVars :: Ident -> Type -> Type -> (Expr, Expr)+tupleVars n t1 t2 = (v1Rep, v2Rep)+  where v     = Var pt n+        pt    = pairT t1 t2+        v1Rep = AppE1 t1 (TupElem First) v+        v2Rep = AppE1 t2 (TupElem (Next First)) v++--------------------------------------------------------------------------------+-- Helpers for combining generators with guards in a comprehensions'+-- qualifier list++-- | Insert a guard in a qualifier list at the first possible+-- position.+insertGuard :: Expr -> S.Set Ident -> NL Qual -> NL Qual+insertGuard guardExpr initialEnv quals = go initialEnv quals+  where+    go :: S.Set Ident -> NL Qual -> NL Qual+    go env (S q)                 =+        if all (\v -> S.member v env) fvs+        then GuardQ guardExpr :* S q+        else q :* (S $ GuardQ guardExpr)+    go env (q@(BindQ x _) :* qs) =+        if all (\v -> S.member v env) fvs+        then GuardQ guardExpr :* q :* qs+        else q :* go (S.insert x env) qs+    go env (GuardQ p :* qs)      = +        if all (\v -> S.member v env) fvs+        then GuardQ guardExpr :* GuardQ p :* qs+        else GuardQ p :* go env qs++    fvs = freeVars guardExpr++------------------------------------------------------------------------+-- Generic iterator that merges guards into generators one by one.++-- | A container for the components of a comprehension expression+data Comp = C Type Expr (NL Qual)++fromQual :: Qual -> Either (Ident, Expr) Expr+fromQual (BindQ x e) = Left (x, e)+fromQual (GuardQ p)  = Right p+++-- | Type of worker functions that merge guards into generators. It+-- receives the comprehension itself (with a qualifier list that+-- consists solely of generators), the current candidate guard+-- expression, guard expressions that have to be tried and guard+-- expressions that have been tried already. Last two are necessary if+-- the merging steps leads to tuplification.+type MergeGuard = Comp -> Expr -> [Expr] -> [Expr] -> TransformC () (Comp, [Expr], [Expr])++tryGuards :: MergeGuard  -- ^ The worker function+          -> Comp        -- ^ The current state of the comprehension+          -> [Expr]      -- ^ Guards to try+          -> [Expr]      -- ^ Guards that have been tried and failed+          -> TransformC () (Comp, [Expr])+-- Try the next guard+tryGuards mergeGuardR comp (p : ps) testedGuards = do+    let tryNextGuard :: TransformC () (Comp, [Expr])+        tryNextGuard = do+            -- Try to combine p with some generators+            (comp', ps', testedGuards') <- mergeGuardR comp p ps testedGuards++            -- On success, back out to give other rewrites+            -- (i.e. predicate pushdown) a chance.+            return (comp', ps' ++ testedGuards')++        -- If the current guard failed, try the next ones.+        tryOtherGuards :: TransformC () (Comp, [Expr])+        tryOtherGuards = tryGuards mergeGuardR comp ps (p : testedGuards)++    tryNextGuard <+ tryOtherGuards++-- No guards left to try and none succeeded+tryGuards _ _ [] _ = fail "no predicate could be merged"++-- | Try to build flat joins (equi-, semi- and antijoins) from a+-- comprehensions qualifier list.+-- FIXME only try on those predicates that look like equi-/anti-/semi-join predicates.+-- FIXME TransformC () ... is an ugly abuse of the rewrite system+mergeGuardsIterR :: MergeGuard -> RewriteC CL+mergeGuardsIterR mergeGuardR = do+    ExprCL (Comp ty e qs) <- idR++    -- Separate generators from guards+    ((g : gs), guards@(_:_)) <- return $ partitionEithers $ map fromQual $ toList qs++    let initialComp = C ty e (fmap (uncurry BindQ) $ fromListSafe g gs)++    -- Try to merge one guard with some generators+    (C _ e' qs', remGuards) <- constT (return ())+                               >>> tryGuards mergeGuardR initialComp guards []++    -- If there are any guards remaining which we could not turn into+    -- joins, append them at the end of the new qualifier list+    case remGuards of+        rg : rgs -> let rqs = fmap GuardQ $ fromListSafe rg rgs+                    in return $ ExprCL $ Comp ty e' (appendNL qs' rqs)+        []       -> return $ ExprCL $ Comp ty e' qs'++--------------------------------------------------------------------------------+-- Traversal functions++-- | Traverse the spine of a NL list top-down and apply the translation as soon+-- as possible.+onetdSpineT+  :: (ReadPath c Int, MonadCatch m, Walker c CL)+  => Transform c m CL b+  -> Transform c m CL b+onetdSpineT t = do+    n <- idR+    case n of+        QualsCL (_ :* _) -> childT 0 t <+ childT 1 (onetdSpineT t)+        QualsCL (S _)    -> childT 0 t+        _                -> $impossible++--------------------------------------------------------------------------------+-- Classification of expressions++complexPrim2 :: Prim2 -> Bool+complexPrim2 _ = True++complexPrim1 :: Prim1 -> Bool+complexPrim1 op =+    case op of+        Concat    -> False+        TupElem _ -> False+        _         -> True++fromGuard :: Monad m => Qual -> m Expr+fromGuard (GuardQ e)  = return e+fromGuard (BindQ _ _) = fail "not a guard"++fromGen :: Monad m => Qual -> m (Ident, Expr)+fromGen (BindQ x xs) = return (x, xs)+fromGen (GuardQ _)   = fail "not a generator"
+ src/Database/DSH/CL/Opt/CompNormalization.hs view
@@ -0,0 +1,232 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Monad comprehension normalization rules (adapted from T. Grust+-- "Comprehending Queries")+module Database.DSH.CL.Opt.CompNormalization+    ( m_norm_1R+    , m_norm_2R+    , m_norm_3R+    , m_norm_4R+    , m_norm_5R+    , invariantguardR+    , guardpushfrontR+    , guardpushbackR+    , ifgeneratorR+    , identityCompR+    ) where++import           Control.Applicative+import           Control.Arrow+import           Data.Either+import qualified Data.Map                   as M+import qualified Data.Set                   as S++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary+import qualified Database.DSH.CL.Primitives as P+import           Database.DSH.Common.Kure+import           Database.DSH.Common.Lang+import           Database.DSH.Impossible++------------------------------------------------------------------+-- Classical Monad Comprehension Normalization rules (Grust)++-- | M-Norm-1: Eliminate comprehensions with empty generators+m_norm_1R :: RewriteC CL+m_norm_1R = do+    Comp t _ _ <- promoteT idR+    matches <- childT CompQuals $ onetdT (promoteT $ patternT <+ patternEndT)+    guardM matches+    return $ inject $ P.nil t++  where+    patternT :: TransformC (NL Qual) Bool+    patternT = do+        BindQ _ (Lit _ (ListV [])) :* _ <- idR+        return True++    patternEndT :: TransformC (NL Qual) Bool+    patternEndT = do+        (S (BindQ _ (Lit _ (ListV [])))) <- idR+        return True++-- | M-Norm-2: eliminate singleton generators.+-- [ h | qs, x <- [v], qs' ]+-- => [ h[v/x] | qs, qs'[v/x] ]+m_norm_2R :: RewriteC CL+m_norm_2R = (normSingletonCompR <+ normCompR) >>> debugTrace "m_norm_2"++  where+    -- This rewrite is a bit annoying: If it triggers, we can remove a+    -- qualifier. However, the type NL forces us to take care that we do not+    -- produce a comprehension with an empty qualifier list.++    -- Due to non-empty NL lists, we have to consider the case of+    -- removing a (the!) qualifier from a singleton list.+    normSingletonCompR :: RewriteC CL+    normSingletonCompR = do+        Comp _ h (S q) <- promoteT idR+        (x, e) <- constT (return q) >>> qualT+        constT (return $ inject $ P.sng h) >>> substR x e++    -- The main rewrite+    normCompR :: RewriteC CL+    normCompR = do+        Comp t _ (_ :* _)   <- promoteT idR+        (tuplifyHeadR, qs') <- statefulT idR $ childT CompQuals (promoteR normQualifiersR) >>> projectT+        h'                  <- childT CompHead tuplifyHeadR >>> projectT+        return $ inject $ Comp t h' qs'++    normQualifiersR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualifiersR = anytdR (normQualsEndR <+ normQualsR)++    -- Match the pattern (singleton generator) on a qualifier+    qualT :: TransformC Qual (Ident, Expr)+    qualT = do+        q <- idR+        case q of+            -- x <- [v]+            BindQ x (Lit t (ListV [v]))   -> return (x, Lit (elemT t) v)+            -- x <- v : []+            BindQ x (AppE1 _ Singleton v) -> return (x, v)+            _                             -> fail "qualR: no match"++    -- Try to match the pattern at the end of the qualifier list+    normQualsEndR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualsEndR = do+        q1 :* (S q2) <- idR+        (x, e)       <- liftstateT $ constT (return q2) >>> qualT+        constT $ modify (>>> substR x e)+        return (S q1)++    -- Try to match the pattern in the middle of the qualifier list+    normQualsR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualsR = do+        q1 :* q2 :* qs <- idR+        (x, e)         <- liftstateT $ constT (return q2) >>> qualT+        qs' <- liftstateT $ constT (return $ inject qs) >>> substR x e >>> projectT+        constT $ modify (>>> substR x e)+        return $ q1 :* qs'++-- | M-Norm-3: unnest comprehensions from a generator+-- [ h | qs, x <- [ h' | qs'' ], qs' ]+-- => [ h[h'/x] | qs, qs'', qs'[h'/x] ]+m_norm_3R :: RewriteC CL+m_norm_3R = do+    Comp t _ _ <- promoteT idR+    (tuplifyHeadR, qs') <- statefulT idR $ childT CompQuals (promoteR normQualifiersR) >>> projectT+    h'                  <- childT CompHead (tryR tuplifyHeadR) >>> projectT+    return $ inject $ Comp t h' qs'++  where++    qualT :: TransformC Qual (Ident, Expr, NL Qual)+    qualT = do+        BindQ x (Comp _ h' qs'') <- idR+        return (x, h', qs'')++    normQualifiersR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualifiersR = anytdR (normQualsEndR <+ normQualsR)++    normQualsEndR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualsEndR = do+        (S q) <- idR+        (x, h', qs'') <- liftstateT $ (constT $ return q) >>> qualT+        constT $ modify (>>> substR x h')+        return qs''++    normQualsR :: Rewrite CompCtx TuplifyM (NL Qual)+    normQualsR = do+        q :* qs <- idR+        (x, h', qs'') <- liftstateT $ (constT $ return q) >>> qualT+        qs' <- liftstateT $ constT (return $ inject qs) >>> substR x h' >>> projectT+        constT $ modify (>>> substR x h')+        return $ appendNL qs'' qs'++-- | M-Norm-4: unnest existential quantifiers if the outer comprehension is over+-- an idempotent monad (i.e. duplicates are eliminated from the result).+m_norm_4R :: RewriteC CL+m_norm_4R = $unimplemented++-- | M-Norm-5: Unnest nested comprehensions over an idempotent monad.+m_norm_5R :: RewriteC CL+m_norm_5R = $unimplemented+++--------------------------------------------------------------------------------+-- Additional normalization rules for comprehensions++qualsguardpushfrontR :: RewriteC (NL Qual)+qualsguardpushfrontR = do+    qs     <- idR+    -- Separate generators from guards+    ((g : gs), guards@(_:_)) <- return $ partitionEithers $ map fromQual $ toList qs++    let gens = fmap (uncurry BindQ) $ fromListSafe g gs+    env <- S.fromList <$> M.keys <$> cl_bindings <$> contextT+    let qs' = foldl (\quals guard -> insertGuard guard env quals) gens guards+    guardM $ qs /= qs'+    return qs'++-- | Push all guards as far as possible to the front of the qualifier+-- list. Note that 'guardpushfrontR' loops with join introduction+-- rewrites and must not be isolated.+guardpushfrontR :: RewriteC CL+guardpushfrontR = do+    Comp t h _ <- promoteT idR+    qs' <- childT CompQuals (promoteR qualsguardpushfrontR) >>> projectT+    return $ inject $ Comp t h qs'++qualsguardpushbackR :: RewriteC (NL Qual)+qualsguardpushbackR = innermostR $ readerT $ \quals -> case quals of+    GuardQ p :* BindQ x xs :* qs -> return $ BindQ x xs :* GuardQ p :* qs+    GuardQ p :* (S (BindQ x xs)) -> return $ BindQ x xs :* (S (GuardQ p))+    _                            -> fail "no pushable guard"+                    ++-- | Push all guards to the end of the qualifier list to bring+-- generators closer together.+guardpushbackR :: RewriteC CL+guardpushbackR = do+    Comp t h _ <- promoteT idR+    qs' <- childT CompQuals (promoteR qualsguardpushbackR) >>> projectT+    return $ inject $ Comp t h qs'+++-- | If a guard does not depend on any generators of the current+-- comprehension, it can be evaluated outside of the comprehension. As+-- preparation, we push guards towards the front of the qualifier+-- list.+invariantguardR :: RewriteC CL+invariantguardR = +    tryR guardpushfrontR +    >>> +    (promoteR $ readerT $ \expr -> case expr of+        Comp t h (GuardQ g :* qs) -> return $ inject $ P.if_ g (Comp t h qs) (P.nil t)+        Comp t h (S (GuardQ p))   -> return $ inject $ P.if_ p (P.sng h) (P.nil t)+        _                         -> fail "no match")++ifgeneratorqualsR :: RewriteC (NL Qual)+ifgeneratorqualsR = anytdR $ readerT $ \quals -> case quals of+    BindQ x (If _ ce te (Lit _ (ListV []))) :* qs -> return $ BindQ x te :* GuardQ ce :* qs+    S (BindQ x (If _ ce te (Lit _ (ListV []))))   -> return $ BindQ x te :* S (GuardQ ce)+    _                                         -> fail "no match"+++-- | Transform an 'if' conditional in a generator into a guard.+ifgeneratorR :: RewriteC CL+ifgeneratorR = do+    Comp t h _ <- promoteT idR+    qs' <- childT CompQuals (promoteR ifgeneratorqualsR) >>> projectT+    return $ inject $ Comp t h qs'++-- | Eliminate comprehensions that do not perform work.+identityCompR :: RewriteC CL+identityCompR = do+    Comp _ (Var _ x) (S (BindQ x' xs)) <- promoteT idR+    guardM $ x == x'+    return $ inject xs
+ src/Database/DSH/CL/Opt/FlatJoin.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Introduce simple theta joins+module Database.DSH.CL.Opt.FlatJoin+    ( flatjoinsR+    ) where++import           Control.Applicative+import           Control.Arrow+import qualified Data.Map                      as M+import qualified Data.Set                      as S++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary++import           Database.DSH.CL.Opt.AntiJoin+import           Database.DSH.CL.Opt.SemiJoin+import           Database.DSH.CL.Opt.ThetaJoin++------------------------------------------------------------------------+-- Flat join detection++-- | Try to build a join from a list of generators and a single+-- guard. If we can build a theta join, the remaining predicates must+-- be tuplified. For this reason, we pass them in here.+mkFlatJoin :: MergeGuard+mkFlatJoin comp guard guardsToTry leftOverGuards = do+    let C ty h qs = comp+    env <- S.fromList <$> M.keys <$> cl_bindings <$> contextT+    let comp' = ExprCL $ Comp ty h (insertGuard guard env qs)+    tryAntijoinR comp' <+ trySemijoinR comp' <+ tryThetajoinR comp'++  where+    tryAntijoinR :: CL -> TransformC () (Comp, [Expr], [Expr])+    tryAntijoinR comp' = do+        ExprCL (Comp ty h qs') <- constT (return comp') >>> antijoinR+        return (C ty h qs', guardsToTry, leftOverGuards)++    trySemijoinR :: CL -> TransformC () (Comp, [Expr], [Expr])+    trySemijoinR comp' = do+        ExprCL (Comp ty h qs') <- constT (return comp') >>> semijoinR+        return (C ty h qs', guardsToTry, leftOverGuards)++    tryThetajoinR :: CL -> TransformC () (Comp, [Expr], [Expr])+    tryThetajoinR comp' = do+        res <- constT (return comp') >>> thetajoinR guardsToTry leftOverGuards+        (ExprCL (Comp ty h qs), guardsToTry', leftOverGuards') <- return res+        return (C ty h qs, guardsToTry', leftOverGuards')++flatjoinsR :: RewriteC CL+flatjoinsR = mergeGuardsIterR mkFlatJoin+
+ src/Database/DSH/CL/Opt/LoopInvariant.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE TemplateHaskell     #-}+    +-- | Extract loop-invariant "complex" expressions from comprehensions+module Database.DSH.CL.Opt.LoopInvariant+  ( loopInvariantR+  ) where++import           Control.Applicative+import           Data.Maybe+import           Data.List++import           Database.DSH.Impossible+import           Database.DSH.Common.Lang+import           Database.DSH.Common.Kure+import           Database.DSH.Common.Pretty+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Kure+import qualified Database.DSH.CL.Primitives as P+import           Database.DSH.CL.Opt.Auxiliary++-- | Extract complex loop-invariant expressions from comprehension+-- heads and guards.+loopInvariantR :: RewriteC CL+loopInvariantR = loopInvariantGuardR <+ loopInvariantHeadR++--------------------------------------------------------------------------------+-- Common code for searching loop-invariant expressions++traverseT :: [Ident] -> TransformC CL (Expr, PathC)+traverseT localVars = readerT $ \expr -> case expr of+    -- We do not traverse into lambdas and comprehensions which are+    -- nested in our current comprehension.  +    -- +    -- FIXME technically, we could consider the generators of the+    -- nested comprehension.+    ExprCL (Comp _ _ _) -> fail "we don't traverse into comprehensions"++    ExprCL _                          -> oneT $ searchInvariantExprT localVars+    _                                 -> fail "we only consider expressions"++-- | Collect a path to a complex expression+complexPathT :: [Ident] -> TransformC CL (Expr, PathC)+complexPathT localVars = do+    ExprCL e <- idR+    -- debugPretty "complexPathT" e+    path <- snocPathToPath <$> absPathT+    +    -- We are only interested in constant expressions that do not+    -- depend on variables bound by generators in the enclosing+    -- comprehension.+    -- debugMsg $ "free: " ++ pp (freeVars e)+    guardM $ null $ freeVars e `intersect` localVars++    -- FIXME more precise heuristics could be employed: A+    -- comprehension is only "complex" if it has more than one+    -- generator OR a filter OR something complex in the head.+    case e of+        Comp _ _ _                          -> return (e, path)+        If _ _ _ _                          -> return (e, path)+        AppE2 _ op _ _ | complexPrim2 op    -> return (e, path)+        AppE1 _ op _   | complexPrim1 op    -> return (e, path)+        _ -> fail "not a complex expression"++-- | Traverse expressions top-down, searching for loop-invariant+-- complex expressions.+searchInvariantExprT :: [Ident] -> TransformC CL (Expr, PathC)+searchInvariantExprT localVars = complexPathT localVars <+ (promoteT $ traverseT localVars)++invariantQualR :: [Ident] -> TransformC CL (Expr, PathC)+invariantQualR localVars = readerT $ \expr -> case expr of+    QualsCL (BindQ{} :* _)  -> childT QualsTail (invariantQualR localVars)+    QualsCL (GuardQ _ :* _) -> (childT QualsHead (searchInvariantExprT localVars)+                                <++                               childT QualsTail (invariantQualR localVars))+    QualsCL (S (GuardQ _))  -> pathT [QualsSingleton, GuardQualExpr] (searchInvariantExprT localVars)+    QualsCL (S BindQ{})     -> fail "no match"+    _                       -> $impossible++--------------------------------------------------------------------------------+-- Search and replace loop-invariant expressions++loopInvariantGuardR :: RewriteC CL+loopInvariantGuardR = do+    c@(Comp _ _ qs) <- promoteT idR+    -- FIXME passing *all* generator variables in the current+    -- comprehension is too conservative. It would be sufficient to+    -- consider those preceding the guard that is under investigation.+    let genVars = fmap fst $ catMaybes $ fmap fromGen $ toList qs+    (invExpr, invPath) <- childT CompQuals (invariantQualR genVars)+    letName            <- freshNameT (genVars ++ boundVars c)++    pathLen <- length <$> snocPathToPath <$> absPathT+    let localPath = drop pathLen invPath+        invVar    = Var (typeOf invExpr) letName++    ExprCL comp' <- pathR localPath (constT $ return $ inject invVar)+    return $ inject $ P.let_ letName invExpr comp'++loopInvariantHeadR :: RewriteC CL+loopInvariantHeadR = do+    Comp _ h qs <- promoteT idR+    let genVars = fmap fst $ catMaybes $ fmap fromGen $ toList qs+    (invExpr, invPath) <- childT CompHead (searchInvariantExprT genVars)+    letName            <- freshNameT (genVars ++ boundVars h)++    pathLen <- length <$> snocPathToPath <$> absPathT+    let localPath = drop pathLen invPath+        invVar    = Var (typeOf invExpr) letName++    ExprCL comp' <- pathR localPath (constT $ return $ inject invVar)+    debugMsg $ "loopInvariantHeadR " ++ pp (P.let_ letName invExpr comp')+    return $ inject $ P.let_ letName invExpr comp'
+ src/Database/DSH/CL/Opt/NestJoin.hs view
@@ -0,0 +1,495 @@+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE QuasiQuotes           #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE MultiParamTypeClasses #-}+    +-- | Deal with nested comprehensions by introducing explicit nesting+-- operators (NestJoin, NestProduct).+module Database.DSH.CL.Opt.NestJoin+  ( nestjoinR+  , zipCorrelatedR+  , nestingGenR+  ) where++import           Control.Applicative((<$>))+import           Control.Arrow+import           Control.Monad++import           Data.List+import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.List.NonEmpty as N++import           Database.DSH.Common.Lang+import           Database.DSH.Common.Kure++import           Database.DSH.CL.Lang+import           Database.DSH.CL.Kure+                 +import qualified Database.DSH.CL.Primitives as P++import           Database.DSH.CL.Opt.Auxiliary+import           Database.DSH.CL.Opt.CompNormalization++nestjoinR :: RewriteC CL+nestjoinR = unnestFromGuardR <+ unnestFromHeadR++--------------------------------------------------------------------------------+-- Common code for unnesting from a comprehension head and from+-- comprehension guards++-- A representation of a nested comprehension which is eligible for+-- unnesting+data NestedComp = NestedComp+    { hType   :: Type+    , hHead   :: Expr+    , hGen    :: (Ident, Expr)+    , hGuards :: [Expr]+    } deriving (Show)++-- | Check if a comprehension is eligible for unnesting. This is the+-- case if the outer generator variable 'x' does not occur in the+-- inner generator and if there is only one inner generator.+nestedCompT :: Ident -> TransformC CL (PathC, NestedComp)+nestedCompT x = do+    Comp t h qs <- promoteT idR+    (y, ys, qsr) <- case qs of+        S (BindQ y ys)    -> return (y, ys, [])+        BindQ y ys :* qsr -> return (y, ys, toList qsr)+        _                 -> fail "no match"++    guardM $ not $ x `elem` freeVars ys+    guards <- constT $ mapM fromGuard qsr++    p <- snocPathToPath <$> absPathT+    return (p, NestedComp t h (y, ys) guards)++-- | Traverse though an expression and search for a comprehension that+-- is eligible for unnesting.+searchNestedCompT :: Ident -> TransformC CL (PathC, NestedComp)+searchNestedCompT x =+    readerT $ \e -> case e of+        -- We expect the single generator at the front of the+        -- qualifiers. This might not be the case if a loop-invariant+        -- guard is present and preceeds the generator. Therefore, we+        -- pre-process by pushing all guards to the back.+        ExprCL Comp{} -> tryR guardpushbackR >>> nestedCompT x+        ExprCL _      -> oneT $ searchNestedCompT x+        _             -> fail "only traverse through expressions"++-- | Take an absolute path and drop the prefix of the path to a direct child of+-- the current node. This makes it a relative path starting from **some** direct+-- child of the current node.+relativePathT :: Path a -> TransformC b (Path a)+relativePathT p = do+    curPath <- snocPathToPath <$> absPathT+    return $ drop (1 + length curPath) p++constNodeT :: (Injection a CL, Monad m) => a -> Transform c m b CL+constNodeT expr = constT $ return $ inject expr++-- | Transform a suitable comprehension that was either nested in a+-- comprehension head or in a guard expression and the corresponding+-- outer generator. Returns a replacement for the inner comprehension,+-- outer generator with nesting op and the tuplify rewrite for the+-- outer generator variable.+unnestWorkerT+  :: NestedComp                   -- ^ The nested comprehension+  -> (Ident, Expr)                -- ^ The outer generator+  -> TransformC CL (Expr, Expr, RewriteC CL)+unnestWorkerT headComp (x, xs) = do+    let (y, ys) = hGen headComp++    -- Generators have to be indepedent+    guardM $ x `notElem` freeVars ys++    let (joinPredCandidates, nonJoinPreds) = partition (isThetaJoinPred x y) +                                                       (hGuards headComp)++    -- Determine which operator to use to implement the nesting. If+    -- there is a join predicate, we use a nestjoin. Only if there is+    -- no matching join predicate, we use a nested cartesian product+    -- (nestproduct).+    -- FIXME include all join predicates on the join operator+    nestOp <- case joinPredCandidates of+        [] -> return NestProduct+        p : ps -> do+            -- Split the join predicate+            p'  <- constT (return p) >>> splitJoinPredT x y+            ps' <- constT (return ps) >>> mapT (splitJoinPredT x y)+           +            return $ NestJoin $ JoinPred $ p' N.:| ps'++    -- Identify predicates which only refer to y and can be evaluated+    -- on the right nestjoin input.+    let (yPreds, leftOverPreds) = partition ((== [y]) . freeVars) nonJoinPreds++    -- Left over we have predicates which (propably) refer to both+    -- x and y and are not/can not be used as the join predicate.+    --    [ [ e x y | y <- ys, p x y, p' x y ] | x <- xs ]+    -- => [ [ e [fst y/x][snd y/y] | y <- snd x, p'[fst y/x][snd y/y] ] | x <- xs nj(p) ys ]+  +    let xt       = elemT $ typeOf xs+        yt       = elemT $ typeOf ys+        tupType  = pairT xt (listT (pairT xt yt))+        joinVar  = Var tupType x+        +    -- If there are inner predicates which only refer to y,+    -- evaluate them on the right (ys) nestjoin input.+    let ys' = case fromList yPreds of+                  Just ps -> Comp (listT yt) (Var yt y) (BindQ y ys :* fmap GuardQ ps)+                  Nothing -> ys++    -- the nesting operator combining xs and ys: +    -- xs nj(p) ys+    let xs'        = AppE2 (listT tupType) nestOp xs ys'++    innerVar <- freshNameT []++    let tuplifyInnerVarR :: Expr -> TransformC CL Expr+        tuplifyInnerVarR e =  constNodeT e+                              >>> tuplifyR innerVar (x, xt) (y, yt)+                              >>> projectT++    -- In the head of the inner comprehension, replace x and y+    -- with the corresponding pair components of the inner lists+    -- in the join result.+    h' <- tuplifyInnerVarR (hHead headComp)++    -- Do the same on left over predicates, which will be+    -- evaluated on the nestjoin result.+    remPreds <- sequence $ map tuplifyInnerVarR leftOverPreds+    let remGuards = map GuardQ remPreds++    -- Construct the inner comprehension with the tuplified head+    -- and apply left-over predicates to the inner comprehension.+    let ti = hType headComp+    let headComp' = case remGuards of+            g : gs -> Comp ti h' (BindQ innerVar (P.snd joinVar) :* fromListSafe g gs)+            []     -> Comp ti h' (S $ BindQ innerVar (P.snd joinVar))++    let tuplifyOuterR :: RewriteC CL+        tuplifyOuterR = substR x $ P.fst joinVar++    return (headComp', xs', tuplifyOuterR)+++--------------------------------------------------------------------------------+-- Unnesting from a comprehension head++-- In constrast to the previous strategy, we unnest only one+-- comprehension at a time. We unnest from the original comprehension+-- head, without normalizing it first. This saves quite a lot of+-- rather complex rewrites for normalizing the head and combining+-- multiple nesting operators. The resulting plans look the same.++-- General rule:+-- [ e x [ f x y | y <- ys, jp x y, p1 x, p2 x y, p3 y ] | x <- xs, p4 x ]+-- =>+-- [ e (fst x) [ f (fst y) (snd y) +--             | y <- snd x+--             , p1 (fst y)+--             , p2 (fst y) (snd y)+--             ]+-- | x <- xs △_jp [ y | y <- ys, p3 y ]+-- ]+-- +-- In the absence of a proper join predicate, we use the Nestproduct +-- operator ▽ instead of NestJoin.+--+-- Predicates on the inner comprehension that only refer to y can be+-- safely evaluated before joining. Note that predicates on the inner+-- comprehension that only refer to x can **not** be evaluated on xs+-- alone!++-- | Search for one comprehension nested in a comprehension head,+-- extract it and transform it into a nesting operator.+unnestFromHeadR :: RewriteC CL+unnestFromHeadR = do+    Comp to ho qso <- promoteT idR++    -- We need one generator on a comprehension+    (x, xs, qsr) <- case qso of+                        S (BindQ x xs)    -> return (x, xs, [])+                        BindQ x xs :* qsr -> return (x, xs, toList qsr)+                        _                 -> fail "no match"++    -- More precisely, we need *exactly one* generator on the+    -- comprehension+    guardM $ all isGuard qsr+    +    (headCompPath, headComp) <- childT CompHead (searchNestedCompT x)++    (headComp', nestOp, tuplifyOuterR) <- unnestWorkerT headComp (x, xs)++    -- Insert the replacement for the nested comprehension.+    +    -- The relative path to the comprehension to be replaced, starting+    -- from the head expression+    -- FIXME use withLocalPathT+    relCompPath <- relativePathT headCompPath++    ExprCL tuplifiedHo <- constNodeT ho >>> tryR tuplifyOuterR+    ExprCL unnestedHo  <- constNodeT tuplifiedHo >>> pathR relCompPath (constNodeT headComp')++    -- In the outer comprehension's qualifier list, x is replaced by+    -- the first pair component of the join result.+    qsr' <- constT (return $ map inject qsr)+            >>> mapT (tryR tuplifyOuterR) +            >>> mapT projectT++    -- ExprCL tuplifiedHead <- constNodeT ho' >>> tryR tuplifyOuterR++    return $ inject $ Comp to unnestedHo (fromListSafe (BindQ x nestOp) qsr')++    +--------------------------------------------------------------------------------+-- Nestjoin introduction: unnesting comprehensions from complex predicates++-- | Try to unnest comprehensions from guards, which we can not unnest otherwise+-- (e.g. by introduing semi- or antijoins).+-- +--   [ e | qs, x <- xs, p x [ f x y | y < ys jp x y ], qs' ]+-- +-- rewrites into+--+--   [ e[fst x/x] | +--   | qs+--   , x <- xs nestjoin(jp) ys+--   , p (fst x) [ f (fst y) (snd y) | y <- snd x ]+--   , qs'[fst x/x]+--   ]+--+-- Additional predicates on the inner comprehension are handled in the+-- same way as in unnesting from a comprehension head.++-- | Store not only the tuplifying rewrite in the state, but also the+-- rewritten guard expression.+-- FIXME this is a rather ugly hack+type GuardM = RewriteStateM (RewriteC CL, Maybe Expr)++-- | Search for an eligible nested comprehension in the current guard+-- and unnest it. Returns the tuplifying rewrite for the outer+-- generator variable 'x', the new generator with the nesting+-- operator, and the modified predicate.+unnestGuardT :: [Ident] -> (Ident, Expr) -> Expr -> TransformC CL (RewriteC CL, Expr, Expr)+unnestGuardT localGenVars (x, xs) guardExpr = do+    -- search for an unnestable comrehension+    (headCompPath, headComp) <- withLocalPathT +                                $ constNodeT guardExpr >>> searchNestedCompT x++    -- Forbid the generator of a comprehension we want to unnest to+    -- depend on *any* generator in the current outer+    -- comprehension. This is to prevent that the right input of a+    -- NestProduct that could be constructed depends on *any*+    -- preceding generator. See lablog (31.07.14) for a more elaborate+    -- explanation.+    guardM $ null $ localGenVars `intersect` freeVars (snd $ hGen headComp)++    -- combine inner and outer comprehension+    (headComp', nestOp, tuplifyOuterR) <- unnestWorkerT headComp (x, xs)++    -- Tuplify occurences of 'x' in the guard.+    ExprCL tuplifiedGuardExpr <- constNodeT guardExpr +                                 >>> tryR tuplifyOuterR++    -- Insert the new inner comprehension into the original guard+    -- expression+    ExprCL simplifiedGuardExpr <- constNodeT tuplifiedGuardExpr +                                  >>> pathR headCompPath (constNodeT headComp')+++    return (tuplifyOuterR, nestOp, simplifiedGuardExpr)+    +-- | Search for unnestable combinations of a generator and a nested+-- guard in a qualifier list.+unnestQualsR :: [Ident] -> Rewrite CompCtx GuardM (NL Qual)+unnestQualsR localGenVars = do+    readerT $ \quals -> case quals of+        -- In the middle of a qualifier list+        BindQ x xs :* GuardQ p :* qs -> do+            (tuplifyHeadR, xs', p') <- liftstateT $ constNodeT p +                                                    >>> +                                                    unnestGuardT localGenVars (x, xs) p+            constT $ modify (\(r, _) -> (r >>> tuplifyHeadR, Just p'))+            qs' <- liftstateT $ constNodeT qs >>> tuplifyHeadR >>> projectT+            return $ BindQ x xs' :* qs'++        -- At the end of a qualifier list+        BindQ x xs :* (S (GuardQ p)) -> do+            (tuplifyHeadR, xs', p') <- liftstateT $ constNodeT p +                                                    >>> +                                                    unnestGuardT localGenVars (x, xs) p+            constT $ modify (\(r, _) -> (r >>> tuplifyHeadR, Just p'))+            return $ S $ BindQ x xs'+        _ -> fail "no match"++-- | Trigger the search for unnesting opportunities in the qualifier+-- list and tuplify comprehension head and remaining qualifiers on+-- success.+-- +-- Note: In contrast to e.g. flat join introduction, we can't merge+-- the complete guard into the operator. The non-comprehension part+-- remains. We handle this by including the succesfully unnested and+-- modified guard in the list of failed guard expressions, even on+-- success.+unnestGuardR :: [Expr] -> [Expr] -> TransformC CL (CL, [Expr], [Expr])+unnestGuardR candGuards failedGuards = do+    Comp t _ qs      <- promoteT idR +    let localGenVars = concatMap (either ((: []) . fst) (const [])) $ map fromQual $ toList qs+    let unnestR = anytdR (promoteR $ unnestQualsR localGenVars) >>> projectT+    ((tuplifyVarR, Just guardExpr), qs') <- statefulT (idR, Nothing) $ childT CompQuals unnestR+                                       +    h'               <- childT CompHead tuplifyVarR >>> projectT+    let tuplifyM e = constNodeT e >>> tuplifyVarR >>> projectT+    candGuards'      <- mapM tuplifyM candGuards+    failedGuards'    <- mapM tuplifyM failedGuards+    return (inject $ Comp t h' qs', candGuards', guardExpr : failedGuards')++-- | Worker for the MergeGuard iterator: Insert the current guard into+-- the qualifier list and search for an unnesting opportunity.+unnestGuardWorkerR :: MergeGuard+unnestGuardWorkerR comp guardExpr candGuards failedGuards = do+    let C ty h qs = comp+    env <- S.fromList <$> M.keys <$> cl_bindings <$> contextT+    let compWithGuard = constT $ return $ ExprCL $ Comp ty h (insertGuard guardExpr env qs)+    (comp', candGuards', failedGuards') <- compWithGuard >>> unnestGuardR candGuards failedGuards+    ExprCL (Comp _ h' qs') <- return comp'+    return (C ty h' qs', candGuards', failedGuards')++unnestFromGuardR :: RewriteC CL+unnestFromGuardR = mergeGuardsIterR unnestGuardWorkerR+++--------------------------------------------------------------------------------+-- Rules that bring nested comprehension patterns into forms that are+-- suitable for unnesting++-- | De-Normalization: This rule is the inverse of rule M-Norm-3+-- [ [ f y | y <- g x ] x <- xs ]+-- =>+-- [ [ f z | z <- y ] | y <- [ g x | x <- xs ] ]+-- provided that+-- (a) g is complex/expensive+-- (b) g contains a comprehension+-- +-- The original comprehension produces a collection for every rule of+-- the outer collection xs and then directly performs an action on all+-- elements of the inner collections. The problem here is that the+-- comprehension nested in g might be combined into a nesting operator+-- with xs (maybe even a nestjoin), but the enclosing comprehension+-- blocks this.++--------------------------------------------------------------------------------+-- Other forms of unnesting++isComplexExpr :: Expr -> Bool+isComplexExpr e = +    case e of+        Comp{}         -> True+        If{}           -> True+        BinOp{}        -> True+        UnOp{}         -> True+        AppE2 _ op _ _ -> complexPrim2 op+        AppE1 _ op _   -> complexPrim1 op+        Lit{}          -> False+        Var{}          -> False+        Table{}        -> False+        MkTuple{}      -> False+        Let{}          -> False++containsComplexExprT :: TransformC CL ()+containsComplexExprT = onetdT isComplexExprT+  where+    isComplexExprT :: TransformC CL ()+    isComplexExprT = do+        e <- promoteT idR+        guardM $ isComplexExpr e+        return ()+        +-- | If a inner comprehension iterates over a complex function of the+-- outer element, pull the function out. The motivation of this+-- rewrite is the following: f is work performed in the head for every+-- x. The rewrite does not change that (f actually has to be performed+-- for every x), but it moves the work out of the head. This might+-- enable subsequent rewrites to move f out of the head of other+-- enclosing comprehensions as well (model use case: dft).+-- +-- [ [ e x y | y <- f x ] | x <- xs ] +-- => [ [ f [x/fst z] y | y <- snd z ] | z <- zip xs [ f x | x <- xs ] ] +-- +-- provided that f is "complex".+-- +-- We need the zip to provide the correlation between one x and the+-- group produced by f for this particular x. +-- +-- Note: This rule is actually a special case of the inverse M-Norm-3+-- rule provided above.+zipCorrelatedR :: RewriteC CL+zipCorrelatedR = do+    Comp to (Comp ti e (S (BindQ y f))) (S (BindQ x xs)) <- promoteT idR+    +    let fvs = freeVars e +    guardM $ x `elem` fvs && y `elem` fvs++    guardM $ x `elem` freeVars f++    -- Is f complex as required?+    void $ pathT [CompHead, CompQuals, QualsSingleton, BindQualExpr] containsComplexExprT++    z <- freshNameT [y]++    let genComp = Comp (listT $ typeOf f) f (S $ BindQ x xs)+        zipGen  = P.zip xs genComp+        zt      = elemT $ typeOf zipGen +        zv      = Var zt z++    ExprCL f' <- constNodeT e >>> substR x (P.fst zv)++    let innerComp = Comp ti f' (S $ BindQ y (P.snd zv))+        outerComp = Comp to innerComp (S (BindQ z zipGen))++    return $ inject outerComp++--------------------------------------------------------------------------------+-- Normalization of nesting patterns++-- | Consider the case in which a comprehension is hidden in the+-- generator of an inner comprehension, such that the generator+-- depends on the outer variable and the inner comprehension can not+-- be unnested.+-- +-- In this case, perform the inverse rewrite to M-Norm-3: Nest the+-- generator expression into the outer comprehension+-- +-- [ [ e y | y <- g x ] | x <- xs ]+-- =>+-- [ [ e y | y <- z ] | z <- [ g x | x <- xs ] ]+-- +-- provided that g contains at least one unnestable comprehension+--+-- Important: This is the dual rewrite to M-Norm-3. An unconditional+-- application will lead into a rewriting loop. It **must** be+-- combined with a rewrite that makes progress on g and xs.+nestingGenR :: RewriteC CL+nestingGenR = do+    Comp  to (Comp ti e (S (BindQ y g))) (S (BindQ x xs)) <- promoteT idR+    +    -- Generator expression g should depend on x (otherwise we could+    -- unnest directly+    guardM $ x `elem` freeVars g++    -- Generator expression g should contain at least one unnestable+    -- comprehension+    void $ constNodeT g >>> searchNestedCompT x++    z <- freshNameT []++    let gty = typeOf g++    let innerComp = Comp ti e (S (BindQ y (Var gty z)))+        genComp   = Comp (listT gty) g (S (BindQ x xs))+        outerComp = Comp to innerComp (S (BindQ z genComp))++    return $ inject outerComp
+ src/Database/DSH/CL/Opt/Normalize.hs view
@@ -0,0 +1,214 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE PatternSynonyms     #-}+    +-- | Normalize patterns from source programs (not to be confused with+-- comprehension normalization)+module Database.DSH.CL.Opt.Normalize+  ( normalizeOnceR +  , normalizeExprR+  ) where++import           Control.Monad+import           Control.Arrow+import qualified Data.Foldable              as F+import qualified Data.Traversable           as T+import           Data.Monoid+       +import           Database.DSH.Impossible+import           Database.DSH.Common.Lang+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Kure+import qualified Database.DSH.CL.Primitives as P+import           Database.DSH.CL.Opt.Auxiliary++------------------------------------------------------------------+-- Simple normalization rewrites that are applied only at the start of+-- rewriting.++-- Rewrites that are expected to only match once in the beginning and whose+-- pattern should not occur due to subsequent rewrites.++-- | Split conjunctive predicates.+splitConjunctsR :: RewriteC (NL Qual)+splitConjunctsR = splitR <+ splitEndR+  where+    splitR :: RewriteC (NL Qual)+    splitR = do+        (GuardQ (BinOp _ (SBBoolOp Conj) p1 p2)) :* qs <- idR+        return $ GuardQ p1 :* GuardQ p2 :* qs+    +    splitEndR :: RewriteC (NL Qual)+    splitEndR = do+        (S (GuardQ (BinOp _ (SBBoolOp Conj) p1 p2))) <- idR+        return $ GuardQ p1 :* (S $ GuardQ p2)+        +normalizeOnceR :: RewriteC CL+normalizeOnceR = repeatR $ anytdR $ promoteR splitConjunctsR+    +--------------------------------------------------------------------------------+-- Simple normalization rewrites that are interleaved with other rewrites.++normalizeExprR :: RewriteC CL+normalizeExprR = readerT $ \expr -> case expr of+    ExprCL AppE1{} -> comprehensionNullR+    ExprCL UnOp{}  -> notNullR <+ notExistsR+    ExprCL BinOp{} -> zeroLengthR+    ExprCL Let{}   -> unusedBindingR <+ simpleBindingR <+ referencedOnceR+    _              -> fail "not a normalizable expression"++--------------------------------------------------------------------------------+-- Normalization rewrites for universal/existential quantification.++pattern PEq e1 e2 <- BinOp _ (SBRelOp Eq) e1 e2+pattern PLength e <- AppE1 _ Length e+pattern PAnd xs <- AppE1 _ And xs+pattern POr xs <- AppE1 _ Or xs+pattern PNot e <- UnOp _ (SUBoolOp Not) e+pattern PNull e <- AppE1 _ Null e++-- Bring a NOT EXISTS pattern into universal quantification form:+-- not (or [ q | y <- ys, ps ])+-- =>+-- and [ not q | y <- ys, ps ]+notExistsR :: RewriteC CL+notExistsR = promoteT $ readerT $ \e -> case e of+    -- With range predicates+    PNot (POr (Comp t q (BindQ y ys :* ps))) -> do+    +        -- All remaining qualifiers have to be guards.+        void $ constT $ T.mapM fromGuard ps++        return $ inject $ P.and $ Comp t (P.not q) (BindQ y ys :* ps)++    -- Without range predicates+    PNot (POr (Comp t q (S (BindQ y ys)))) -> do+        return $ inject $ P.and $ Comp t (P.not q) (S $ BindQ y ys)++    _ -> fail "no match"++-- Normalization of null occurences+-- length xs == 0 => null xs+-- 0 == length xs => null xs+zeroLengthR :: RewriteC CL+zeroLengthR = promoteT $ readerT $ \e -> case e of+    PEq (PLength xs) (Lit _ (IntV 0)) -> return $ inject $ P.null xs+    PEq (Lit _ (IntV 0)) (PLength xs) -> return $ inject $ P.null xs+    _                                 -> fail "no match"++-- null [ _ | x <- xs, p1, p2, ... ] +-- => and [ not (p1 && p2 && ...) | x <- xs ]+comprehensionNullR :: RewriteC CL+comprehensionNullR = do+    PNull (Comp _ _ (BindQ x xs :* guards)) <- promoteT idR+    +    -- We need exactly one generator and at least one guard.+    guardExprs           <- constT $ T.mapM fromGuard guards++    -- Merge all guards into a conjunctive form+    let conjPred = P.not $ F.foldl1 P.conj guardExprs+    return $ inject $ P.and $ Comp (listT boolT) conjPred (S $ BindQ x xs)++-- not $ null [ _ | x <- xs, ps ]+-- =>+-- not $ and [ not ps | x <- xs ] (comprehensionNullR)+-- =>+-- or [ ps | x <- xs ]+notNullR :: RewriteC CL+notNullR = do+    PNot (PAnd (Comp _ (PNot p) (S (BindQ x xs)))) <- promoteT idR+    return $ inject $ P.or (Comp (listT boolT) p (S (BindQ x xs)))++--------------------------------------------------------------------------------+-- Inline let bindings++-- | This function inlines let-bound expressions. In contrast to+-- general substitution, we do not inline into comprehensions, even if+-- we could. The reason is that expressions should not be evaluated+-- iteratively if they are loop-invariant.+inlineBindingR :: Ident -> Expr -> RewriteC CL+inlineBindingR v s = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    ExprCL (Var _ n) | n == v          -> return $ inject s++    -- If a let-binding shadows the name we substitute, only descend+    -- into the bound expression.+    ExprCL (Let _ n _ _) | n == v      -> promoteR $ letR (extractR $ inlineBindingR v s) idR+    ExprCL (Let _ n _ _) | otherwise   ->+        if n `elem` freeVars s+        -- If the let-bound name occurs free in the substitute,+        -- alpha-convert the binding to avoid capturing the name.+        then $unimplemented >>> anyR (substR v s)+        else anyR $ inlineBindingR v s++    -- We don't inline into comprehensions to avoid conflicts with+    -- loop-invariant extraction.+    ExprCL (Comp _ _ _)                -> idR+    ExprCL _                           -> anyR $ inlineBindingR v s+    _                                  -> $impossible++-- | Count all occurences of an identifier for let-inlining.+countVarRefT :: Ident -> TransformC CL (Sum Int)+countVarRefT v = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    ExprCL (Var _ n) | n == v          -> return 1+    ExprCL (Var _ _) | otherwise       -> return 0++    ExprCL (Let _ n _ _) | n == v      -> promoteT $ letT (constT $ return 0) +                                                          (extractT $ countVarRefT v)+                                                          (\_ _ c1 c2 -> c1 + c2)+    ExprCL (Let _ _ _ _) | otherwise   -> promoteT $ letT (extractT $ countVarRefT v)+                                                          (extractT $ countVarRefT v)+                                                          (\_ _ c1 c2 -> c1 + c2)++    ExprCL (Comp _ _ qs) | v `elem` compBoundVars qs -> promoteT $ compT (constT $ return 0)+                                                                         (extractT $ countVarRefT v)+                                                                         (\_ c1 c2 -> c1 + c2)+    ExprCL (Comp _ _ _) | otherwise                  -> promoteT $ compT (extractT $ countVarRefT v)+                                                                         (extractT $ countVarRefT v)+                                                                         (\_ c1 c2 -> c1 + c2)+    ExprCL Table{}                      -> return 0+    ExprCL Lit{}                        -> return 0++    ExprCL _                            -> allT (countVarRefT v)++    QualsCL (BindQ v' _ :* _) | v == v' -> childT QualsHead (countVarRefT v)+    QualsCL _                           -> allT (countVarRefT v)++    QualCL  _                           -> allT (countVarRefT v)++-- | Remove a let-binding that is not referenced.+unusedBindingR :: RewriteC CL+unusedBindingR = do+    Let _ x _ e2 <- promoteT idR+    0            <- childT LetBody $ countVarRefT x+    return $ inject e2++-- | Inline a let-binding that is only referenced once.+referencedOnceR :: RewriteC CL+referencedOnceR = do+    Let _ x e1 _ <- promoteT idR+    1            <- childT LetBody $ countVarRefT x++    -- We do not inline into comprehensions, but 'countVarRef' counts+    -- all occurences including those in comprehensions. For this+    -- reason, we check if the occurence was actually eliminated by+    -- inlining and fail otherwise.+    body'        <- childT LetBody (inlineBindingR x e1)+    0 <- (constT $ return body') >>> countVarRefT x+    return body'++simpleExpr :: Expr -> Bool+simpleExpr Table{} = True+simpleExpr Var{}   = True+simpleExpr _       = False++-- | Inline a let-binding that binds a simple expression.+simpleBindingR :: RewriteC CL+simpleBindingR = do+    Let _ x e1 _ <- promoteT idR+    guardM $ simpleExpr e1+    childR LetBody $ substR x e1+
+ src/Database/DSH/CL/Opt/PartialEval.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE TemplateHaskell     #-}+    +-- | Support rewrites (partial evaluation, house cleaning)+module Database.DSH.CL.Opt.PartialEval+  ( partialEvalR+  ) where+  +import           Database.DSH.Common.Nat+import           Database.DSH.Common.Lang+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Kure++--------------------------------------------------------------------------------+-- Partial evaluation rules++-- | Eliminate tuple construction if the elements are first and second of the+-- same pair:+-- pair (fst x) (snd x) => x+identityPairR :: RewriteC CL+identityPairR = do+    MkTuple _ [ AppE1 _ (TupElem First)  v@(Var tupleTy x) +              , AppE1 _ (TupElem (Next First)) (Var _ x')+              ] <- promoteT idR++    -- Check that the original value actually was a /pair/ and that no+    -- elements are discarded.+    TupleT [_, _] <- return tupleTy++    guardM $ x == x'+    return $ inject v++tupleElemR :: RewriteC CL+tupleElemR = do+    AppE1 _ (TupElem i) (MkTuple _ es) <- promoteT idR+    return $ inject $ es !! (tupleIndex i - 1)++fromLiteral :: Expr -> TransformC CL Val+fromLiteral (Lit _ val) = return val+fromLiteral _           = fail "not a literal"++literalTupleR :: RewriteC CL+literalTupleR = do+    MkTuple tupTy elems <- promoteT idR+    vals                <- mapM fromLiteral elems+    return $ inject $ Lit tupTy $ TupleV vals++literalAppendR :: RewriteC CL+literalAppendR = do+    AppE2 listTy Append x y <- promoteT idR+    ListV xVals             <- fromLiteral x+    ListV yVals             <- fromLiteral y+    return $ inject $ Lit listTy $ ListV $ xVals ++ yVals++literalSingletonR :: RewriteC CL+literalSingletonR = do+    AppE1 listTy Singleton x <- promoteT idR+    xVal                     <- fromLiteral x+    return $ inject $ Lit listTy $ ListV [xVal]++appendEmptyLeftR :: RewriteC CL+appendEmptyLeftR = do+    AppE2 _ Append (Lit _ (ListV [])) ys <- promoteT idR+    return $ inject ys++appendEmptyRightR :: RewriteC CL+appendEmptyRightR = do+    AppE2 _ Append xs (Lit _ (ListV [])) <- promoteT idR+    return $ inject xs++partialEvalR :: RewriteC CL+partialEvalR = +    readerT $ \cl -> case cl of+        ExprCL AppE1{}   -> tupleElemR <+ literalSingletonR+        ExprCL MkTuple{} -> identityPairR <+ literalTupleR+        ExprCL AppE2{}   -> literalAppendR <+ appendEmptyLeftR <+ appendEmptyRightR+        _                -> fail "can't apply partial evaluation rules"
+ src/Database/DSH/CL/Opt/PostProcess.hs view
@@ -0,0 +1,72 @@+module Database.DSH.CL.Opt.PostProcess+    ( introduceCartProductsR+    ) where++import           Control.Arrow++import           Database.DSH.Common.Lang+import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary+import qualified Database.DSH.CL.Primitives as P++--------------------------------------------------------------------------------+++--------------------------------------------------------------------------------+-- Turn adjacent generators into cartesian products:+-- [ e | ..., x <- xs, y <- ys, qs ]+-- =>+-- [ e[x/fst x][y/snd x] | ..., x <- xs × ys, qs[x/fst x][y/snd x] ]++mkproduct :: (Ident, Expr) -> (Ident, Expr) -> (RewriteC CL, Qual)+mkproduct (x, xs) (y, ys) =+    -- Conditions for the rewrite are fulfilled.+    let xst          = typeOf xs+        yst          = typeOf ys+        xt           = elemT xst+        yt           = elemT yst+        tuplifyHeadR = tuplifyR x (x, xt) (y, yt)+        joinGen      = BindQ x (P.cartproduct xs ys)++    in (tuplifyHeadR, joinGen)++cartProductR :: Rewrite CompCtx TuplifyM (NL Qual)+cartProductR = do+    readerT $ \e -> case e of+        BindQ x xs :* BindQ y ys :* qs -> do+            -- xs and ys generators must be independent+            guardM $ x `notElem` freeVars ys++            let (tuplifyHeadR, q') = mkproduct (x, xs) (y, ys)+            -- Next, we apply the tuplifyHeadR rewrite to the tail,+            -- i.e. to all following qualifiers+            -- FIXME why is extractT required here?+            qs' <- catchesT [ liftstateT $ (constT $ return qs)+                                           >>> (extractR tuplifyHeadR)+                            , constT $ return qs+                            ]++            -- The tuplify rewrite must be handed to the top level+            constT $ put tuplifyHeadR++            return $ q' :* qs'++        BindQ x xs :* (S (BindQ y ys)) -> do+            -- xs and ys generators must be independent+            guardM $ x `notElem` freeVars ys++            let (tuplifyHeadR, q') = mkproduct (x, xs) (y, ys)++            -- The tuplify rewrite must be handed to the top level+            constT $ put tuplifyHeadR++            return (S q')+        _ -> fail "no match"++introduceCartProductsR :: RewriteC CL+introduceCartProductsR = do+    Comp t _ _          <- promoteT idR+    (tuplifyHeadR, qs') <- statefulT idR $ childT CompQuals (promoteR cartProductR) >>> projectT+    ExprCL h'           <- childT CompHead tuplifyHeadR+    return $ inject $ Comp t h' qs'
+ src/Database/DSH/CL/Opt/PredPushdown.hs view
@@ -0,0 +1,245 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | This module implements predicate pushdown on comprehensions.+module Database.DSH.CL.Opt.PredPushdown+  ( predpushdownR+  ) where++import           Control.Applicative+import           Control.Arrow+import qualified Data.List.NonEmpty       as N+import qualified Data.Set                 as S++import           Database.DSH.Common.Lang+import           Database.DSH.Common.Nat+import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary++--------------------------------------------------------------------------------+-- Auxiliary functions++-- | Return path to occurence of variable x+varPathT :: Ident -> TransformC CL PathC+varPathT x = do+    Var _ x' <- promoteT idR+    guardM $ x == x'+    snocPathToPath <$> absPathT++-- | Collect all paths to variable x in the current expression and+-- turn them into relative paths.+allVarPathsT :: Ident -> TransformC CL [PathC]+allVarPathsT x = do+    varPaths <- collectT $ varPathT x+    guardM $ not $ null varPaths+    parentPathLen <- length <$> snocPathToPath <$> absPathT+    let localPaths = map (init . drop parentPathLen) varPaths+    return localPaths++-- | All occurences of variable x must occur in the form of a tuple+-- accessor, either fst or snd. Remove this tuple accessor.+unTuplifyR :: (Prim1 -> Bool) -> PathC -> RewriteC CL+unTuplifyR isTupleOp path = pathR path $ do+    AppE1 ty op (Var _ x)  <- promoteT idR+    guardM $ isTupleOp op+    return $ inject $ Var ty x++--------------------------------------------------------------------------+-- Push a guard into a branch of a join operator++-- | Try to push predicate into the left input of a binary operator+-- which produces tuples: equijoin, nestjoin, nestproduct+pushLeftTupleR :: Ident -> Expr -> RewriteC CL+pushLeftTupleR x p = do+    AppE2 t op xs ys <- promoteT idR++    let predTrans = constT $ return $ inject p++    localPaths <- predTrans >>> allVarPathsT x++    ExprCL p' <- predTrans >>> andR (map (unTuplifyR (== (TupElem First))) localPaths)++    let xst = typeOf xs++    let filterComp = Comp xst (Var (elemT xst) x) (BindQ x xs :* S (GuardQ p'))+    return $ inject $ AppE2 t op filterComp ys++-- | Try to push predicate into the right input of a binary operator+-- which produces tuples: equijoin+pushRightTupleR :: Ident -> Expr -> RewriteC CL+pushRightTupleR x p = do+    AppE2 t op xs ys <- promoteT idR++    let predTrans = constT $ return $ inject p++    localPaths <- predTrans >>> allVarPathsT x++    ExprCL p' <- predTrans >>> andR (map (unTuplifyR (== (TupElem (Next (First))))) localPaths)++    let yst = typeOf ys++    let filterComp = Comp yst (Var (elemT yst) x) (BindQ x ys :* S (GuardQ p'))+    return $ inject $ AppE2 t op xs filterComp++pushLeftOrRightTupleR :: Ident -> Expr -> RewriteC CL+pushLeftOrRightTupleR x p = pushLeftTupleR x p <+ pushRightTupleR x p++-- | Try to push predicates into the left input of a binary operator+-- which produces only the left input, i.e. semijoin, antijoin+pushLeftR :: Ident -> Expr -> RewriteC CL+pushLeftR x p = do+    AppE2 ty op xs ys <- promoteT idR+    let xst = typeOf xs+    let xs' = Comp xst (Var (elemT xst) x) (BindQ x xs :* (S $ GuardQ p))+    return $ inject $ AppE2 ty op xs' ys++--------------------------------------------------------------------------+-- Merging of join predicates into already established theta-join+-- operators+--+-- A predicate can be merged into a theta-join as an additional+-- conjunct if it has the shape of a join predicate and if its left+-- expression refers only to the fst component of the join pair and+-- the right expression refers only to the snd component (or vice+-- versa).++mkMergeableJoinPredT :: Ident -> Expr -> BinRelOp -> Expr -> TransformC CL (JoinConjunct JoinExpr)+mkMergeableJoinPredT x leftExpr op rightExpr = do+    let constLeftExpr = constT $ return $ inject leftExpr+        constRightExpr = constT $ return $ inject rightExpr++    leftVarPaths  <- constLeftExpr >>> allVarPathsT x+    rightVarPaths <- constRightExpr >>> allVarPathsT x++    leftExpr'     <- constLeftExpr+                         >>> andR (map (unTuplifyR (== (TupElem First))) leftVarPaths)+                         >>> projectT+                         >>> toJoinExpr x++    rightExpr'    <- constRightExpr+                         >>> andR (map (unTuplifyR (== (TupElem (Next First)))) rightVarPaths)+                         >>> projectT+                         >>> toJoinExpr x++    return $ JoinConjunct leftExpr' op rightExpr'++mirrorRelOp :: BinRelOp -> BinRelOp+mirrorRelOp Eq  = Eq+mirrorRelOp Gt  = Lt+mirrorRelOp GtE = LtE+mirrorRelOp Lt  = Gt+mirrorRelOp LtE = GtE+mirrorRelOp NEq = NEq++splitMergeablePredT :: Ident -> Expr -> TransformC CL (JoinConjunct JoinExpr)+splitMergeablePredT x p = do+    ExprCL (BinOp _ (SBRelOp op) leftExpr rightExpr) <- return $ inject p+    guardM $ freeVars p == [x]++    -- We might have e1(fst x) op e2(snd x) or e1(snd x) op e2(fst x)+    mkMergeableJoinPredT x leftExpr op rightExpr+      <+ mkMergeableJoinPredT x rightExpr (mirrorRelOp op) leftExpr++-- | If a predicate can be turned into a join predicate, merge it into+-- the current theta join.+mergePredIntoJoinR :: Ident -> Expr -> RewriteC CL+mergePredIntoJoinR x p = do+    AppE2 t (ThetaJoin (JoinPred ps)) xs ys <- promoteT idR+    joinConjunct <- splitMergeablePredT x p++    let extendedJoin = ThetaJoin (JoinPred $ joinConjunct N.<| ps)++    return $ inject $ AppE2 t extendedJoin xs ys++-- | Push into the /first/ argument (input) of some operator that+-- commutes with selection.++-- This was nicer with a higher-order 'sortWith'. With first-order+-- 'sort', we have to push the predicate into both arguments, which+-- works only if the comprehension for the sorting criteria is still+-- in its original form.+pushSortInputR :: Ident -> Expr -> RewriteC CL+pushSortInputR x p = do+    AppE2 t Sort xs (Comp st se (S (BindQ x' xs'))) <- promoteT idR++    -- FIXME this compares whole terms in an uncontrolled way and+    -- could be too expensive.+    guardM $ xs == xs'+    guardM $ x == x'++    let xst = typeOf xs+        xt  = elemT xt+        -- We reuse the generator variable for the filter comprehension+        xsFiltered = Comp xst (Var xt x) (BindQ x xs :* S (GuardQ p))+        ssFiltered = Comp st se (BindQ x' xs' :* S (GuardQ p))++    return $ inject $ AppE2 t Sort xsFiltered ssFiltered++--------------------------------------------------------------------------+-- Take remaining comprehension guards and try to push them into the+-- generator. This might be accomplished by either merging it into a+-- join, pushing it into a join input or pushing it through some other+-- operator that commutes with selection (e.g. sorting).++pushPredicateR :: Ident -> Expr -> RewriteC CL+pushPredicateR x p = do+    readerT $ \e -> case e of+        -- First, try to merge the predicate into the join. For+        -- regular joins and products, non-join predicates might apply+        -- to the left or right input.+        ExprCL (AppE2 _ (ThetaJoin _) _ _) -> mergePredIntoJoinR x p+                                              <+ pushLeftOrRightTupleR x p+        ExprCL (AppE2 _ CartProduct _ _)   -> pushLeftOrRightTupleR x p++        -- For nesting operators, a guard can only refer to the left+        -- input, i.e. the original outer generator.++        -- FIXME why commented out?+        -- ExprCL (AppE2 _ (Prim2 (NestProduct _ _) _) _ _) -> pushLeftTupleR p+        ExprCL (AppE2 _ (NestJoin _) _ _)  -> pushLeftTupleR x p++        -- Semi- and Antijoin operators produce a subset of their left+        -- input. A filter can only apply to the left input,+        -- consequently.+        ExprCL (AppE2 _ (SemiJoin _) _ _)  -> pushLeftR x p+        ExprCL (AppE2 _ (AntiJoin _) _ _)  -> pushLeftR x p++        -- Sorting commutes with selection+        ExprCL (AppE2 _ Sort _ _)          -> pushSortInputR x p+        _                                  -> fail "expression does not allow predicate pushing"++pushQualsR :: RewriteC CL+pushQualsR = do+    BindQ x _ :* GuardQ p :* qs <- promoteT idR+    [x'] <- return $ freeVars p+    guardM $ x == x'+    ExprCL gen' <- pathT [QualsHead, BindQualExpr] (pushPredicateR x p)+    return $ inject $ BindQ x gen' :* qs++pushQualsEndR :: RewriteC CL+pushQualsEndR = do+    BindQ x _ :* (S (GuardQ p)) <- promoteT idR+    [x'] <- return $ freeVars p+    guardM $ x == x'+    ExprCL gen' <- pathT [QualsHead, BindQualExpr] (pushPredicateR x p)+    return $ inject $ S $ BindQ x gen'++pushDownSinglePredR :: RewriteC CL+pushDownSinglePredR = do+    Comp _ _ _ <- promoteT idR+    childR CompQuals (promoteR $ pushQualsR <+ pushQualsEndR)++pushDownPredsR :: MergeGuard+pushDownPredsR comp guard guardsToTry leftOverGuards = do+    let C ty h qs = comp+    env <- S.fromList <$> inScopeNames <$> contextT+    let compExpr = ExprCL $ Comp ty h (insertGuard guard env qs)+    ExprCL (Comp _ _ qs') <- constT (return compExpr) >>> pushDownSinglePredR+    return (C ty h qs', guardsToTry, leftOverGuards)++-- | Push down all guards in a qualifier list, if possible.+predpushdownR :: RewriteC CL+predpushdownR = mergeGuardsIterR pushDownPredsR
+ src/Database/DSH/CL/Opt/Resugar.hs view
@@ -0,0 +1,69 @@+{-# LANGUAGE PatternSynonyms #-}++-- | Resguaring rules that restore a source comprehension form from+-- the desugared 'concatMap' form.+module Database.DSH.CL.Opt.Resugar+    ( resugarR+    ) where++import           Control.Arrow++import           Database.DSH.Common.Lang+import           Database.DSH.Common.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Kure+import           Database.DSH.CL.Opt.PartialEval++pattern ConcatP xs   <- AppE1 _ Concat xs+pattern SingletonP x <- AppE1 _ Singleton x+pattern GuardP p     <- AppE1 _ Guard p++-- | Eliminate a singleton list in a comprehension head.+-- concat [ [e] | qs ] => [ e | qs ]+concatCompSingletonR :: RewriteC CL+concatCompSingletonR = do+    ConcatP (Comp (ListT ty) (SingletonP e) qs) <- promoteT idR+    return $ inject $ Comp ty e qs++-- | Eliminate a singleton literal list in a comprehension head.+-- concat [ [v] | qs ] => [ v | qs ]+concatCompSingletonLitR :: RewriteC CL+concatCompSingletonLitR = do+    ConcatP (Comp _ (Lit (ListT ty) (ListV [v])) qs) <- promoteT idR+    return $ inject $ Comp (ListT $ ListT ty) (Lit ty v) qs++-- | Merge nested comprehensions+-- concat [ [ e | qs' ] | qs ] => [ e | qs, qs' ]+concatNestedCompR :: RewriteC CL+concatNestedCompR = do+    ConcatP (Comp _ (Comp compTy innerHead innerQs) outerQs) <- promoteT idR+    return $ inject $ Comp compTy innerHead (appendNL outerQs innerQs)++-- | Eliminate the guard combinator+-- [ e | qs, x <- guard p, qs' ] => [ e | qs, p, qs' ]+-- FIXME To be extra sure, we should check wether x occurs free in  or qs'+guardGeneratorR :: RewriteC (NL Qual)+guardGeneratorR = readerT $ \qual -> case qual of+    BindQ _ (GuardP p) :* qs -> do+        return $ GuardQ p :* qs+    S (BindQ _ (GuardP p))     -> do+        return $ S $ GuardQ p+    _                          -> fail "not a guard combinator"++guardGeneratorsR :: RewriteC CL+guardGeneratorsR = do+    Comp _ _ _ <- promoteT idR+    childR CompQuals (promoteR $ onetdR guardGeneratorR)++resugarRulesR :: RewriteC CL+resugarRulesR = readerT $ \expr -> case expr of+    ExprCL (ConcatP (Comp _ _ _)) -> concatCompSingletonR+                                     <+ concatCompSingletonLitR+                                     <+ concatNestedCompR+    ExprCL (Comp _ _ _)           -> guardGeneratorsR+    ExprCL _                      -> partialEvalR+    _                    -> fail "no resugaring rule applies"++-- | Resugar a comprehension.+resugarR :: RewriteC CL+resugarR = (repeatR $ anybuR resugarRulesR) >>> debugShow "resugared"
+ src/Database/DSH/CL/Opt/SemiJoin.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE PatternSynonyms #-}++module Database.DSH.CL.Opt.SemiJoin+    ( semijoinR+    ) where++import           Control.Arrow+import qualified Data.Traversable as T+import           Data.List+import           Data.List.NonEmpty(NonEmpty((:|)))+import qualified Data.List.NonEmpty as NL++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary+import           Database.DSH.Common.Lang+import qualified Database.DSH.CL.Primitives as P++--------------------------------------------------------------------------------+-- Introduce semi joins (existential quantification)++pattern POr xs <- AppE1 _ Or xs+pattern PTrue = Lit BoolT (BoolV True)++existentialQualR :: RewriteC (NL Qual)+existentialQualR = readerT $ \quals -> case quals of+    -- Special case: existential quantifier without a quantifier predicate+    -- [ ... | ..., x <- xs, or [ True | y <- ys, ps ], ... ]+    BindQ x xs :* (GuardQ (POr (Comp _ PTrue (BindQ y ys :* ps)))) :* qs -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) Nothing (Just ps)+        return $ semijoinGen :* qs++    -- Special case: existential quantifier without a quantifier predicate+    -- [ ... | ..., x <- xs, or [ True | y <- ys, ps ] ]+    BindQ x xs :* (S (GuardQ (POr (Comp _ PTrue (BindQ y ys :* ps))))) -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) Nothing (Just ps)+        return $ S semijoinGen++    -- Special case: Existential quantifier without a range predicate+    -- [ ... | ..., x <- xs, or [ q | y <- ys ], ... ]+    BindQ x xs :* (GuardQ (POr (Comp _ q (S (BindQ y ys))))) :* qs -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) (Just q) Nothing+        return $ semijoinGen :* qs++    -- Special case: Existential quantifier without a range predicate+    -- [ ... | ..., x <- xs, or [ q | y <- ys ] ]+    BindQ x xs :* (S (GuardQ (POr (Comp _ q (S (BindQ y ys)))))) -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) (Just q) Nothing+        return $ S semijoinGen+    +    -- Existential quantifier with range and quantifier predicates+    -- [ ... | ..., x <- xs, or [ True | y <- ys, ps ], ... ]+    BindQ x xs :* (GuardQ (POr (Comp _ q (BindQ y ys :* ps)))) :* qs -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) (Just q) (Just ps)+        return $ semijoinGen :* qs++    -- Existential quantifier with range and quantifier predicates+    -- [ ... | ..., x <- xs, or [ True | y <- ys, ps ] ]+    BindQ x xs :* (S (GuardQ (POr (Comp _ q (BindQ y ys :* ps))))) -> do+        -- Generators have to be indepedent+        guardM $ x `notElem` freeVars ys++        semijoinGen <- mkExistentialSemiJoinT (x, xs) (y, ys) (Just q) (Just ps)+        return $ S semijoinGen++    _ -> fail "no match"++mkExistentialSemiJoinT :: (Ident, Expr) +                       -> (Ident, Expr)+                       -> Maybe Expr+                       -> Maybe (NL Qual)+                       -> TransformC (NL Qual) Qual+mkExistentialSemiJoinT (x, xs) (y, ys) mq mps = do+    let yst = typeOf ys+        yt  = elemT yst++    -- All inner qualifiers have to be guards.+    guardExprs <- case mps of+        Just ps -> constT (T.mapM fromGuard ps) >>^ toList+        Nothing -> return []++    quantExprs <- case mq of+        Just q  -> constT (return $ inject q) >>> conjunctsT >>^ NL.toList+        Nothing -> return []++    let allExprs = guardExprs ++ quantExprs++    -- We demand at least one predicate expression+    guardM $ not $ null allExprs+        +    -- Separate those guards that can be evaluated just on the+    -- inner generator+    let (innerGuards, corrGuards) = partition (\e -> freeVars e == [y]) +                                              allExprs++    let ys' = case innerGuards of+          ige : iges -> let igs = fmap GuardQ $ fromListSafe ige iges+                        in Comp yst (Var yt y) (BindQ y ys :* igs)+          []         -> ys++    corrPreds <- constT (return corrGuards) >>> mapT (splitJoinPredT x y)++    case corrPreds of+        cp : cps -> return $ BindQ x $ P.semijoin xs ys' (JoinPred $ cp :| cps)+        _        -> fail "there have to be correlation predicates for a semijoin"+ +++existentialQualsR :: RewriteC (NL Qual)+existentialQualsR = onetdR existentialQualR++semijoinR :: RewriteC CL+semijoinR = do+    Comp _ _ _ <- promoteT idR+    childR CompQuals (promoteR existentialQualsR)
+ src/Database/DSH/CL/Opt/ThetaJoin.hs view
@@ -0,0 +1,91 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE QuasiQuotes         #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Introduce simple theta joins.+module Database.DSH.CL.Opt.ThetaJoin+    ( thetajoinR+    ) where++import           Control.Arrow++import           Database.DSH.CL.Kure+import           Database.DSH.CL.Lang+import           Database.DSH.CL.Opt.Auxiliary+import           Database.DSH.Common.Lang+import qualified Database.DSH.CL.Primitives as P++--------------------------------------------------------------------------------+-- Introduce simple theta joins++-- | Concstruct an thetajoin generator+mkthetajoinT+  :: Expr  -- ^ The predicate+  -> Ident -- ^ Identifier from the first generator+  -> Ident -- ^ Identifier from the second generator+  -> Expr  -- ^ First generator expression+  -> Expr  -- ^ Second generator expression+  -> Transform CompCtx TuplifyM (NL Qual) (RewriteC CL, Qual)+mkthetajoinT joinPred x y xs ys = do+    -- Generators have to be indepedent+    guardM $ x `notElem` freeVars ys++    -- The predicate must be a join predicate+    joinConjunct <- constT (return joinPred) >>> (liftstateT $ splitJoinPredT x y)++    -- Conditions for the rewrite are fulfilled.+    let xst          = typeOf xs+        yst          = typeOf ys+        xt           = elemT xst+        yt           = elemT yst+        tuplifyHeadR = tuplifyR x (x, xt) (y, yt)+        joinGen      = BindQ x (P.thetajoin xs ys (singlePred joinConjunct))++    return (tuplifyHeadR, joinGen)++-- | Match a thetajoin pattern in the middle of a qualifier list+thetajoinQualR :: Rewrite CompCtx TuplifyM (NL Qual)+thetajoinQualR = do+    -- We need two generators followed by a predicate+    BindQ x xs :* BindQ y ys :* GuardQ p :* qs <- promoteT idR++    (tuplifyHeadR, q') <- mkthetajoinT p x y xs ys++    -- Next, we apply the tuplifyHeadR rewrite to the tail, i.e. to all following+    -- qualifiers+    -- FIXME why is extractT required here?+    qs' <- catchesT [ liftstateT $ (constT $ return qs) >>> (extractR tuplifyHeadR)+                    , constT $ return qs+                    ]++    -- The tuplify rewrite must be handed to the top level+    constT $ put tuplifyHeadR++    return $ q' :* qs'++-- | Match a thetajoin pattern at the end of a qualifier list+thetajoinQualEndR :: Rewrite CompCtx TuplifyM (NL Qual)+thetajoinQualEndR = do+    -- We need two generators followed by a predicate+    BindQ x xs :* BindQ y ys :* (S (GuardQ p)) <- promoteT idR++    (tuplifyHeadR, q') <- mkthetajoinT p x y xs ys++    -- The tuplify rewrite must be handed to the top level+    constT $ put tuplifyHeadR++    return (S q')++thetajoinQualsR :: Rewrite CompCtx TuplifyM (NL Qual)+thetajoinQualsR = onetdR (thetajoinQualEndR <+ thetajoinQualR)++thetajoinR :: [Expr] -> [Expr] -> TransformC CL (CL, [Expr], [Expr])+thetajoinR currentGuards testedGuards = do+    Comp t _ _          <- promoteT idR+    (tuplifyHeadR, qs') <- statefulT idR $ childT CompQuals (promoteR thetajoinQualsR >>> projectT)+    e'                  <- (tryR $ childT CompHead tuplifyHeadR) >>> projectT+    -- FIXME should propably wrap tuplifyHeadR in tryR+    currentGuards'      <- constT (return currentGuards) >>> mapT (extractR tuplifyHeadR)+    testedGuards'       <- constT (return testedGuards) >>> mapT (extractR tuplifyHeadR)+    return $ (inject $ Comp t e' qs', currentGuards', testedGuards')
+ src/Database/DSH/CL/Primitives.hs view
@@ -0,0 +1,369 @@+{-# LANGUAGE TemplateHaskell #-}++-- | Smart constructors for CL primitives+module Database.DSH.CL.Primitives where++import qualified Prelude                    as P++import qualified Data.List                  as List+import           Text.Printf++import           Database.DSH.CL.Lang+import qualified Database.DSH.Common.Lang   as L+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Pretty+import           Database.DSH.Impossible++tyErr :: P.String -> a+tyErr comb = P.error P.$ printf "CL.Primitives type error in %s" comb++tyErrShow :: P.String -> [Type] -> a+tyErrShow comb ts = P.error (printf "CL.Primitives type error in %s: %s" comb (P.show P.$ P.map pp ts))++if_ :: Expr -> Expr -> Expr -> Expr+if_ c t e = if BoolT P.== typeOf c+            then If (typeOf t) c t e+            else tyErr "if_"++reverse :: Expr -> Expr+reverse e = let t@(ListT _) = typeOf e+             in AppE1 t Reverse e++length :: Expr -> Expr+length e = let t = typeOf e+           in if isList t+              then AppE1 intT Length e+              else tyErr "length"++null :: Expr -> Expr+null e =+    if isList t+    then AppE1 boolT Null e+    else tyErr "null"++  where t = typeOf e++and :: Expr -> Expr+and e = let t = typeOf e+         in if listT boolT P.== t+            then AppE1 boolT And e+            else tyErrShow "and" [t]++or :: Expr -> Expr+or e = let t = typeOf e+         in if listT boolT P.== t+            then AppE1 boolT Or e+            else tyErr "or"++concat :: Expr -> Expr+concat e = let t = typeOf e+            in if listDepth t P.> 1+               then AppE1 (unliftType t) Concat e+               else tyErr "concat"++-- reshape :: [a] -> [[a]]+reshape :: P.Integer -> Expr -> Expr+reshape n e =+    let t = typeOf e+    in AppE1 (ListT t) (Reshape n) e++-- transpose :: [[a]] -> [[a]]+transpose :: Expr -> Expr+transpose e =+    let t = typeOf e+    in AppE1 t Transpose e++sum :: Expr -> Expr+sum e = let (ListT t) = typeOf e+         in if isNum t+                then AppE1 t Sum e+                else tyErr "sum"++avg :: Expr -> Expr+avg e = let (ListT t) = typeOf e+         in if isNum t+                then AppE1 doubleT Avg e+                else tyErr "avg"++minimum :: Expr -> Expr+minimum e = let (ListT t) = typeOf e+             in if isNum t+                 then AppE1 t Minimum e+                 else tyErr "minimum"++maximum :: Expr -> Expr+maximum e = let (ListT t) = typeOf e+             in if isNum t+                 then AppE1 t Maximum e+                 else tyErr "maximum"++the :: Expr -> Expr+the e = let (ListT t) = typeOf e+         in AppE1 t The e++head :: Expr -> Expr+head e = let (ListT t) = typeOf e+          in AppE1 t Head e++last :: Expr -> Expr+last e = let (ListT t) = typeOf e+          in AppE1 t Last e++tail :: Expr -> Expr+tail e = let (ListT t) = typeOf e+          in AppE1 (ListT t) Tail e++nub :: Expr -> Expr+nub e = let (ListT t) = typeOf e+         in AppE1 (ListT t) Nub e++number :: Expr -> Expr+number e = let (ListT t) = typeOf e+           in AppE1 (ListT (pairT t IntT )) Number e++guard :: Expr -> Expr+guard e = AppE1 (listT UnitT) Guard e++init :: Expr -> Expr+init e = let (ListT t) = typeOf e+        in AppE1 (ListT t) Init e++tupElem :: TupleIndex -> Expr -> Expr+tupElem f e =+    let t = tupleElemT (typeOf e) f+    in AppE1 t (TupElem f) e++fst :: Expr -> Expr+fst e = tupElem First e++snd :: Expr -> Expr+snd e = tupElem (Next First) e++singleGenComp :: Expr -> L.Ident -> Expr -> Expr+singleGenComp bodyExp v gen =+    let bodyTy = typeOf bodyExp+    in Comp (listT bodyTy) bodyExp (S P.$ BindQ v gen)++group :: Expr -> Expr -> Expr+group xs gs = let ListT xt  = typeOf xs+                  ListT grt = typeOf gs+                  rt        = ListT (TupleT [grt, ListT xt])+              in AppE2 rt Group xs gs++sort :: Expr -> Expr -> Expr+sort xs ss = AppE2 (typeOf xs) Sort xs ss++pair :: Expr -> Expr -> Expr+pair a b = tuple [a, b]++tuple :: [Expr] -> Expr+tuple es =+    let ts = P.map typeOf es+        rt = TupleT ts+    in MkTuple rt es++append :: Expr -> Expr -> Expr+append e1 e2 = let t1@(ListT _) = typeOf e1+                   t2@(ListT _) = typeOf e2+                in if t1 P.== t2+                    then AppE2 t1 Append e1 e2+                    else tyErr "append"++index :: Expr -> Expr -> Expr+index e1 e2 = let ListT t = typeOf e1+                  t2 = typeOf e2+                in if intT P.== t2+                    then AppE2 t Index e1 e2+                    else tyErr "index"++sng :: Expr -> Expr+sng e = AppE1 (listT P.$ typeOf e) Singleton e++zip :: Expr -> Expr -> Expr+zip e1 e2 = let ListT t1' = typeOf e1+                ListT t2' = typeOf e2+             in AppE2 (listT P.$ pairT t1' t2') Zip e1 e2++var :: Type -> P.String -> Expr+var = Var++table :: Type -> P.String -> [L.Column] -> L.TableHints -> Expr+table = Table++cond :: Expr -> Expr -> Expr -> Expr+cond eb et ee = let tb = typeOf eb+                    tt = typeOf et+                    te = typeOf ee+                 in if tb P.== boolT P.&& tt P.== te+                      then If te eb et ee+                      else tyErr "cond"++let_ :: L.Ident -> Expr -> Expr -> Expr+let_ x e1 e2 = let t = typeOf e2 in Let t x e1 e2++---------------------------------------------------------------------------------------+-- Smart constructors for join operators++cartproduct :: Expr -> Expr -> Expr+cartproduct xs ys = AppE2 resType CartProduct xs ys+  where+    resType  = listT P.$ pairT (elemT P.$ typeOf xs) (typeOf ys)++nestjoin :: Expr -> Expr -> L.JoinPredicate L.JoinExpr -> Expr+nestjoin xs ys p = AppE2 resType (NestJoin p) xs ys+  where+    resType  = listT P.$ pairT (elemT P.$ typeOf xs) (typeOf ys)++thetajoin :: Expr -> Expr -> L.JoinPredicate L.JoinExpr -> Expr+thetajoin xs ys p = AppE2 rt (ThetaJoin p) xs ys+  where+    xst = typeOf xs+    yst = typeOf ys+    rt  = listT (pairT (elemT xst) (elemT yst))++semijoin :: Expr -> Expr -> L.JoinPredicate L.JoinExpr -> Expr+semijoin xs ys p = AppE2 xst (SemiJoin p) xs ys+  where+    xst = typeOf xs++antijoin :: Expr -> Expr -> L.JoinPredicate L.JoinExpr -> Expr+antijoin xs ys p = AppE2 xst (AntiJoin p) xs ys+  where+    xst = typeOf xs++---------------------------------------------------------------------------------------+-- Literal value constructors++unit :: Expr+unit = Lit unitT L.UnitV++int :: P.Int -> Expr+int i = Lit intT (L.IntV i)++bool :: P.Bool -> Expr+bool b = Lit boolT (L.BoolV b)++string :: P.String -> Expr+string s = Lit stringT (L.StringV s)++double :: P.Double -> Expr+double d = Lit doubleT (L.DoubleV d)++nil :: Type -> Expr+nil t = Lit t (L.ListV [])++list :: Type -> [Expr] -> Expr+list _ (e : es) = List.foldl' append (sng e) (P.map sng es)+list t []       = nil t++cons :: Expr -> Expr -> Expr+cons e1 e2 = append (sng e1) e2++---------------------------------------------------------------------------------------+-- Smart constructors for scalar unary operators++scalarUnOp :: L.ScalarUnOp -> Expr -> Expr+scalarUnOp op e =+    let t = typeOf e+    in case (op, t) of+           (L.SUNumOp _, DoubleT)                 -> UnOp t op e+           (L.SUBoolOp _, BoolT)                  -> UnOp BoolT op e+           (L.SUCastOp L.CastDouble, _) | isNum t -> UnOp DoubleT op e+           (L.SUTextOp L.SubString{}, StringT)    -> UnOp StringT op e+           (L.SUDateOp, _)                        -> $unimplemented+           (_, _)                                 -> P.error err+               where err = printf "CL.Primitives.scalarUnOp: %s" (P.show (op, t))++castDouble :: Expr -> Expr+castDouble = scalarUnOp (L.SUCastOp L.CastDouble)++not :: Expr -> Expr+not = scalarUnOp (L.SUBoolOp L.Not)++sin :: Expr -> Expr+sin = scalarUnOp (L.SUNumOp L.Sin)++cos :: Expr -> Expr+cos = scalarUnOp (L.SUNumOp L.Cos)++tan :: Expr -> Expr+tan = scalarUnOp (L.SUNumOp L.Tan)++asin :: Expr -> Expr+asin = scalarUnOp (L.SUNumOp L.ASin)++acos :: Expr -> Expr+acos = scalarUnOp (L.SUNumOp L.ACos)++atan :: Expr -> Expr+atan = scalarUnOp (L.SUNumOp L.ATan)++log :: Expr -> Expr+log = scalarUnOp (L.SUNumOp L.Log)++sqrt :: Expr -> Expr+sqrt = scalarUnOp (L.SUNumOp L.Sqrt)++exp :: Expr -> Expr+exp = scalarUnOp (L.SUNumOp L.Exp)++substring :: P.Integer -> P.Integer -> Expr -> Expr+substring f t = scalarUnOp (L.SUTextOp P.$ L.SubString f t)++---------------------------------------------------------------------------------------+-- Smart constructors for scalar binary operators++scalarBinOp :: L.ScalarBinOp -> Expr -> Expr -> Expr+scalarBinOp op e1 e2 =+    let t1 = typeOf e1+        t2 = typeOf e2+    in case (op, t1, t2) of+           (L.SBNumOp _, _, _) | t1 P.== t2 P.&& isNum t1 P.&& isNum t2 -> BinOp t1 op e1 e2+           (L.SBRelOp _, _, _) | t1 P.== t2                             -> BinOp BoolT op e1 e2+           (L.SBBoolOp _, BoolT, BoolT)                                 -> BinOp BoolT op e1 e2+           (L.SBStringOp L.Like, StringT, StringT)                      -> BinOp BoolT op e1 e2+           _                                                            -> P.error err+               where err = printf "CL.Primitives.scalarBinOp: %s" (P.show (op, t1, t2))++add :: Expr -> Expr -> Expr+add = scalarBinOp (L.SBNumOp L.Add)++sub :: Expr -> Expr -> Expr+sub = scalarBinOp (L.SBNumOp L.Sub)++mul :: Expr -> Expr -> Expr+mul = scalarBinOp (L.SBNumOp L.Mul)++div :: Expr -> Expr -> Expr+div = scalarBinOp (L.SBNumOp L.Div)++mod :: Expr -> Expr -> Expr+mod = scalarBinOp (L.SBNumOp L.Mod)++eq :: Expr -> Expr -> Expr+eq = scalarBinOp (L.SBRelOp L.Eq)++neq :: Expr -> Expr -> Expr+neq = scalarBinOp (L.SBRelOp L.NEq)++gt :: Expr -> Expr -> Expr+gt = scalarBinOp (L.SBRelOp L.Gt)++lt :: Expr -> Expr -> Expr+lt = scalarBinOp (L.SBRelOp L.Lt)++gte :: Expr -> Expr -> Expr+gte = scalarBinOp (L.SBRelOp L.GtE)++lte :: Expr -> Expr -> Expr+lte = scalarBinOp (L.SBRelOp L.LtE)++conj :: Expr -> Expr -> Expr+conj = scalarBinOp (L.SBBoolOp L.Conj)++disj :: Expr -> Expr -> Expr+disj = scalarBinOp (L.SBBoolOp L.Disj)++like :: Expr -> Expr -> Expr+like = scalarBinOp (L.SBStringOp L.Like)+
− src/Database/DSH/CSV.hs
@@ -1,42 +0,0 @@-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Database.DSH.CSV (csvImport) where--import Database.DSH.Internals--import qualified Data.Text as T-import Text.CSV--csvImport :: (Reify a) => FilePath -> Type [a] -> IO (Exp [a])-csvImport filepath csvType = do-  let rType = recordType csvType-  contents <- readFile filepath-  let csv1 = case parseCSV filepath contents of-               Left er -> error (show er)-               Right r -> filter (\l -> not (all null l) || length l > 1) (tail r)-  return (ListE (fmap (csvRecordToNorm rType) csv1))-  where csvError :: String -> a-        csvError s = error ("Error in '" ++ filepath ++ "': " ++ s)--        recordType :: Type [a] -> Type a-        recordType (ListT rType) = rType--        csvRecordToNorm :: Type a -> [String] -> Exp a-        csvRecordToNorm UnitT  [] = UnitE-        csvRecordToNorm t      [] = csvError ("When converting record '" ++ "[]" ++ "' to a value of type '" ++ show t ++ "'")-        csvRecordToNorm t1     [bs] = csvFieldToNorm t1 bs-        csvRecordToNorm (PairT (t1 :: Type b) (t2 :: Type c)) (bs : bss) = PairE (csvFieldToNorm t1 bs :: Exp b) (csvRecordToNorm t2 bss)-        csvRecordToNorm t           rs       = csvError ("When converting record '" ++ show rs ++ "' to a value of type '" ++ show t ++ "'")---        csvFieldToNorm :: Type a -> String -> Exp a-        csvFieldToNorm t s = case t of-          UnitT      -> UnitE-          BoolT      -> BoolE    (read s) -          CharT      -> CharE    (head s) -          IntegerT   -> IntegerE (read s) -          DoubleT    -> DoubleE  (read s) -          TextT      -> TextE    (T.pack s) -          _          -> er-          where er = csvError ("When converting CSV field'" ++ s ++ "' to a value of type '" ++ show t ++ "'")
+ src/Database/DSH/Common/Kure.hs view
@@ -0,0 +1,87 @@+module Database.DSH.Common.Kure+  ( -- * Debugging combinators+    prettyR+  , debug+  , debugPretty+  , debugMsg+  , debugOpt+  , debugPipeR+  , debugTrace+  , debugShow+  ) where++#ifdef DEBUGCOMP+import Debug.Trace+import Text.Printf+#endif++import Language.KURE+import Database.DSH.Common.Pretty+import Control.Arrow++--------------------------------------------------------------------------------+-- Simple debugging combinators++-- | Trace output of the value being rewritten; use for debugging only.+prettyR :: (Monad m, Pretty a) => String -> Rewrite c m a+#ifdef DEBUGCOMP+prettyR msg = acceptR (\a -> trace (msg ++ pp a) True)+#else+prettyR _ = idR+#endif++debug :: Pretty a => String -> a -> b -> b+#ifdef DEBUGCOMP+debug msg a b = trace ("\n" ++ msg ++ " =>\n" ++ pp a) b+#else+debug _ _ b = b+#endif++debugPretty :: (Pretty a, Monad m) => String -> a -> m ()+debugPretty msg a = debug msg a (return ())++debugMsg :: Monad m => String -> m ()+#ifdef DEBUGCOMP+debugMsg msg = trace msg $ return ()+#else+debugMsg _ = return ()+#endif++debugOpt :: Pretty e => String -> e -> Either String e -> e+debugOpt stage origExpr mExpr = +#ifdef DEBUGCOMP+    trace (showOrig origExpr)+    $ either (flip trace origExpr) (\e -> trace (showOpt e) e) mExpr++  where+    padSep :: String -> String+    padSep s = "\n" ++ s ++ " " ++ replicate (100 - length s) '=' ++ "\n"++    showOrig :: Pretty e => e -> String+    showOrig e = padSep (printf "Original Query (%s)" stage) ++ pp e ++ padSep ""++    showOpt :: Pretty e => e -> String+    showOpt e = padSep (printf "Optimized Query (%s)" stage) ++ pp e ++ padSep ""+#else+    either (const origExpr) id mExpr+#endif++debugPipeR :: (Monad m, Pretty a) => Rewrite c m a -> Rewrite c m a+debugPipeR r = prettyR "Before >>>>>>"+               >>> r+               >>> prettyR ">>>>>>> After"++debugTrace :: Monad m => String -> Rewrite c m a+#ifdef DEBUGCOMP+debugTrace msg = trace msg idR+#else+debugTrace _ = idR+#endif++debugShow :: (Monad m, Pretty a) => String -> Rewrite c m a+#ifdef DEBUGCOMP+debugShow msg = prettyR (msg ++ "\n")+#else+debugShow _ = idR+#endif+
+ src/Database/DSH/Common/Lang.hs view
@@ -0,0 +1,293 @@+{-# LANGUAGE GADTs           #-}+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Common.Lang where++import           Data.Aeson+import           Data.Aeson.TH+import qualified Data.List.NonEmpty           as N+import           Text.PrettyPrint.ANSI.Leijen+import           Text.Printf++import           Database.DSH.Common.Type+import           Database.DSH.Impossible++import           Database.DSH.Common.Nat++instance ToJSON a => ToJSON (N.NonEmpty a) where+    toJSON (n N.:| nl) = toJSON (n, nl)++instance FromJSON a => FromJSON (N.NonEmpty a) where+    parseJSON doc = parseJSON doc >>= \(n, nl) -> return $ n N.:| nl++-----------------------------------------------------------------------------+-- Common types for backend expressions++-- | Basic values in both FKL and NKL.+data Val where+    ListV   :: [Val] -> Val+    IntV    :: Int -> Val+    BoolV   :: Bool -> Val+    StringV :: String -> Val+    DoubleV :: Double -> Val+    TupleV  :: [Val] -> Val+    UnitV   :: Val+    deriving (Eq, Ord, Show)++newtype ColName = ColName String deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''ColName)++-- | Typed table columns+type Column = (ColName, Type)++-- | Table keys+newtype Key = Key [ColName] deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''Key)++-- | Is the table guaranteed to be not empty?+data Emptiness = NonEmpty+               | PossiblyEmpty+               deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''Emptiness)++-- | Catalog information hints that users may give to DSH+data TableHints = TableHints+    { keysHint     :: [Key]+    , nonEmptyHint :: Emptiness+    } deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''TableHints)++-- | Identifiers+type Ident = String+++-----------------------------------------------------------------------------+-- Scalar operators++data UnCastOp = CastDouble+                deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''UnCastOp)++data UnBoolOp = Not+                deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''UnBoolOp)++data UnNumOp = Sin+             | Cos+             | Tan+             | ASin+             | ACos+             | ATan+             | Sqrt+             | Exp+             | Log+             deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''UnNumOp)++data UnTextOp = SubString Integer Integer+                deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''UnTextOp)++data ScalarUnOp = SUNumOp UnNumOp+                | SUBoolOp UnBoolOp+                | SUCastOp UnCastOp+                | SUTextOp UnTextOp+                | SUDateOp+                deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''ScalarUnOp)++data BinNumOp = Add+              | Sub+              | Div+              | Mul+              | Mod+              deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''BinNumOp)++data BinRelOp = Eq+              | Gt+              | GtE+              | Lt+              | LtE+              | NEq+              deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''BinRelOp)++data BinBoolOp = Conj+               | Disj+                deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''BinBoolOp)++data BinStringOp = Like+                   deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''BinStringOp)++-- FIXME this would be a good fit for PatternSynonyms+data ScalarBinOp = SBNumOp BinNumOp+                 | SBRelOp BinRelOp+                 | SBBoolOp BinBoolOp+                 | SBStringOp BinStringOp+                 deriving (Show, Eq, Ord)++$(deriveJSON defaultOptions ''ScalarBinOp)+++-----------------------------------------------------------------------------+-- Join operator arguments: limited expressions that can be used on joins++data JoinConjunct e = JoinConjunct e BinRelOp e+                    deriving (Show, Eq, Ord)++instance ToJSON e => ToJSON (JoinConjunct e) where+    toJSON (JoinConjunct e1 op e2) = toJSON (e1, op, e2)++instance FromJSON e => FromJSON (JoinConjunct e) where+    parseJSON d = parseJSON d >>= \(e1, op, e2) -> return $ JoinConjunct e1 op e2++newtype JoinPredicate e = JoinPred (N.NonEmpty (JoinConjunct e))+                        deriving (Show, Eq, Ord)++instance ToJSON e => ToJSON (JoinPredicate e) where+    toJSON (JoinPred conjs) = toJSON conjs++instance FromJSON e => FromJSON (JoinPredicate e) where+    parseJSON d = parseJSON d >>= \conjs -> return $ JoinPred conjs++singlePred :: JoinConjunct e -> JoinPredicate e+singlePred c = JoinPred $ c N.:| []++data JoinBinOp = JBNumOp BinNumOp+               | JBStringOp BinStringOp+               deriving (Show, Eq, Ord)++data JoinUnOp = JUNumOp UnNumOp+              | JUCastOp UnCastOp+              | JUTextOp UnTextOp+              deriving (Show, Eq, Ord)++data JoinExpr = JBinOp Type JoinBinOp JoinExpr JoinExpr+              | JUnOp Type JoinUnOp JoinExpr+              | JTupElem Type TupleIndex JoinExpr+              | JLit Type Val+              | JInput Type+              deriving (Show, Eq)++instance Typed JoinExpr where+    typeOf (JBinOp t _ _ _) = t+    typeOf (JUnOp t _ _)    = t+    typeOf (JTupElem t _ _) = t+    typeOf (JLit t _)       = t+    typeOf (JInput t)       = t++-----------------------------------------------------------------------------+-- Pretty-printing of stuff++parenthize :: JoinExpr -> Doc+parenthize e =+    case e of+        JBinOp _ _ _ _ -> parens $ pretty e+        JUnOp _ _ _    -> parens $ pretty e+        JTupElem _ _ _ -> pretty e+        JLit  _ _      -> pretty e+        JInput _       -> pretty e++instance Pretty Val where+    pretty (ListV xs)    = list $ map pretty xs+    pretty (IntV i)      = int i+    pretty (BoolV b)     = bool b+    pretty (StringV s)   = dquotes $ string s+    pretty (DoubleV d)   = double d+    pretty UnitV         = text "()"+    pretty (TupleV vs)   = tupled $ map pretty vs++instance Pretty BinRelOp where+    pretty Eq  = text "=="+    pretty Gt  = text ">"+    pretty Lt  = text "<"+    pretty GtE = text ">="+    pretty LtE = text "<="+    pretty NEq = text "/="++instance Pretty BinStringOp where+    pretty Like = text "LIKE"++instance Pretty BinNumOp where+    pretty Add = text "+"+    pretty Sub = text "-"+    pretty Div = text "/"+    pretty Mul = text "*"+    pretty Mod = text "%"++instance Pretty BinBoolOp where+    pretty Conj = text "&&"+    pretty Disj = text "||"++instance Pretty UnNumOp where+    pretty Sin  = text "sin"+    pretty Cos  = text "cos"+    pretty Tan  = text "tan"+    pretty Sqrt = text "sqrt"+    pretty Exp  = text "exp"+    pretty Log  = text "log"+    pretty ASin = text "asin"+    pretty ACos = text "acos"+    pretty ATan = text "atan"++instance Pretty UnCastOp where+    pretty CastDouble = text "double"++instance Pretty JoinUnOp where+    pretty (JUNumOp o)  = pretty o+    pretty (JUCastOp o) = pretty o+    pretty (JUTextOp o) = pretty o++instance Pretty JoinBinOp where+    pretty (JBNumOp o)    = pretty o+    pretty (JBStringOp o) = pretty o++instance Pretty JoinExpr where+    pretty (JBinOp _ op e1 e2) = parenthize e1 <+> pretty op <+> parenthize e2+    pretty (JUnOp _ op e)      = pretty op <+> parenthize e+    pretty (JLit _ v)          = pretty v+    pretty (JInput _)          = text "I"+    pretty (JTupElem _ i e1)   =+        parenthize e1 <> dot <> int (tupleIndex i)++instance Pretty e => Pretty (JoinConjunct e) where+    pretty (JoinConjunct e1 op e2) = parens $ pretty e1 <+> pretty op <+> pretty e2++instance Pretty e => Pretty (JoinPredicate e) where+    pretty (JoinPred ps) = list $ map pretty $ N.toList ps+++instance Pretty ScalarBinOp where+    pretty (SBNumOp o)    = pretty o+    pretty (SBRelOp o)    = pretty o+    pretty (SBBoolOp o)   = pretty o+    pretty (SBStringOp o) = pretty o++instance Pretty UnBoolOp where+    pretty Not = text "not"++instance Pretty ScalarUnOp where+    pretty (SUNumOp op)  = pretty op+    pretty (SUBoolOp op) = pretty op+    pretty (SUCastOp op) = pretty op+    pretty SUDateOp      = $unimplemented+    pretty (SUTextOp op) = pretty op++instance Pretty UnTextOp where+    pretty (SubString f t) = text $ printf "subString_%d,%d" f t
+ src/Database/DSH/Common/Nat.hs view
@@ -0,0 +1,23 @@+module Database.DSH.Common.Nat where++import Control.Exception++-- | Natural numbers that encode lifting levels+data Nat = Zero | Succ Nat deriving (Show, Eq)++intFromNat :: Nat -> Int+intFromNat Zero     = 0+intFromNat (Succ n) = 1 + intFromNat n++-- | Indexes of tuple fields+data TupleIndex = First | Next TupleIndex deriving (Show, Eq)++tupleIndex :: TupleIndex -> Int+tupleIndex First    = 1+tupleIndex (Next f) = 1 + tupleIndex f++intIndex :: Int -> TupleIndex +intIndex i = assert (i >= 1) $+    if i > 1+    then Next $ (intIndex $ i - 1)+    else First
+ src/Database/DSH/Common/Pretty.hs view
@@ -0,0 +1,9 @@+module Database.DSH.Common.Pretty +  ( pp+  , Pretty, pretty+  ) where++import Text.PrettyPrint.ANSI.Leijen++pp :: Pretty a => a -> String+pp a = (displayS $ renderPretty 0.9 120 $ pretty a) ""
+ src/Database/DSH/Common/QueryPlan.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE TemplateHaskell #-}++-- | A QueryPlan describes the computation of the top-level query+-- result from algebraic plans over some algebra and describes how the+-- result's structure is encoded by the individual queries.+module Database.DSH.Common.QueryPlan where++import           Data.Aeson.TH++import           Database.Algebra.Dag+import           Database.Algebra.Dag.Common++import           Database.DSH.VL.Vector++-- | A Layout describes the tuple structure of values encoded by+-- one particular query from a bundle.+data Layout q = LCol Int+              | LNest q (Layout q)+              | LTuple [Layout q]+              deriving (Show, Read)++-- | A Shape describes the structure of the result produced by a+-- bundle of nested queries. 'q' is the type of individual vectors,+-- e.g. plan entry nodes or rendered database code. On the top level+-- we distinguish between a single value and a proper vector with more+-- than one element.+data Shape q = VShape q (Layout q)  -- | A regular vector shape+             | SShape q (Layout q)  -- | A shape for a singleton vector+             deriving (Show, Read)++$(deriveJSON defaultOptions ''Layout)+$(deriveJSON defaultOptions ''Shape)++-- | Extract all plan root nodes stored in the layout+layoutNodes :: DagVector v => Layout v -> [AlgNode]+layoutNodes (LCol _)      = []+layoutNodes (LNest v lyt) = vectorNodes v ++ layoutNodes lyt+layoutNodes (LTuple lyts) = concatMap layoutNodes lyts++-- | Extract all plan root nodes stored in the shape+shapeNodes :: DagVector v => Shape v -> [AlgNode]+shapeNodes (VShape v lyt) = vectorNodes v ++ layoutNodes lyt+shapeNodes (SShape v lyt) = vectorNodes v ++ layoutNodes lyt++-- | Replace a node in a top shape with another node.+updateShape :: DagVector v => AlgNode -> AlgNode -> Shape v -> Shape v+updateShape old new shape =+    case shape of+        VShape dbv lyt -> VShape (updateVector old new dbv) (updateLayout lyt)+        SShape dbv lyt -> SShape (updateVector old new dbv) (updateLayout lyt)++  where+    updateLayout (LNest dbv lyt) = LNest (updateVector old new dbv) (updateLayout lyt)+    updateLayout (LTuple lyts)   = LTuple (map updateLayout lyts)+    updateLayout l               = l++columnsInLayout :: Layout q -> Int+columnsInLayout (LCol _)      = 1+columnsInLayout (LNest _ _)   = 0+columnsInLayout (LTuple lyts) = sum $ map columnsInLayout lyts++isOuterMost :: AlgNode -> Shape NDVec -> Bool+isOuterMost n (VShape (ADVec n' _) _) = n == n'+isOuterMost n (SShape (ADVec n' _) _) = n == n'++-- | A query plan consists of a DAG over some algebra and information about the+-- shape of the query.+data QueryPlan a v =+  QueryPlan { queryDag   :: AlgebraDag a+            , queryShape :: Shape v+            , queryTags  :: NodeMap [Tag]+            }++-- | Construct a query plan from the operator map and the description+-- of the result shape.+mkQueryPlan :: (Operator a, DagVector v) +            => AlgebraDag a +            -> Shape v +            -> NodeMap [Tag] +            -> QueryPlan a v+mkQueryPlan dag shape tagMap =+  QueryPlan { queryDag   = addRootNodes dag (shapeNodes shape)+            , queryShape = shape+            , queryTags  = tagMap +            }
+ src/Database/DSH/Common/RewriteM.hs view
@@ -0,0 +1,132 @@+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE InstanceSigs         #-}++module Database.DSH.Common.RewriteM+    ( RewriteM+    , RewriteStateM+    , runRewriteM+    , freshName+    , freshNameS+    , put+    , get+    , modify+    , stateful+    , liftstate+    ) where++import Control.Applicative+import Control.Monad+       +import Language.KURE++import Database.DSH.Common.Lang++--------------------------------------------------------------------------------+-- | The rewriting monad. Currently, it only provides fresh names+-- FIXME Figure out how to define a MonadCatch instance and use StateT s RewriteM+newtype RewriteM s a = RewriteM { compM :: s -> (s, Either String a) }++-- | A variant of RewriteM which adds extra state to the+-- name-generating counter.+type RewriteStateM s = RewriteM (Int, s)++runRewriteM :: RewriteM Int a -> Either String a+runRewriteM m = snd (compM m 0)++runRewriteM' :: s -> RewriteM s a -> (s, Either String a)+runRewriteM' s m = compM m s++instance Monad (RewriteM s) where+  return = returnM+  (>>=)  = bindM+  fail   = failM+  +returnM :: a -> RewriteM s a+returnM a = RewriteM (\n -> (n, Right a))+{-# INLINE returnM #-}+  +bindM :: RewriteM s a -> (a -> RewriteM s b) -> RewriteM s b+bindM (RewriteM f) gg = RewriteM $ \ n -> case f n of+                                    (n', Left msg) -> (n', Left msg)+                                    (n', Right a)  -> compM (gg a) n'+{-# INLINE bindM #-}                                    +                                    +failM :: String -> RewriteM s a+failM msg = RewriteM (\n -> (n, Left msg))+{-# INLINE failM #-}++instance MonadCatch (RewriteM s) where+    catchM = catchRewriteM++catchRewriteM :: RewriteM s a -> (String -> RewriteM s a) -> RewriteM s a+catchRewriteM (RewriteM st) f = RewriteM $ \ n -> case st n of+                                        (n', Left msg) -> compM (f msg) n'+                                        (n', Right a)  -> (n', Right a)+{-# INLINE catchRewriteM #-}                                        +++instance Functor (RewriteM s) where+  fmap = liftM++instance Applicative (RewriteM s) where+  pure  = return+  (<*>) = ap++suggestName :: RewriteM Int Ident+suggestName = RewriteM (\n -> ((n+1), Right ("v" ++ show n)))++-- | Generate a fresh name, taking the list of in-scope names as parameter. We+-- assume that every name is bound. Therefore, a name that is not bound is+-- assumed to be fresh.+freshName :: [Ident] -> RewriteM Int Ident+freshName vs = do v <- suggestName+                  if v `elem` vs+                    then freshName vs+                    else return v+                    +suggestName' :: RewriteStateM s Ident+suggestName' = RewriteM (\(n, s) -> ((n+1, s), Right ("v" ++ show n)))++freshNameS :: [Ident] -> RewriteStateM s Ident+freshNameS vs = do v <- suggestName'+                   if v `elem` vs+                     then freshNameS vs+                     else return v+                     +get :: RewriteStateM s s+get = RewriteM $ \(i, s) -> ((i, s), Right s)+{-# INLINE get #-}++put :: s -> RewriteStateM s ()+put s = RewriteM $ \(i, _) -> ((i, s), Right ())+{-# INLINE put #-}++modify :: (s -> s) -> RewriteStateM s ()+modify f = RewriteM $ \(i, s) -> ((i, f s), Right ())+{-# INLINE modify #-}++stateful :: s -> RewriteStateM s a -> RewriteM Int (s, a)+stateful s ma = RewriteM $ \i -> +               case runRewriteM' (i, s) ma of+                   ((i', _), Left msg) -> (i', Left msg)+                   ((i', s'), Right a) -> (i', Right (s', a))+                   +liftstate :: RewriteM Int a -> RewriteStateM s a+liftstate ma = RewriteM $ \(i, s) -> let (i', a) = runRewriteM' i ma+                                  in ((i', s), a)+                   ++-- runRewriteM' (i, s) (ma :: RewriteM (Int, s) a) :: ((i', s'), ++{-+type FooM s = StateT s RewriteM++-- automatic due to StateT+-- instance Monad ...++instance MonadCatch (FooM s) where+    ma `catchM` f = StateT $ \s ->+                        let (ka, s') = runStateT ma s+                        in runRewriteM (return . return) undefined ka+-}                        
+ src/Database/DSH/Common/Type.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE TypeSynonymInstances   #-}+{-# LANGUAGE MultiParamTypeClasses  #-}+{-# LANGUAGE TemplateHaskell        #-}++module Database.DSH.Common.Type +    ( isNum+    , isList+    , elemT+    , tupleElemT+    , tupleElemTypes+    , fstT+    , sndT+    , listDepth+    , extractShape+    , unliftTypeN+    , unliftType+    , liftType+    , liftTypeN+    , Type(..)+    , intT+    , boolT+    , unitT+    , stringT+    , doubleT+    , listT+    , pairT+    , Typed (..)+    ) where++import Text.PrettyPrint.ANSI.Leijen++import Database.DSH.Impossible+import Database.DSH.Common.Pretty+import Database.DSH.Common.Nat+  +instance Pretty Type where +    pretty IntT          = text "Int"+    pretty BoolT         = text "Bool"+    pretty DoubleT       = text "Double"+    pretty StringT       = text "String"+    pretty UnitT         = text "()"+    pretty (ListT t)     = brackets $ pretty t+    pretty (TupleT ts)   = tupled $ map pretty ts++-- | We use the following type language to type the various+-- intermediate languages.+data Type  = IntT +           | BoolT +           | DoubleT+           | StringT +           | UnitT +           | ListT Type+           | TupleT [Type]+           deriving (Show, Eq, Ord)++isNum :: Type -> Bool+isNum IntT        = True+isNum DoubleT     = True+isNum BoolT       = False+isNum StringT     = False+isNum UnitT       = False+isNum (ListT _)   = False+isNum (TupleT _)  = False+      +intT :: Type+intT = IntT++stringT :: Type+stringT = StringT++doubleT :: Type+doubleT = DoubleT++boolT :: Type+boolT = BoolT++unitT :: Type+unitT = UnitT++listT :: Type -> Type+listT = ListT++pairT :: Type -> Type -> Type+pairT t1 t2 = TupleT [t1, t2]++isList :: Type -> Bool+isList (ListT _) = True+isList _        = False++elemT :: Type -> Type+elemT (ListT t) = t+elemT _        = error "elemT: argument is not a list type"++tupleElemT :: Type -> TupleIndex -> Type+tupleElemT (TupleT ts) f = ts !! (tupleIndex f - 1)+tupleElemT _           _ = $impossible++tupleElemTypes :: Type -> [Type]+tupleElemTypes (TupleT ts) = ts+tupleElemTypes _           = $impossible++listDepth :: Type -> Int+listDepth (ListT t1) = 1 + listDepth t1+listDepth _          = 0++fstT :: Type -> Type+fstT (TupleT [t1, _]) = t1+fstT _                = error "Type is not a pair type"++sndT :: Type -> Type+sndT (TupleT [_, t2]) = t2+sndT _                = error "Type is not a pair type"++extractShape :: Type -> Type -> Type+extractShape (ListT t1) = \x -> listT $ extractShape t1 x+extractShape _          = \x -> x++liftTypeN :: Nat -> Type -> Type+liftTypeN Zero t     = t+liftTypeN (Succ n) t = liftTypeN n $ liftType t++liftType :: Type -> Type+liftType t = listT t ++unliftTypeN :: Nat -> Type -> Type+unliftTypeN Zero t     = t+unliftTypeN (Succ n) t = unliftTypeN n $ unliftType t++unliftType :: Type -> Type+unliftType (ListT t1) = t1+unliftType t          = error $ "Type: " ++ pp t ++ " cannot be unlifted."++class Typed a where+  typeOf :: a -> Type
− src/Database/DSH/Compile.hs
@@ -1,275 +0,0 @@-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell     #-}--module Database.DSH.Compile where--import Database.DSH.Internals-import Database.DSH.Impossible--import Database.Pathfinder--import qualified Data.Array as A-import qualified Data.List as L-import Data.Maybe (fromJust, isNothing, isJust, fromMaybe)-import Data.List (sortBy)-import Data.Function-import Control.Arrow-import Control.Monad.Reader-import Control.Exception (evaluate)--import qualified Text.XML.HaXml as X-import Text.XML.HaXml (Content(..), AttValue(..), tag, deep, children, xmlParse, Document(..))--import Database.HDBC-import qualified Data.Text as T-import qualified Data.Text.Encoding as T---- | Wrapper type with phantom type for algebraic plan--- The type variable represents the type of the result of the plan-newtype AlgebraXML a = Algebra String---- | Wrapper type with phantom type for SQL plan--- The type variable represents the type of the result of the plan-newtype SQLXML a = SQL String- deriving Show---- | Type representing a query bundle, the type variable represents the type--- of the result of the query bundle. A bundle consists of pair of numbered queries.--- Each query consists of the query itself, a schema explaining its types.--- If the query is a nested value in the result of another query the optional attribute--- represents (queryID, columnID). The queryId refers to the number of the query in the bundle--- the columnID refers -newtype QueryBundle a = Bundle [(Int, (String, SchemaInfo, Maybe (Int, Int)))]---- | Description of a table. The field iterN contains the name of the iter column--- the items field contains a list of item column names and their position within the result.-data SchemaInfo = SchemaInfo {iterN :: String, items :: [(String, Int)]}---- | Description of result data of a query. The field iterR contains the column number of--- the iter column. resCols contains a for all items columns their column number in the result.-data ResultInfo = ResultInfo {iterR :: Int, resCols :: [(String, Int)]}- deriving Show---- | Translate the algebraic plan to SQL and then execute it using the provided --- DB connection. If debug is switchd on the SQL code is written to a file --- named query.sql-executePlan :: forall a. forall conn. (Reify a, IConnection conn) => conn -> AlgebraXML a -> IO (Exp a)-executePlan c p = do-                        sql@(SQL _s) <- algToSQL p-                        runSQL c $ extractSQL sql--algToAlg :: AlgebraXML a -> IO (AlgebraXML a)-algToAlg (Algebra s) = do r <- pathfinder s [] OutputXml-                          case r of-                            (Right sql) -> return $ Algebra sql-                            (Left err)  -> error $ "Pathfinder compilation for input: \n"-                                                     ++ s ++ "\n failed with error: \n"-                                                     ++ err---- | Translate an algebraic plan into SQL code using Pathfinder-algToSQL :: AlgebraXML a -> IO (SQLXML a)-algToSQL (Algebra s) = do r <- pathfinder s [] OutputSql-                          case r of-                             (Right sql) -> return $ SQL sql-                             (Left err) -> error $ "Pathfinder compilation for input: \n"-                                                     ++ s ++ "\n failed with error: \n"-                                                     ++ err---- | Extract the SQL queries from the XML structure generated by pathfinder-extractSQL :: SQLXML a -> QueryBundle a-extractSQL (SQL q) = let (Document _ _ r _) = xmlParse "query" q-                      in Bundle $ map extractQuery $ (deep $ tag "query_plan") (CElem r $impossible)-    where-        extractQuery c@(CElem (X.Elem n attrs cs) _) = let qId = maybe ($impossible) attrToInt (lookup (X.N "id") attrs)-                                                           rId = fmap attrToInt $ lookup (X.N "idref") attrs-                                                           cId = fmap attrToInt $ lookup (X.N "colref") attrs-                                                           ref = liftM2 (,) rId cId-                                                           query = extractCData $  head $ concatMap children $ deep (tag "query") c-                                                           schema = toSchemeInf $ map process $ concatMap (deep (tag "column")) $ deep (tag "schema") c-                                                        in (qId, (query, schema, ref))-        extractQuery _ = $impossible-        attrToInt :: AttValue -> Int-        attrToInt (AttValue [Left i]) = read i-        attrToInt _ = $impossible-        attrToString :: AttValue -> String-        attrToString (AttValue [Left i]) = i-        attrToString _ = $impossible-        extractCData :: Content i -> String-        extractCData (CString _ d _) = d-        extractCData _ = $impossible-        toSchemeInf :: [(String, Maybe Int)] -> SchemaInfo-        toSchemeInf results = let iterName = fst $ head $ filter (\(_, p) -> isNothing p) results-                                  cols = map (second fromJust) $ filter (\(_, p) -> isJust p) results-                               in SchemaInfo iterName cols-        process :: Content i -> (String, Maybe Int)-        process (CElem (X.Elem _ attrs _) _) = let name = fromJust $ fmap attrToString $ lookup (X.N "name") attrs-                                                   pos = fmap attrToInt $ lookup (X.N "position") attrs-                                                in (name, pos)-        process _ = $impossible---- | Execute the given SQL queries and assemble the results into one structure-runSQL :: forall a. forall conn. (Reify a, IConnection conn) => conn -> QueryBundle a -> IO (Exp a)-runSQL c (Bundle queries) = do-                             results <- mapM (runQuery c) queries-                             let (queryMap, valueMap) = foldr buildRefMap ([],[]) results-                             let ty = reify (undefined :: a)-                             let results' = runReader (processResults 0 ty) (queryMap, valueMap)-                             case ty of-                                 (ListT _) -> return $ fromMaybe (ListE []) (lookup 1 results')-                                 _         -> return $ fromJust (lookup 1 results')---- | Type of the environment under which we reconstruct ordinary haskell data from the query result.--- The first component of the reader monad contains a mapping from (queryNumber, columnNumber) to --- the number of a nested query. The second component is a tuple consisting of query number associated--- with a pair of the raw result data partitioned by iter, and a description of this result data.-type QueryR = Reader ([((Int, Int), Int)] ,[(Int, ([(Int, [[SqlValue]])], ResultInfo))])---- | Retrieve the data asociated with query i.-getResults :: Int -> QueryR [(Int, [[SqlValue]])]-getResults i = do-                env <- ask-                return $ case lookup i $ snd env of-                              Just x -> fst x-                              Nothing -> $impossible---- | Get the position of item i of query q-getColResPos :: Int -> Int -> QueryR Int-getColResPos q i = do-                    env <- ask-                    return $ case lookup q $ snd env of-                                Just (_, ResultInfo _ x) -> snd (x !! i)-                                Nothing -> $impossible---- | Get the id of the query that is nested in column c of query q.-findQuery :: (Int, Int) -> QueryR Int-findQuery (q, c) = do-                    env <- ask-                    return $ fromMaybe (error $ show $ fst env) $ lookup (q, c + 1) $ fst env---- | Reconstruct the haskell value out of the result of query i with type ty.-processResults :: Int -> Type a -> QueryR [(Int, Exp a)]-processResults i (ListT t1) = do-                                v <- getResults i-                                mapM (\(it, vals) -> do-                                                        v1 <- processResults' i 0 vals t1-                                                        return (it, ListE v1)) v-processResults i t = do-                        v <- getResults i-                        mapM (\(it, vals) -> do-                                              v1 <- processResults' i 0 vals t-                                              return (it, head v1)) v--nrColsInType :: Type a -> Int-nrColsInType UnitT = 1-nrColsInType BoolT = 1-nrColsInType CharT = 1-nrColsInType IntegerT = 1-nrColsInType DoubleT = 1-nrColsInType TextT = 1-nrColsInType (PairT t1 t2) = nrColsInType t1 + nrColsInType t2-nrColsInType (ListT _) = 1-nrColsInType (ArrowT _ _) = $impossible---- | Reconstruct the values for column c of query q out of the rawData vals with type t.-processResults' :: Int -> Int -> [[SqlValue]] -> Type a -> QueryR [Exp a]-processResults' _ _ vals UnitT = return $ map (\_ -> UnitE) vals-processResults' q c vals (PairT t1 t2) = do-                                            v1s <- processResults' q c vals t1-                                            v2s <- processResults' q (c + nrColsInType t1) vals t2-                                            return (zipWith PairE v1s v2s)-processResults' q c vals t@(ListT _) = do-                                        nestQ <- findQuery (q, c)-                                        list <- processResults nestQ t-                                        i <- getColResPos q c-                                        let (maxV, vals') = foldr (\v (m,vs) -> let v' = sqlValueToInt (v !! i)-                                                                                in (m `max` v', v':vs))  (1,[]) vals-                                        let maxI = if null list-                                                    then 1-                                                    else fst $ L.maximumBy (compare `on` fst) list-                                        let lA = A.accumArray ($impossible) Nothing (1,maxI `max` maxV) [] A.// map (second Just) list-                                        return $ map (\val -> fromMaybe (ListE []) (lA A.! val)) vals'-processResults' _ _ _ (ArrowT _ _) = $impossible-processResults' q c vals t = do-                                    i <- getColResPos q c-                                    return $ map (\val -> convert (val !! i) t) vals--sqlValueToInt :: SqlValue -> Int-sqlValueToInt (SqlInteger i) = fromIntegral i-sqlValueToInt _ = $impossible--convert :: SqlValue -> Type a -> Exp a-convert SqlNull         UnitT    = UnitE-convert (SqlInteger i)  IntegerT = IntegerE i-convert (SqlInt32 i)    IntegerT = IntegerE $ fromIntegral i-convert (SqlInt64 i)    IntegerT = IntegerE $ fromIntegral i-convert (SqlWord32 i)   IntegerT = IntegerE $ fromIntegral i-convert (SqlWord64 i)   IntegerT = IntegerE $ fromIntegral i-convert (SqlDouble d)  DoubleT  = DoubleE d-convert (SqlRational d) DoubleT = DoubleE $ fromRational d-convert (SqlInteger d)  DoubleT = DoubleE $ fromIntegral d-convert (SqlInt32 d)    DoubleT = DoubleE $ fromIntegral d-convert (SqlInt64 d)    DoubleT = DoubleE $ fromIntegral d-convert (SqlWord32 d)   DoubleT = DoubleE $ fromIntegral d-convert (SqlWord64 d)   DoubleT = DoubleE $ fromIntegral d-convert (SqlBool b) BoolT       = BoolE b-convert (SqlInteger i) BoolT    = BoolE (i /= 0)-convert (SqlInt32 i)   BoolT    = BoolE (i /= 0)-convert (SqlInt64 i)   BoolT    = BoolE (i /= 0)-convert (SqlWord32 i)  BoolT    = BoolE (i /= 0)-convert (SqlWord64 i)  BoolT    = BoolE (i /= 0) -convert (SqlChar c) CharT       = CharE c-convert (SqlString (c:_)) CharT = CharE c-convert (SqlByteString c) CharT = CharE (head $ T.unpack $ T.decodeUtf8 c)-convert (SqlString t) TextT     = TextE (T.pack t) -convert (SqlByteString s) TextT = TextE (T.decodeUtf8 s)-convert sql                 _   = error $ "Unsupported SqlValue: "  ++ show sql---- | Partition by iter column--- The first argument is the position of the iter column.--- The second argument the raw data--- It returns a list of pairs (iterVal, rawdata within iter) -partByIter :: Int -> [[SqlValue]] -> [(Int, [[SqlValue]])]-partByIter n (v:vs) = let i = getIter n v-                          (vi, vr) = span (\v' -> i == getIter n v') vs-                       in (i, v:vi) : partByIter n vr-       where-           getIter :: Int -> [SqlValue] -> Int-           getIter n' vals = fromSql (vals !! n') :: Int-partByIter _ [] = []----- | Execute the given query plan bundle, over the provided connection.--- It returns the raw data for each query along with a description on how to reconstruct --- ordinary haskell data-runQuery :: IConnection conn => conn -> (Int, (String, SchemaInfo, Maybe (Int, Int))) -> IO (Int, ([(Int, [[SqlValue]])], ResultInfo, Maybe (Int, Int)))-runQuery c (qId, (query, schema, ref)) = do-                                                sth <- prepare c query-                                                _ <- execute sth []-                                                res <- dshFetchAllRowsStrict sth-                                                resDescr <- describeResult sth-                                                let ri = schemeToResult schema resDescr-                                                let res' = partByIter (iterR ri) res -                                                return (qId, (res', ri, ref))--dshFetchAllRowsStrict :: Statement -> IO [[SqlValue]]-dshFetchAllRowsStrict stmt = go []-  where-  go :: [[SqlValue]] -> IO [[SqlValue]]-  go acc = do  mRow <- fetchRow stmt-               case mRow of-                 Nothing   -> return (reverse acc)-                 Just row  -> do mapM_ evaluate row-                                 go (row : acc)---- | Transform algebraic plan scheme info into resultinfo-schemeToResult :: SchemaInfo -> [(String, SqlColDesc)] -> ResultInfo-schemeToResult (SchemaInfo itN cols) resDescr = let ordCols = sortBy (\(_, c1) (_, c2) -> compare c1 c2) cols-                                                    resColumns = flip zip [0..] $ map (\(c, _) -> takeWhile (/= '_') c) resDescr-                                                    itC = fromJust $ lookup itN resColumns-                                                 in ResultInfo itC $ map (\(n, _) -> (n, fromJust $ lookup n resColumns)) ordCols---- | -buildRefMap :: (Int, ([(Int, [[SqlValue]])], ResultInfo, Maybe (Int, Int))) -> ([((Int, Int), Int)] ,[(Int, ([(Int, [[SqlValue]])], ResultInfo))]) -> ([((Int, Int), Int)] ,[(Int, ([(Int, [[SqlValue]])], ResultInfo))])-buildRefMap (q, (r, ri, Just (t, c))) (qm, rm) = (((t, c), q):qm, (q, (r, ri)):rm)-buildRefMap (q, (r, ri, _)) (qm, rm) = (qm, (q, (r, ri)):rm)
src/Database/DSH/Compiler.hs view
@@ -1,353 +1,132 @@-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell     #-}---- | DSH compiler module exposes the function fromQ that can be used to--- execute DSH programs on a database. It transform the DSH program into--- FerryCore which is then translated into SQL (through a table algebra). The SQL--- code is executed on the database and then processed to form a Haskell value.--module Database.DSH.Compiler (fromQ, debugPlan, debugCore, debugPlanOpt, debugSQL, debugCoreDot) where--import Database.DSH.Internals as D-import Database.DSH.Impossible-import Database.DSH.CSV--import Database.DSH.Compile as C--import Database.Ferry.SyntaxTyped  as F-import Database.Ferry.Compiler--import qualified Data.Map as M-import Data.Char-import Database.HDBC--import Control.Monad.State-import Control.Applicative--import Data.Text (unpack)--import Data.List (nub)-import qualified Data.List as L--{--N monad, version of the state monad that can provide fresh variable names.--}-type N conn = StateT (conn, Int, M.Map String [(String,FType -> Bool)]) IO---- | Provide a fresh identifier name during compilation-freshVar :: N conn Int-freshVar = do-             (c, i, env) <- get-             put (c, i + 1, env)-             return i---- | Get from the state the connection to the database                -getConnection :: IConnection conn => N conn conn-getConnection = do-                 (c, _, _) <- get-                 return c---- | Lookup information that describes a table. If the information is --- not present in the state then the connection is used to retrieve the--- table information from the Database.-tableInfo :: IConnection conn => String -> N conn [(String,FType -> Bool)]-tableInfo t = do-               (c, i, env) <- get-               case M.lookup t env of-                     Nothing -> do-                                 inf <- lift $ getTableInfo c t-                                 put (c, i, M.insert t inf env)-                                 return inf                                      -                     Just v -> return v---- | Turn a given integer into a variable beginning with prefix "__fv_"                    -prefixVar :: Int -> String-prefixVar = (++) "__fv_" . show-     --- | Execute the transformation computation. During--- compilation table information can be retrieved from--- the database, therefor the result is wrapped in the IO--- Monad.      -runN :: IConnection conn => conn -> N conn a -> IO a-runN c  = liftM fst . flip runStateT (c, 1, M.empty)-            --- * Convert DB queries into Haskell values---- | Execute the query on the database-fromQ :: (QA a, IConnection conn) => conn -> Q a -> IO a-fromQ c (Q e) = fmap frExp (evaluate c e)---- | Convert the query into unoptimised algebraic plan-debugPlan :: (IConnection conn,Reify a) => conn -> Exp a -> IO String-debugPlan = doCompile---- | Convert the query into optimised algebraic plan-debugPlanOpt :: (IConnection conn,Reify a) => conn -> Exp a -> IO String-debugPlanOpt q c = do-                    p <- doCompile q c-                    (C.Algebra r) <- algToAlg (C.Algebra p :: AlgebraXML a)-                    return r--debugCore :: (IConnection conn,Reify a) => conn -> Exp a -> IO String-debugCore c a = do core <- runN c $ transformE a-                   return $ show core---debugCoreDot :: (IConnection conn,Reify a) => conn -> Exp a -> IO String-debugCoreDot c a = do core <- runN c $ transformE a-                      return $ (\(Right d) -> d) $ dot core---- | Convert the query into SQL-debugSQL :: (IConnection conn,Reify a) => conn -> Exp a -> IO String-debugSQL q c = do p <- doCompile q c-                  (C.SQL r) <- algToSQL (C.Algebra p :: AlgebraXML a)-                  return r---- | evaluate compiles the given Q query into an executable plan, executes this and returns --- the result as norm. For execution it uses the given connection. If the boolean flag is set--- to true it outputs the intermediate algebraic plan to disk.-evaluate :: (Reify a, IConnection conn) => conn -> Exp a -> IO (Exp a)-evaluate c q = do algPlan' <- doCompile c q-                  let algPlan = C.Algebra algPlan' :: AlgebraXML a-                  n <- executePlan c algPlan-                  disconnect c-                  return n+{-# LANGUAGE TemplateHaskell #-} --- | Transform a query into an algebraic plan.                   -doCompile :: (IConnection conn, Reify a) => conn -> Exp a -> IO String-doCompile c a = do core <- runN c $ transformE a-                   return $ typedCoreToAlgebra core+-- | Compilation, execution and introspection of queries+module Database.DSH.Compiler+  ( -- * Executing queries+    runQ+    -- * Debug functions+  , debugQ+  , debugVL+  , debugVLOpt+  , debugTA+  , debugTAOpt+  , runPrint+  ) where --- | Transform the Query into a ferry core program.-transformE :: forall a conn. (IConnection conn, Reify a) => Exp a -> N conn CoreExpr-transformE (UnitE ) = return $ Constant ([] :=> int) $ CInt 1-transformE (BoolE b) = return $ Constant ([] :=> bool) $ CBool b-transformE (CharE c) = return $ Constant ([] :=> string) $ CString [c] -transformE (IntegerE i) = return $ Constant ([] :=> int) $ CInt i-transformE (DoubleE d) = return $ Constant ([] :=> float) $ CFloat d-transformE (TextE t) = return $ Constant ([] :=> string) $ CString $ unpack t-transformE (PairE e1 e2) = do let ty = reify (undefined :: a)-                              c1 <- transformE e1-                              c2 <- transformE e2-                              return $ Rec ([] :=> transformTy ty) [RecElem (typeOf c1) "1" c1, RecElem (typeOf c2) "2" c2] -transformE (ListE es) = let ty = reify (undefined :: a)-                            qt = ([] :=> transformTy ty) -                        in foldr (F.Cons qt) (Nil qt) <$> mapM transformE es-transformE (AppE GroupWithKey (PairE (gfn :: Exp (ta -> rt)) (e :: Exp el))) = do-  let tel = reify (undefined :: el)-  fn' <- transformLamArg gfn-  let (_ :=> tfn@(FFn _ rt)) = typeOf fn'-  let gtr = list $ rec [(RLabel "1", rt), (RLabel "2", transformTy $ ListT tel)]-  e' <- transformArg e-  let (_ :=> te) = typeOf e'-  fv <- transformLamArg (LamE id :: Exp (el -> el))-  let (_ :=> tfv) = typeOf fv-  return $ App ([] :=> gtr)-               (App ([] :=> te .-> gtr)-                    (App ([] :=> tfn .-> te .-> gtr) (Var ([] :=> tfv .-> tfn .-> te .-> gtr) "groupWith") fv)-                    fn')-               e'-transformE (AppE D.Cons (PairE e1 e2)) = do-                                            e1' <- transformE e1-                                            e2' <- transformE e2-                                            let (_ :=> t) = typeOf e1'-                                            return $ F.Cons ([] :=> list t) e1' e2'-transformE (AppE Cond (PairE e1 (PairE e2 e3))) = do-                                             e1' <- transformE e1-                                             e2' <- transformE e2-                                             e3' <- transformE e3-                                             let (_ :=> t) = typeOf e2'-                                             return $ If ([] :=> t) e1' e2' e3'-transformE (AppE Fst (PairE e1 e2)) = do-  let ty = reify (undefined :: a)-  let tr = transformTy ty-  e1' <- transformArg (PairE e1 e2)-  let (_ :=> ta) = typeOf e1'-  return $ App ([] :=> tr) (transformF Fst (ta .-> tr)) e1'+import           Control.Applicative+import           Control.Arrow+import qualified Database.HDBC.PostgreSQL                 as H -transformE (AppE Snd (PairE e1 e2)) = do-  let ty = reify (undefined :: a)-  let tr = transformTy ty-  e1' <- transformArg (PairE e1 e2)-  let (_ :=> ta) = typeOf e1'-  return $ App ([] :=> tr) (transformF Snd (ta .-> tr)) e1'+import           Database.DSH.Translate.Frontend2CL+import           Database.DSH.Execute.Sql -transformE (AppE f2 (PairE (LamE f) e)) = do-  let ty = reify (undefined :: a)-  let tr = transformTy ty-  f' <- transformLamArg (LamE f)-  e' <- transformArg e-  let (_ :=> t1) = typeOf f'-  let (_ :=> t2) = typeOf e'-  return $ App ([] :=> tr)-              (App ([] :=> t2 .-> tr) (transformF f2 (t1 .-> t2 .-> tr)) f')-              e'+import qualified Database.DSH.VL.Lang                     as VL+import           Database.DSH.VL.Vector+import           Database.DSH.NKL.Rewrite+import qualified Database.DSH.CL.Lang                     as CL+import           Database.DSH.CL.Opt+import           Database.DSH.Common.QueryPlan+import           Database.DSH.Export+import           Database.DSH.Frontend.Internals+import           Database.DSH.Optimizer.TA.OptimizeTA+import           Database.DSH.Optimizer.VL.OptimizeVL+import           Database.DSH.Frontend.Schema+import           Database.DSH.Translate.Algebra2Query+import           Database.DSH.Translate.CL2NKL+import           Database.DSH.Translate.FKL2VL+import           Database.DSH.Translate.NKL2FKL+import           Database.DSH.Translate.VL2Algebra -transformE (AppE f2 (PairE e1 e2)) = do-  let ty = reify (undefined :: a)-  let tr = transformTy ty-  if isOp f2-     then do e1' <- transformE e1-             e2' <- transformE e2-             return $ BinOp ([] :=> tr) (transformOp f2) e1' e2'-     else do e1' <- transformArg e1-             e2' <- transformArg e2-             let (_ :=> ta1) = typeOf e1'-             let (_ :=> ta2) = typeOf e2'-             return $ App ([] :=> tr) (App ([] :=> ta2 .-> tr) (transformF f2 (ta1 .-> ta2 .-> tr)) e1') e2'+--------------------------------------------------------------------------------+-- Different versions of the flattening compiler pipeline -transformE (AppE f1 e1) = do-  let ty = reify (undefined :: a)-  let tr = transformTy ty-  e1' <- transformArg e1-  let (_ :=> ta) = typeOf e1'-  return $ App ([] :=> tr) (transformF f1 (ta .-> tr)) e1'+-- | Backend-agnostic part of the pipeline.+commonPipeline :: CL.Expr -> QueryPlan VL.VL VLDVec+commonPipeline =+    optimizeComprehensions+    >>> desugarComprehensions+    >>> optimizeNKL+    >>> flatTransform+    >>> specializeVectorOps -transformE (VarE i) = do-  let ty = reify (undefined :: a)-  return $ Var ([] :=> transformTy ty) $ prefixVar $ fromIntegral i-  -transformE (TableE (TableCSV filepath)) = do-  let ty = reify (undefined :: a)-  e1 <- lift (csvImport filepath ty)-  transformE e1+nkl2Sql :: CL.Expr -> Shape (BackendCode SqlBackend)+nkl2Sql =+    commonPipeline+    >>> optimizeVLDefault+    >>> implementVectorOpsPF+    >>> optimizeTA+    >>> generateSqlQueries --- When a table node is encountered check that the given description--- matches the actual table information in the database.-transformE (TableE (TableDB n ks)) = do-                                    let ty = reify (undefined :: a)-                                    fv <- freshVar-                                    let tTy@(FList (FRec ts)) = flatFTy ty-                                    let varB = Var ([] :=> FRec ts) $ prefixVar fv-                                    tableDescr <- tableInfo n-                                    let tyDescr = if length tableDescr == length ts-                                                    then zip tableDescr ts-                                                    else error $ "Inferred typed: " ++ show tTy ++ " \n doesn't match type of table: \"" -                                                                        ++ n ++ "\" in the database. The table has the shape: " ++ show (map fst tableDescr) ++ ". " ++ show ty -                                    let cols = [Column cn t | ((cn, f), (RLabel i, t)) <- tyDescr, legalType n cn i t f]-                                    let keyCols = nub (concat ks) L.\\ map fst tableDescr-                                    let keys = if keyCols == []-                                                  then if ks /= [] then map Key ks else [Key $ map (\(Column n' _) -> n') cols]-                                                  else error $ "The following columns were used as key but not a column of table " ++ n ++ " : " ++ show keyCols-                                    let table' = Table ([] :=> tTy) n cols keys-                                    let pattern = [prefixVar fv]-                                    let nameType = map (\(Column name t) -> (name, t)) cols -                                    let body = foldr (\(nr, t) b -> -                                                    let (_ :=> bt) = typeOf b-                                                     in Rec ([] :=> FRec [(RLabel "1", t), (RLabel "2", bt)]) [RecElem ([] :=> t) "1" (F.Elem ([] :=> t) varB nr), RecElem ([] :=> bt) "2" b])-                                                  ((\(nr,t) -> F.Elem ([] :=> t) varB nr) $ last nameType)-                                                  (init nameType)-                                    let ([] :=> rt) = typeOf body-                                    let lambda = ParAbstr ([] :=> FRec ts .-> rt) pattern body-                                    let expr = App ([] :=> FList rt) (App ([] :=> (FList $ FRec ts) .-> FList rt) -                                                                    (Var ([] :=> (FRec ts .-> rt) .-> (FList $ FRec ts) .-> FList rt) "map") -                                                                    lambda)-                                                                   (ParExpr (typeOf table') table') -                                    return expr-    where-        legalType :: String -> String -> String -> FType -> (FType -> Bool) -> Bool-        legalType tn cn nr t f = f t || error ( "The type: "-                                                ++ show t-                                                ++ "\nis not compatible with the type of column nr: " ++ nr-                                                ++ " namely: " ++ cn ++ "\n in table " ++ tn ++ ".")-transformE (LamE _) = $impossible+nkl2TAFile :: String -> CL.Expr -> IO ()+nkl2TAFile prefix =+    commonPipeline+    >>> optimizeVLDefault+    >>> implementVectorOpsPF+    >>> (exportTAPlan prefix) -transformLamArg :: forall a b conn. (IConnection conn) => Exp (a -> b) -> N conn Param-transformLamArg (LamE f) = do -  let ty = reify (undefined :: a -> b)-  n <- freshVar-  let fty = transformTy ty-  let e1 = f $ VarE $ fromIntegral n -  ParAbstr ([] :=> fty) [prefixVar n] <$> transformE e1-transformLamArg (AppE _ _) = $impossible-transformLamArg (VarE _)   = $impossible+nkl2TAFileOpt :: String -> CL.Expr -> IO ()+nkl2TAFileOpt prefix =+    commonPipeline+    >>> optimizeVLDefault+    >>> implementVectorOpsPF+    >>> optimizeTA+    >>> exportTAPlan (prefix ++ "_opt") +nkl2VLFile :: String -> CL.Expr -> IO ()+nkl2VLFile prefix = commonPipeline >>> exportVLPlan prefix -transformArg :: (IConnection conn,Reify a) => Exp a -> N conn Param-transformArg e = (\e' -> ParExpr (typeOf e') e') <$> transformE e- --- | Construct a flat-FerryCore type out of a DSH type--- A flat type consists out of two tuples, a record is translated as:--- {r1 :: t1, r2 :: t2, r3 :: t3, r4 :: t4} (t1, (t2, (t3, t4)))-flatFTy :: Type a -> FType-flatFTy (ListT t) = FList $ FRec $ flatFTy' 1 t- where-     flatFTy' :: Int -> Type a -> [(RLabel, FType)]-     flatFTy' i (PairT t1 t2) = (RLabel $ show i, transformTy t1) : flatFTy' (i + 1) t2-     flatFTy' i ty            = [(RLabel $ show i, transformTy ty)]-flatFTy _         = $impossible+nkl2VLFileOpt :: String -> CL.Expr -> IO ()+nkl2VLFileOpt prefix =+    commonPipeline+    >>> optimizeVLDefault+    >>> exportVLPlan (prefix ++ "_opt") --- Determine the size of a flat type-sizeOfTy :: Type a -> Int-sizeOfTy (PairT _ t2) = 1 + sizeOfTy t2-sizeOfTy _              = 1 +--------------------------------------------------------------------------------+-- Functions for executing and debugging DSH queries via the Flattening backend --- | Transform an arbitrary DSH-type into a ferry core type -transformTy :: Type a -> FType-transformTy UnitT = int-transformTy BoolT = bool-transformTy CharT = string-transformTy TextT = string-transformTy IntegerT = int-transformTy DoubleT = float-transformTy (PairT t1 t2) = FRec [(RLabel "1", transformTy t1), (RLabel "2", transformTy t2)]-transformTy (ListT t1) = FList $ transformTy t1-transformTy (ArrowT t1 t2) = transformTy t1 .-> transformTy t2+-- | Run a query on a SQL backend+runQ :: QA a => H.Connection -> Q a -> IO a+runQ conn (Q q) = do+    let ty = reify (undefined :: a)+    q' <- toComprehensions (getTableInfo conn) q+    let sqlQueryBundle = nkl2Sql q'+    frExp <$> executeSql (SqlBackend conn) sqlQueryBundle ty +-- | Debugging function: dump the table algebra plan (JSON) to a file.+debugTA :: QA a => String -> H.Connection -> Q a -> IO ()+debugTA prefix c (Q e) = do+    e' <- toComprehensions (getTableInfo c) e+    nkl2TAFile prefix e' -isOp :: Fun a b -> Bool-isOp Add  = True-isOp Sub  = True-isOp Mul  = True-isOp Div  = True-isOp Equ  = True-isOp Lt   = True-isOp Lte  = True-isOp Gte  = True-isOp Gt   = True-isOp Conj = True-isOp Disj = True-isOp _    = False+-- | Debugging function: dump the optimized table algebra plan (JSON) to a file.+debugTAOpt :: QA a => String -> H.Connection -> Q a -> IO ()+debugTAOpt prefix c (Q e) = do+    e' <- toComprehensions (getTableInfo c) e+    nkl2TAFileOpt prefix e' --- | Translate the DSH operator to Ferry Core operators-transformOp :: Fun a b -> Op-transformOp Add  = Op "+"-transformOp Sub  = Op "-"-transformOp Mul  = Op "*"-transformOp Div  = Op "/"-transformOp Equ  = Op "=="-transformOp Lt   = Op "<"-transformOp Lte  = Op "<="-transformOp Gte  = Op ">="-transformOp Gt   = Op ">"-transformOp Conj = Op "&&"-transformOp Disj = Op "||"-transformOp _    = $impossible+-- | Debugging function: dump the VL query plan (DAG) for a query to a+-- file (SQL version).+debugVL :: QA a => String -> H.Connection -> Q a -> IO ()+debugVL prefix c (Q e) = do+    e' <- toComprehensions (getTableInfo c) e+    nkl2VLFile prefix e' +-- | Debugging function: dump the optimized VL query plan (DAG) for a+-- query to a file (SQL version).+debugVLOpt :: QA a => String -> H.Connection -> Q a -> IO ()+debugVLOpt prefix c (Q e) = do+    e' <- toComprehensions (getTableInfo c) e+    nkl2VLFileOpt prefix e' --- | Transform a DSH-primitive-function (f) with an instantiated typed into a FerryCore--- expression-transformF :: (Show f) => f -> FType -> CoreExpr-transformF f t = Var ([] :=> t) $ (\txt -> case txt of-                                            (x:xs) -> toLower x : xs-                                            _      -> $impossible) $ show f+-- | Dump all intermediate algebra representations (VL, TA) to files.+debugQ :: QA a => String -> H.Connection -> Q a -> IO ()+debugQ prefix conn q = do+    debugVL prefix conn q+    debugVLOpt prefix conn q+    debugTA prefix conn q+    debugTAOpt prefix conn q --- | Retrieve through the given database connection information on the table (columns with their types)--- which name is given as the second argument.        -getTableInfo :: IConnection conn => conn -> String -> IO [(String,FType -> Bool)]-getTableInfo c n = do-                    info <- describeTable c n-                    return $ toTableDescr info-                    -        where-          toTableDescr :: [(String, SqlColDesc)] -> [(String,FType -> Bool)]-          toTableDescr = L.sortBy (\(n1, _) (n2, _) -> compare n1 n2) . map (\(name, props) -> (name, compatibleType (colType props)))-          compatibleType :: SqlTypeId -> FType -> Bool-          compatibleType dbT hsT = case hsT of-                                        FUnit   -> True-                                        FBool   -> dbT `L.elem` [SqlSmallIntT, SqlIntegerT, SqlBitT]-                                        FString -> dbT `L.elem` [SqlCharT, SqlWCharT, SqlVarCharT]-                                        FInt    -> dbT `L.elem` [SqlSmallIntT, SqlIntegerT, SqlTinyIntT, SqlBigIntT, SqlNumericT]-                                        FFloat  -> dbT `L.elem` [SqlDecimalT, SqlRealT, SqlFloatT, SqlDoubleT]-                                        t       -> error $ "You can't store this kind of data in a table... " ++ show t ++ " " ++ show n+-- | Convenience function: execute a query on a SQL backend and print+-- its result+runPrint :: (Show a, QA a) => H.Connection -> Q a -> IO ()+runPrint conn q = (show <$> runQ conn q) >>= putStrLn
+ src/Database/DSH/Execute/Backend.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs            #-}+{-# LANGUAGE TypeFamilies     #-}+{-# LANGUAGE TemplateHaskell  #-}++-- | This module provides an abstraction over flat relational backends+-- w.r.t. to query execution and result value construction.+module Database.DSH.Execute.Backend where++import           Text.Printf+import qualified Data.IntMap                     as IM+import qualified Data.DList as D+import           Data.List              ++import           Database.DSH.Impossible+import           Database.DSH.Frontend.Internals+import           Database.DSH.Common.QueryPlan+import           Database.DSH.Common.Pretty+import           Database.DSH.Execute.TH++-- | An abstract backend on which flat queries can be executed.+class Backend c where+    data BackendRow c+    data BackendCode c++    execFlatQuery :: c -> BackendCode c -> IO [BackendRow c]++-- | Abstraction over result rows for a specific backend.+class Row r where+    -- | The type of single attribute values+    data Scalar r++    -- | Look up an attribute in the row+    col       :: String -> r -> (Scalar r)++    -- | Convert an attribute value to a segment descriptor value+    descrVal  :: Scalar r -> Int++    -- | Convert an attribute value to a value term+    scalarVal :: Scalar r -> Type a -> Exp a++------------------------------------------------------------------------------+-- Different kinds of layouts that contain results in various forms++-- Generate the definition for the 'TabTuple' type+$(mkTabTupleType 16)++-- | Row layout with nesting data in the form of raw tabular results+data TabLayout a where+    TCol   :: Type a -> String -> TabLayout a+    TNest  :: (Reify a, Backend c, Row (BackendRow c)) => Type [a] -> [BackendRow c] -> TabLayout a -> TabLayout [a]+    TTuple :: TabTuple a -> TabLayout a++-- Generate the definition for the 'SegTuple' type+$(mkSegTupleType 16)++-- | A map from segment descriptor to list value expressions+type SegMap a = IM.IntMap (Exp a)++-- | Row layout with nesting data in the form of segment maps+data SegLayout a where+    SCol   :: Type a -> String -> SegLayout a+    SNest  :: Reify a => Type [a] -> SegMap [a] -> SegLayout [a]+    STuple :: SegTuple a -> SegLayout a++execQueryBundle :: (Backend c, Row (BackendRow c)) => c -> Shape (BackendCode c) -> Type a -> IO (Exp a)+execQueryBundle conn shape ty = +    case (shape, ty) of+        (VShape q lyt, ListT ety) -> do+            tab  <- execFlatQuery conn q+            tlyt <- execNested conn lyt ety+            return $ fromVector tab tlyt+        (SShape q lyt, _) -> do+            tab  <- execFlatQuery conn q+            tlyt <- execNested conn lyt ty+            return $ fromPrim tab tlyt+        _ -> $impossible++-- | Traverse the layout and execute all subqueries for nested vectors+execNested :: (Backend c, Row (BackendRow c)) => c -> Layout (BackendCode c) -> Type a -> IO (TabLayout a)+execNested conn lyt ty =+    case (lyt, ty) of+        (LCol i, t)             -> return $ TCol t (itemCol i)+        (LNest q clyt, ListT t) -> do+            tab   <- execFlatQuery conn q+            clyt' <- execNested conn clyt t+            return $ TNest ty tab clyt'+        (LTuple lyts, TupleT tupTy) -> let execTuple = $(mkExecTuple 16)+                                       in execTuple lyts tupTy+        (_, ty) -> error $ printf "Type does not match query structure: %s" (pp ty)++------------------------------------------------------------------------------+-- Construct result value terms from raw tabular results++-- | +itemCol :: Int -> String+itemCol 1 = "item1"+itemCol 2 = "item2"+itemCol 3 = "item3"+itemCol 4 = "item4"+itemCol 5 = "item5"+itemCol 6 = "item6"+itemCol 7 = "item7"+itemCol 8 = "item8"+itemCol 9 = "item9"+itemCol 10 = "item10"+itemCol n = "item" ++ show n++posCol :: Row r => r -> Int+posCol row = descrVal $ col "pos" row++descrCol :: Row r => r -> Int+descrCol row = descrVal $ col "descr" row++fromVector :: (Reify a, Row r) => [r] -> TabLayout a -> Exp [a]+fromVector tab tlyt =+    let slyt = segmentLayout tlyt+    in ListE $ D.toList $ foldl' (vecIter slyt) D.empty tab++vecIter :: Row r => SegLayout a -> D.DList (Exp a) -> r -> D.DList (Exp a)+vecIter slyt vals row = +    let val = constructVal slyt row+    in D.snoc vals val++fromPrim :: Row r => [r] -> TabLayout a -> Exp a+fromPrim tab tlyt =+    let slyt = segmentLayout tlyt+    in case tab of+           [row] -> constructVal slyt row+           _     -> $impossible++------------------------------------------------------------------------------+-- Construct nested result values from segmented vectors++-- | Construct values for nested vectors in the layout.+segmentLayout :: TabLayout a -> SegLayout a+segmentLayout tlyt =+    case tlyt of+        TCol ty s            -> SCol ty s+        TNest ty tab clyt    -> SNest ty (fromSegVector tab clyt)+        TTuple tup           -> let segmentTuple = $(mkSegmentTupleFun 16)+                                in STuple $ segmentTuple tup++data SegAcc a = SegAcc { currSeg :: Int+                       , segMap  :: SegMap [a]+                       , currVec :: D.DList (Exp a)+                       }++-- | Construct a segment map from a segmented vector+fromSegVector :: (Reify a, Row r) => [r] -> TabLayout a -> SegMap [a]+fromSegVector tab tlyt =+    let slyt = segmentLayout tlyt+        initialAcc = SegAcc { currSeg = 0, segMap = IM.empty, currVec = D.empty }+        finalAcc = foldl' (segIter slyt) initialAcc tab+    in IM.insert (currSeg finalAcc) (ListE $ D.toList $ currVec finalAcc) (segMap finalAcc)++-- | Fold iterator that constructs a map from segment descriptor to+-- the list value that is represented by that segment+segIter :: (Reify a, Row r) => SegLayout a -> SegAcc a -> r -> SegAcc a+segIter lyt acc row = +    let val   = constructVal lyt row+        descr = descrCol row+    in if descr == currSeg acc+       then acc { currVec = D.snoc (currVec acc) val }+       else acc { currSeg = descr+                , segMap  = IM.insert (currSeg acc) (ListE $ D.toList $ currVec acc) (segMap acc)+                , currVec = D.singleton val+                }++------------------------------------------------------------------------------+-- Construct values from table rows    ++-- | Construct a value from a vector row according to the given layout+constructVal :: Row r => SegLayout a -> r -> Exp a+constructVal lyt row =+    case lyt of+        STuple stup       -> let constructTuple = $(mkConstructTuple 16) +                             in constructTuple stup row+        SNest _ segmap    -> let pos = posCol row+                              in case IM.lookup pos segmap of+                                  Just v  -> v+                                  Nothing -> ListE []+        SCol ty c         -> scalarVal (col c row) ty
+ src/Database/DSH/Execute/Sql.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell   #-}+{-# LANGUAGE TypeFamilies      #-}+{-# LANGUAGE GADTs             #-}++-- | This module implements the execution of SQL query bundles and the+-- construction of nested values from the resulting vector bundle.+module Database.DSH.Execute.Sql+  ( executeSql+  , SqlBackend(..)+  , BackendCode(..)+  ) where++import           Text.Printf++import           Database.HDBC+import           Database.HDBC.PostgreSQL++import qualified Data.Text                             as Txt+import qualified Data.Text.Encoding                    as Txt+import qualified Data.Map as M+import           Control.Monad+import           Control.Applicative++import           Database.DSH.Impossible+import           Database.DSH.Frontend.Internals+import           Database.DSH.Execute.Backend++import           Database.DSH.Common.QueryPlan++newtype SqlBackend = SqlBackend Connection++instance Backend SqlBackend where+    data BackendRow SqlBackend  = SqlRow (M.Map String SqlValue)+    data BackendCode SqlBackend = SqlCode String++    execFlatQuery (SqlBackend conn) (SqlCode q) = do+        stmt  <- prepare conn q+        void $ execute stmt []+        map SqlRow <$> fetchAllRowsMap' stmt++instance Row (BackendRow SqlBackend) where+    data Scalar (BackendRow SqlBackend) = SqlScalar SqlValue++    col c (SqlRow r) = +        case M.lookup c r of+            Just v  -> SqlScalar v+            Nothing -> error $ printf "col lookup %s failed in %s" c (show r)++    descrVal (SqlScalar (SqlInt32 i))   = fromIntegral i+    descrVal (SqlScalar (SqlInteger i)) = fromIntegral i+    descrVal _                          = $impossible++    scalarVal (SqlScalar SqlNull)           UnitT    = UnitE+    scalarVal (SqlScalar (SqlInteger _))    UnitT    = UnitE+    scalarVal (SqlScalar (SqlInteger i))    IntegerT = IntegerE i+    scalarVal (SqlScalar (SqlInt32 i))      IntegerT = IntegerE $ fromIntegral i+    scalarVal (SqlScalar (SqlInt64 i))      IntegerT = IntegerE $ fromIntegral i+    scalarVal (SqlScalar (SqlWord32 i))     IntegerT = IntegerE $ fromIntegral i+    scalarVal (SqlScalar (SqlWord64 i))     IntegerT = IntegerE $ fromIntegral i+    scalarVal (SqlScalar (SqlDouble d))     DoubleT  = DoubleE d+    scalarVal (SqlScalar (SqlRational d))   DoubleT  = DoubleE $ fromRational d+    scalarVal (SqlScalar (SqlInteger d))    DoubleT  = DoubleE $ fromIntegral d+    scalarVal (SqlScalar (SqlInt32 d))      DoubleT  = DoubleE $ fromIntegral d+    scalarVal (SqlScalar (SqlInt64 d))      DoubleT  = DoubleE $ fromIntegral d+    scalarVal (SqlScalar (SqlWord32 d))     DoubleT  = DoubleE $ fromIntegral d+    scalarVal (SqlScalar (SqlWord64 d))     DoubleT  = DoubleE $ fromIntegral d+    scalarVal (SqlScalar (SqlBool b))       BoolT    = BoolE b+    scalarVal (SqlScalar (SqlInteger i))    BoolT    = BoolE (i /= 0)+    scalarVal (SqlScalar (SqlInt32 i))      BoolT    = BoolE (i /= 0)+    scalarVal (SqlScalar (SqlInt64 i))      BoolT    = BoolE (i /= 0)+    scalarVal (SqlScalar (SqlWord32 i))     BoolT    = BoolE (i /= 0)+    scalarVal (SqlScalar (SqlWord64 i))     BoolT    = BoolE (i /= 0)+    scalarVal (SqlScalar (SqlChar c))       CharT    = CharE c+    scalarVal (SqlScalar (SqlString (c:_))) CharT    = CharE c+    scalarVal (SqlScalar (SqlByteString c)) CharT    = CharE (head $ Txt.unpack $ Txt.decodeUtf8 c)+    scalarVal (SqlScalar (SqlString t))     TextT    = TextE (Txt.pack t)+    scalarVal (SqlScalar (SqlByteString s)) TextT    = TextE (Txt.decodeUtf8 s)+    scalarVal (SqlScalar sql)               _        = error $ "Unsupported SqlValue: "  ++ show sql++-- | Execute a SQL query bundle on PostgreSQL.+executeSql :: SqlBackend -> Shape (BackendCode SqlBackend) -> Type a -> IO (Exp a)+executeSql = execQueryBundle
+ src/Database/DSH/Execute/TH.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Execute.TH+    ( mkExecTuple+    , mkTabTupleType+    , mkSegTupleType+    , mkSegmentTupleFun+    , mkConstructTuple+    ) where++import           Control.Applicative+import           Language.Haskell.TH+import           Data.List++import           Text.Printf++import           Database.DSH.Impossible+import           Database.DSH.Frontend.TupleTypes+import qualified Database.DSH.Frontend.Internals as DSH++--------------------------------------------------------------------------------+-- Common name definitions++tabTupleConsName :: Int -> Name+tabTupleConsName width = mkName $ printf "TTuple%d" width++segTupleConsName :: Int -> Name+segTupleConsName width = mkName $ printf "STuple%d" width++--------------------------------------------------------------------------------+-- Generate the function that executes queries in a tuple layout++elemTyName :: Int -> Q Name+elemTyName i = newName $ printf "ty%d" i++elemLytName :: Int -> Q (Name, Name)+elemLytName i = (,) <$> newName (printf "lyt%d" i)+                    <*> newName (printf "lyt%d'" i)+++-- | Generate the recursive call to 'execNested'+-- 'lyt<n>' <- execNested conn lyt<n> ty<n>'+mkExecNestedStmt :: Name -> Name -> Name -> Stmt+mkExecNestedStmt tyName lytName resLytName =+    let execNested = VarE $ mkName "execNested"+        conn       = VarE $ mkName "conn"      +        callE      = AppE (AppE (AppE execNested conn) (VarE lytName)) (VarE tyName)++    in BindS (VarP resLytName) callE++-- | Generate the case for one particular tuple type+mkExecTupleMatch :: Int -> Q Match+mkExecTupleMatch width = do+    tyNames               <- mapM elemTyName [1..width]+    (lytNames, lytNames') <- unzip <$> mapM elemLytName [1..width]++    -- '([lyt1, ..., lyt<n>], Tuple<n>T ty1 ... ty<n>)'+    let pat = TupP [ ListP $ map VarP lytNames+                   , ConP (tupTyConstName width) (map VarP tyNames)+                   ]++    -- 'return $ TTuple $ TTuple<n> ty lyt1 ... lyt<n>'+    let execNestedStmts = zipWith3 mkExecNestedStmt tyNames lytNames lytNames'+        returnStmt      = NoBindS $ AppE (VarE 'return)+                                  $ AppE (ConE $ mkName "TTuple")+                                  $ foldl' AppE +                                           (AppE (ConE $ tabTupleConsName width) (VarE $ mkName "ty"))+                                           (map VarE lytNames')+                                                ++    return $ Match pat (NormalB $ DoE $ execNestedStmts ++ [returnStmt]) []++-- | Generate a lambda expression that matches on a tuple type layout+-- and recursively calls execNested on the tuple member layouts.+-- @+-- \lyts ty ->+--  case (lyts, ty) of+--      ([lyt1, ..., lyt<n>], Tuple<n>T ty1 ... ty<n>) -> do+--          lyt1' <- execNested conn lyt1 ty1+--          ...+--          lyt<n>' <- execNested conn lyt<n> ty<n>+--          return $ TTuple $ TTuple<n> ty lyt1 ... lyt<n>+-- @+-- +-- The lambda expression is /not/ closed: The names 'conn' and 'ty' must be in+-- scope where 'conn' is the database connection and 'ty' is the tuple type being+-- dissected.+mkExecTuple :: Int -> Q Exp+mkExecTuple maxWidth = do+    lytName       <- newName "lyts"+    tyName        <- newName "tys"++    tupMatches    <- mapM mkExecTupleMatch [2..maxWidth]+    impossibleExp <- impossible+    let matches = tupMatches ++ [Match WildP (NormalB impossibleExp) []]++    let lamBody = CaseE (TupE [VarE lytName, VarE tyName]) matches+    return $ LamE [VarP lytName, VarP tyName] lamBody++--------------------------------------------------------------------------------+-- Generate tuple layout type containing individual query results or+-- segmaps. The code generated for both is mostly identical except for+-- the layout type constructor and the constructor names.++tupElemTyName :: Int -> Q Name+tupElemTyName i = newName $ printf "t%d" i++-- | Generate a single constructor for the 'TabTuple' type.+mkTupleLytCons :: Name -> (Type -> Type) -> (Int -> Name) -> Int -> Q Con+mkTupleLytCons tupTyName lytTyCons conName width = do++    tupElemTyNames <- mapM tupElemTyName [1..width]++    let tyVarBinders     = map PlainTV tupElemTyNames++        -- (t1, ..., t<n>)+        tupTy            = foldl' AppT (TupleT width)+                           $ map VarT tupElemTyNames+    +        -- a ~ (t1, ..., t<n>)+        tupConstraint    = EqualP (VarT tupTyName) tupTy++        -- Reify t1, ..., Reify t<n>+        reifyConstraints = map (\n -> ClassP ''DSH.Reify [VarT n]) tupElemTyNames++        constraints      = tupConstraint : reifyConstraints ++    let -- 'Type a'+        dshTypeTy  = (NotStrict, AppT (ConT ''DSH.Type) (VarT tupTyName))+        -- 'TabLayout t1, TabLayout t<n>+        elemLytTys = [ (NotStrict, lytTyCons (VarT t)) -- AppT (ConT $ mkName "TabLayout") (VarT t))+                     | t <- tupElemTyNames+                     ]+        argTys     = dshTypeTy : elemLytTys +    +    return $ ForallC tyVarBinders constraints+           $ NormalC (conName width) {- (tabTupleConsName width) -} argTys++-- | Generate the data type for 'TabTuple'/'SegTuple' layouts that contain+-- tabular query results.+-- @+-- data TabTuple a where+--     TTuple3 :: (Reify t1, ..., Reify t<n>) => Type (t1, ..., t<n>) +--                                            -> TabLayout t1 +--                                            -> ... +--                                            -> TabLayout t<n> +--                                            -> TabTuple (t1, ..., t<n>)+-- @+-- +-- Because TH does not directly support GADT syntax, we have to+-- emulate it using explicit universal quantification:+-- +-- @+-- data TabTuple a =+--     forall t1, ..., t<n>. a ~ (t1, ..., t<n>),+--                           Reify t1,+--                           ...+--                           Reify t<n> =>+--                           Type a -> TabLayout t1 -> ... -> TabLayout t<n>+-- @+mkTupleLyt :: Name -> (Type -> Type) -> (Int -> Name) -> Int -> Q [Dec]+mkTupleLyt tyName lytTyCons conName maxWidth = do+    tupTyName <- newName "a"+    cons      <- mapM (mkTupleLytCons tupTyName lytTyCons conName) [2..maxWidth]+    +    return $ [DataD [] tyName  [PlainTV tupTyName] cons []]++--------------------------------------------------------------------------------+-- Generate the tuple layout type containing tabular results++mkTabTupleType :: Int -> Q [Dec]+mkTabTupleType maxWidth = mkTupleLyt tabTupleTyName tabLayoutTyCons tabTupleConsName maxWidth+  where+    tabLayoutTyCons :: Type -> Type+    tabLayoutTyCons argTy = AppT (ConT $ mkName "TabLayout") argTy++    tabTupleTyName :: Name+    tabTupleTyName = mkName "TabTuple"++--------------------------------------------------------------------------------+-- Generate the tuple layout type containing segment maps++mkSegTupleType :: Int -> Q [Dec]+mkSegTupleType maxWidth = mkTupleLyt segTupleTyName segLayoutTyCons segTupleConsName maxWidth+  where+    segLayoutTyCons :: Type -> Type+    segLayoutTyCons argTy = AppT (ConT $ mkName "SegLayout") argTy++    segTupleTyName :: Name+    segTupleTyName = mkName "SegTuple"++--------------------------------------------------------------------------------+-- Generate the mapping function between tabular and segment map layouts.++mkSegmentTupleMatch :: Int -> Q Match+mkSegmentTupleMatch width = do+    tyName   <- newName "ty"+    lytNames <- mapM (\i -> newName $ printf "tlyt%d" i) [1..width]+    let tuplePat = ConP (tabTupleConsName width) (VarP tyName : map VarP lytNames)++    let segFun  = VarE $ mkName "segmentLayout"+        segLyts = map ((AppE segFun) . VarE) lytNames++    let bodyExp = foldl' AppE (ConE $ segTupleConsName width) +                              (VarE tyName : segLyts)+    return $ Match tuplePat (NormalB bodyExp) []++-- | Generate the definition for the 'segmentTuple' function that maps+-- layouts with tabular SQL results to layouts with segment maps.+-- @+-- +-- \lyt -> +--   case lyt of+--   ...+--   (TTuple<n> ty tlyt1 ... tlyt<n>) = STuple<n> ty (segmentLayout tlyt1) +--                                                   ...+--                                                   (segmentLayout tlyt<n>)+-- @+mkSegmentTupleFun :: Int -> Q Exp+mkSegmentTupleFun maxWidth = do+    lytName       <- newName "lyt"+    tupMatches    <- mapM mkSegmentTupleMatch [2..maxWidth]+    let lamBody = CaseE (TupE [VarE lytName]) tupMatches++    return $ LamE [VarP lytName] lamBody++--------------------------------------------------------------------------------+-- Generate the constructor function from a segmap tuple layout to a+-- tuple value++mkConstructTupleMatch :: Name -> Int -> Q Match+mkConstructTupleMatch rowName width = do+    lytNames <- mapM (\i -> newName $ printf "slyt%d" i) [1..width]++    let tuplePat = ConP (segTupleConsName width) (WildP : map VarP lytNames)++    let constructFun   = VarE $ mkName "constructVal"+        resultElemExps = [ AppE (AppE constructFun (VarE l)) (VarE rowName)+                         | l <- lytNames+                         ]+        resultValExp   = AppE (ConE outerConst) +                              (foldl' AppE (ConE $ innerConst width) resultElemExps)++    return $ Match tuplePat (NormalB resultValExp) []++mkConstructTuple :: Int -> Q Exp+mkConstructTuple maxWidth = do+    lytName       <- newName "lyt"+    rowName       <- newName "row"++    tupMatches    <- mapM (mkConstructTupleMatch rowName) [2..maxWidth]+    let lamBody = CaseE (TupE [VarE lytName]) tupMatches++    return $ LamE [VarP lytName, VarP rowName] lamBody
+ src/Database/DSH/Export.hs view
@@ -0,0 +1,37 @@+-- | Debug functions to export query plans and rendered database code+-- in various forms.+module Database.DSH.Export+  ( exportVLPlan+  , exportTAPlan+  ) where++import           Database.Algebra.Dag+import           Database.Algebra.Table.Lang+import qualified Database.Algebra.Table.Render.JSON as PFJSON++import           Database.DSH.Common.QueryPlan+import           Database.DSH.VL.Lang+import           Database.DSH.VL.Vector+import qualified Database.DSH.VL.Render.JSON        as VLJSON++exportVLPlan :: String -> QueryPlan VL VLDVec -> IO ()+exportVLPlan prefix vlPlan = do+  let planPath = prefix ++ "_vl.plan"+      shapePath = prefix ++ "_vl.shape"++  VLJSON.planToFile planPath ( queryTags vlPlan+                             , shapeNodes $ queryShape vlPlan+                             , nodeMap $ queryDag vlPlan+                             )+  writeFile shapePath $ show $ queryShape vlPlan++exportTAPlan :: String -> QueryPlan TableAlgebra NDVec -> IO ()+exportTAPlan prefix pfPlan = do+  let planPath = prefix ++ "_ta.plan"+      shapePath = prefix ++ "_ta.shape"++  PFJSON.planToFile planPath ( queryTags pfPlan+                             , shapeNodes $ queryShape pfPlan+                             , nodeMap $ queryDag pfPlan+                             )+  writeFile shapePath $ show $ queryShape pfPlan
− src/Database/DSH/Externals.hs
@@ -1,661 +0,0 @@-{-# LANGUAGE FlexibleInstances     #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TemplateHaskell       #-}-{-# LANGUAGE TypeFamilies          #-}--module Database.DSH.Externals where--import Database.DSH.Internals-import Database.DSH.Impossible-import Database.DSH.TH--import Prelude ( Eq, Ord, Num(..), Fractional(..), Show(..)-               , Bool(..), Char, Integer, Double, String, Maybe(..), Either(..)-               , id, undefined, ($), (.))-import qualified Prelude as P--import Data.String-import Data.Text (Text)-import qualified Data.Text as T---- QA Instances--instance QA () where-  type Rep () = ()-  toExp () = UnitE-  frExp UnitE = ()-  frExp _ = $impossible--instance QA Bool where-  type Rep Bool = Bool-  toExp = BoolE-  frExp (BoolE b) = b-  frExp _ = $impossible--instance QA Char where-  type Rep Char = Char-  toExp = CharE-  frExp (CharE c) = c-  frExp _ = $impossible--instance QA Integer where-  type Rep Integer = Integer-  toExp = IntegerE-  frExp (IntegerE i) = i-  frExp _ = $impossible--instance QA Double where-  type Rep Double = Double-  toExp = DoubleE-  frExp (DoubleE d) = d-  frExp _ = $impossible--instance QA Text where-  type Rep Text = Text-  toExp = TextE-  frExp (TextE t) = t-  frExp _ = $impossible--instance (QA a,QA b) => QA (a,b) where-  type Rep (a,b) = (Rep a,Rep b)-  toExp (a,b) = PairE (toExp a) (toExp b)-  frExp (PairE a b) = (frExp a,frExp b)-  frExp _ = $impossible--instance (QA a,QA b,QA c) => QA (a,b,c) where-  type Rep (a,b,c) = (Rep a,(Rep b,Rep c))-  toExp (a,b,c) = PairE (toExp a) (PairE (toExp b) (toExp c))-  frExp (PairE a (PairE b c)) = (frExp a,frExp b,frExp c)-  frExp _ = $impossible--instance (QA a) => QA [a] where-  type Rep [a] = [Rep a]-  toExp as = ListE (P.map toExp as)-  frExp (ListE as) = P.map frExp as-  frExp _ = $impossible--instance (QA a) => QA (Maybe a) where-  type Rep (Maybe a) = [Rep a]-  toExp Nothing = ListE []-  toExp (Just a) = ListE [toExp a]-  frExp (ListE []) = Nothing-  frExp (ListE (a : _)) = Just (frExp a)-  frExp _ = $impossible--instance (QA a,QA b) => QA (Either a b) where-  type Rep (Either a b) = ([Rep a],[Rep b])-  toExp (Left a) = PairE (ListE [toExp a]) (ListE [])-  toExp (Right b) = PairE (ListE []) (ListE [toExp b])-  frExp (PairE (ListE (a : _)) _) = Left (frExp a)-  frExp (PairE _ (ListE (a : _))) = Right (frExp a)-  frExp _ = $impossible---- Elim instances--instance (QA r) => Elim () r where-  type Eliminator () r = Q r -> Q r-  elim _ r = r--instance (QA r) => Elim Bool r where-  type Eliminator Bool r = Q r -> Q r -> Q r-  elim (Q e) (Q e1) (Q e2) = Q (AppE Cond (PairE e (PairE e1 e2)))--instance (QA r) => Elim Char r where-  type Eliminator Char r = (Q Char -> Q r) -> Q r-  elim q f = f q--instance (QA r) => Elim Integer r where-  type Eliminator Integer r = (Q Integer -> Q r) -> Q r-  elim q f = f q--instance (QA r) => Elim Double r where-  type Eliminator Double r = (Q Double -> Q r) -> Q r-  elim q f = f q--instance (QA r) => Elim Text r where-  type Eliminator Text r = (Q Text -> Q r) -> Q r-  elim q f = f q--instance (QA a,QA b,QA r) => Elim (a,b) r where-  type Eliminator (a,b) r = (Q a -> Q b -> Q r) -> Q r-  elim q f = f (fst q) (snd q)--instance (QA a,QA r) => Elim (Maybe a) r where-  type Eliminator (Maybe a) r = Q r -> (Q a -> Q r) -> Q r-  elim q r f = maybe r f q--instance (QA a,QA b,QA r) => Elim (Either a b) r where-  type Eliminator (Either a b) r = (Q a -> Q r) -> (Q b -> Q r) -> Q r-  elim q f g = either f g q---- BasicType instances--instance BasicType () where-instance BasicType Bool where-instance BasicType Char where-instance BasicType Integer where-instance BasicType Double where-instance BasicType Text where---- TA instances--instance TA () where-instance TA Bool where-instance TA Char where-instance TA Integer where-instance TA Double where-instance TA Text where-instance (BasicType a, BasicType b) => TA (a,b) where-instance (BasicType a, BasicType b, BasicType c) => TA (a,b,c) where---- Num and Fractional instances--instance Num (Exp Integer) where-  (+) e1 e2 = AppE Add (PairE e1 e2)-  (*) e1 e2 = AppE Mul (PairE e1 e2)-  (-) e1 e2 = AppE Sub (PairE e1 e2)--  fromInteger = IntegerE--  abs e = let c = AppE Lt (PairE e 0)-          in  AppE Cond (PairE c (PairE (negate e) e))--  signum e = let c1 = AppE Lt  (PairE e 0)-                 c2 = AppE Equ (PairE e 0)-                 e' = AppE Cond (PairE c2 (PairE 0 1))-             in  AppE Cond (PairE c1 (PairE (-1) e'))--instance Num (Exp Double) where-  (+) e1 e2 = AppE Add (PairE e1 e2)-  (*) e1 e2 = AppE Mul (PairE e1 e2)-  (-) e1 e2 = AppE Sub (PairE e1 e2)--  fromInteger = DoubleE . fromInteger--  abs e = let c = AppE Lt (PairE e 0)-          in  AppE Cond (PairE c (PairE (negate e) e))--  signum e = let c1 = AppE Lt  (PairE e 0.0)-                 c2 = AppE Equ (PairE e 0.0)-                 e' = AppE Cond (PairE c2 (PairE 0 1))-             in  AppE Cond (PairE c1 (PairE (-1) e'))--instance Fractional (Exp Double) where-  (/) e1 e2    = AppE Div (PairE e1 e2)-  fromRational = DoubleE . fromRational--instance Num (Q Integer) where-  (+) (Q e1) (Q e2) = Q (e1 + e2)-  (*) (Q e1) (Q e2) = Q (e1 * e2)-  (-) (Q e1) (Q e2) = Q (e1 - e2)-  fromInteger       = Q . IntegerE-  abs (Q e)         = Q (abs e)-  signum (Q e)      = Q (signum e)--instance Num (Q Double) where-  (+) (Q e1) (Q e2) = Q (e1 + e2)-  (*) (Q e1) (Q e2) = Q (e1 * e2)-  (-) (Q e1) (Q e2) = Q (e1 - e2)-  fromInteger       = Q . DoubleE . fromInteger-  abs (Q e)         = Q (abs e)-  signum (Q e)      = Q (signum e)--instance Fractional (Q Double) where-  (/) (Q e1) (Q e2) = Q (e1 / e2)-  fromRational = Q . DoubleE . fromRational---- View instances--instance View (Q ()) where-  type ToView (Q ()) = Q ()-  view = id--instance View (Q Bool) where-  type ToView (Q Bool) = Q Bool-  view = id--instance View (Q Char) where-  type ToView (Q Char) = Q Char-  view = id--instance View (Q Integer) where-  type ToView (Q Integer) = Q Integer-  view = id--instance View (Q Double) where-  type ToView (Q Double) = Q Double-  view = id--instance View (Q Text) where-  type ToView (Q Text) = Q Text-  view = id--instance (QA a, QA b) => View (Q (a,b)) where-  type ToView (Q (a,b)) = (Q a,Q b)-  view (Q e) = (Q (AppE Fst e),Q (AppE Snd e))--instance (QA a,QA b,QA c) => View (Q (a,b,c)) where-  type ToView (Q (a,b,c)) = (Q a,Q b,Q c)-  view (Q e) = (Q (AppE Fst e),Q (AppE Fst (AppE Snd e)),Q (AppE Snd (AppE Snd e)))---- IsString instances--instance IsString (Q Text) where-  fromString = Q . TextE . T.pack---- * Referring to persistent tables--table :: (QA a, TA a) => String -> Q [a]-table name = Q (TableE (TableDB name []))--tableDB :: (QA a, TA a) => String -> Q [a]-tableDB name = Q (TableE (TableDB name []))--tableWithKeys :: (QA a, TA a) => String -> [[String]] -> Q [a]-tableWithKeys name keys = Q (TableE (TableDB name keys))--tableCSV :: (QA a, TA a) => String -> Q [a]-tableCSV filename = Q (TableE (TableCSV filename))---- * toQ--toQ :: (QA a) => a -> Q a-toQ = Q . toExp---- * Unit--unit :: Q ()-unit = Q UnitE---- * Boolean logic--false :: Q Bool-false = Q (BoolE False)--true :: Q Bool-true = Q (BoolE True)--not :: Q Bool -> Q Bool-not (Q e) = Q (AppE Not e)--(&&) :: Q Bool -> Q Bool -> Q Bool-(&&) (Q a) (Q b) = Q (AppE Conj (PairE a b))--(||) :: Q Bool -> Q Bool -> Q Bool-(||) (Q a) (Q b) = Q (AppE Disj (PairE a b))---- * Equality and Ordering--eq :: (QA a,Eq a) => Q a -> Q a -> Q Bool-eq (Q a) (Q b) = Q (AppE Equ (PairE a b))--(==) :: (QA a,Eq a) => Q a -> Q a -> Q Bool-(==) = eq--neq :: (QA a,Eq a) => Q a -> Q a -> Q Bool-neq a b = not (eq a b)--(/=) :: (QA a,Eq a) => Q a -> Q a -> Q Bool-(/=) = neq--lt :: (QA a,Ord a) => Q a -> Q a -> Q Bool-lt (Q a) (Q b) = Q (AppE Lt (PairE a b))--(<) :: (QA a,Ord a) => Q a -> Q a -> Q Bool-(<) = lt--lte :: (QA a,Ord a) => Q a -> Q a -> Q Bool-lte (Q a) (Q b) = Q (AppE Lte (PairE a b))--(<=) :: (QA a,Ord a) => Q a -> Q a -> Q Bool-(<=) = lte--gte :: (QA a,Ord a) => Q a -> Q a -> Q Bool-gte (Q a) (Q b) = Q (AppE Gte (PairE a b))--(>=) :: (QA a,Ord a) => Q a -> Q a -> Q Bool-(>=) = gte--gt :: (QA a,Ord a) => Q a -> Q a -> Q Bool-gt (Q a) (Q b) = Q (AppE Gt (PairE a b))--(>) :: (QA a,Ord a) => Q a -> Q a -> Q Bool-(>) = gt--min :: (QA a,Ord a) => Q a -> Q a -> Q a-min (Q a) (Q b) = Q (AppE Min (PairE a b))--max :: (QA a,Ord a) => Q a -> Q a -> Q a-max (Q a) (Q b) = Q (AppE Max (PairE a b))---- * Conditionals--bool :: (QA a) => Q a -> Q a -> Q Bool -> Q a-bool f t b = cond b t f--cond :: (QA a) => Q Bool -> Q a -> Q a -> Q a-cond (Q c) (Q a) (Q b) = Q (AppE Cond (PairE c (PairE a b)))--ifThenElse :: (QA a) => Q Bool -> Q a -> Q a -> Q a-ifThenElse = cond--(?) :: (QA a) => Q Bool -> (Q a,Q a) -> Q a-(?) c (a,b) = cond c a b---- * Maybe--listToMaybe :: (QA a) => Q [a] -> Q (Maybe a)-listToMaybe (Q as) = Q as--maybeToList :: (QA a) => Q (Maybe a) -> Q [a]-maybeToList (Q ma) = Q ma--nothing :: (QA a) => Q (Maybe a)-nothing = listToMaybe nil--just :: (QA a) => Q a -> Q (Maybe a)-just a = listToMaybe (singleton a)--isNothing :: (QA a) => Q (Maybe a) -> Q Bool-isNothing ma = null (maybeToList ma)--isJust :: (QA a) => Q (Maybe a) -> Q Bool-isJust ma = not (isNothing ma)--fromJust :: (QA a) => Q (Maybe a) -> Q a-fromJust ma = head (maybeToList ma)--maybe :: (QA a,QA b) => Q b -> (Q a -> Q b) -> Q (Maybe a) -> Q b-maybe b f ma = isNothing ma ? (b,f (fromJust ma))--fromMaybe :: (QA a) => Q a -> Q (Maybe a) -> Q a-fromMaybe a ma = isNothing ma ? (a,fromJust ma)--catMaybes :: (QA a) => Q [Maybe a] -> Q [a]-catMaybes = concatMap maybeToList--mapMaybe :: (QA a,QA b) => (Q a -> Q (Maybe b)) -> Q [a] -> Q [b]-mapMaybe f = concatMap (maybeToList . f)---- * Either--pairToEither :: (QA a,QA b) => Q ([a],[b]) -> Q (Either a b)-pairToEither (Q a) = Q a--eitherToPair :: (QA a,QA b) => Q (Either a b) -> Q ([a],[b])-eitherToPair (Q a) = Q a--left :: (QA a,QA b) => Q a -> Q (Either a b)-left a = pairToEither (pair (singleton a) nil)--right :: (QA a,QA b) => Q b -> Q (Either a b)-right a = pairToEither (pair nil (singleton a))--isLeft :: (QA a,QA b) => Q (Either a b) -> Q Bool-isLeft = null . snd . eitherToPair--isRight :: (QA a,QA b) => Q (Either a b) -> Q Bool-isRight = null . fst . eitherToPair--either :: (QA a,QA b,QA c) => (Q a -> Q c) -> (Q b -> Q c) -> Q (Either a b) -> Q c-either lf rf e =-  let p = eitherToPair e-  in  head (map lf (fst p) ++ map rf (snd p))--lefts :: (QA a,QA b) => Q [Either a b] -> Q [a]-lefts = concatMap (fst . eitherToPair)--rights :: (QA a,QA b) => Q [Either a b] -> Q [b]-rights = concatMap (snd . eitherToPair)--partitionEithers :: (QA a,QA b) => Q [Either a b] -> Q ([a], [b])-partitionEithers es = pair (lefts es) (rights es)---- * List Construction--nil :: (QA a) => Q [a]-nil = Q (ListE [])--empty :: (QA a) => Q [a]-empty = nil--cons :: (QA a) => Q a -> Q [a] -> Q [a]-cons (Q a) (Q as) = Q (AppE Cons (PairE a as))--(<|) :: (QA a) => Q a -> Q [a] -> Q [a]-(<|) = cons--snoc :: (QA a) => Q [a] -> Q a -> Q [a]-snoc as a = append as (singleton a)--(|>) :: (QA a) => Q [a] -> Q a -> Q [a]-(|>) = snoc--singleton :: (QA a) => Q a -> Q [a]-singleton (Q e) = cons (Q e) nil---- * List Operations--head :: (QA a) => Q [a] -> Q a-head (Q as) = Q (AppE Head as)--tail :: (QA a) => Q [a] -> Q [a]-tail (Q as) = Q (AppE Tail as)--take :: (QA a) => Q Integer -> Q [a] -> Q [a]-take (Q i) (Q as) = Q (AppE Take (PairE i as))--drop :: (QA a) => Q Integer -> Q [a] -> Q [a]-drop (Q i) (Q as) = Q (AppE Drop (PairE i as))--map :: (QA a,QA b) => (Q a -> Q b) ->  Q [a] -> Q [b]-map f (Q as) = Q (AppE Map (PairE (LamE (toLam f)) as))--append :: (QA a) => Q [a] -> Q [a] -> Q [a]-append (Q as) (Q bs) = Q (AppE Concat (ListE [as,bs]))--(++) :: (QA a) => Q [a] -> Q [a] -> Q [a]-(++) = append--filter :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]-filter f (Q as) = Q (AppE Filter (PairE (LamE (toLam f)) as))--groupWithKey :: (QA a,QA b,Ord b) => (Q a -> Q b) -> Q [a] -> Q [(b,[a])]-groupWithKey f (Q as) = Q (AppE GroupWithKey (PairE (LamE (toLam f)) as))--groupWith :: (QA a,QA b,Ord b) => (Q a -> Q b) -> Q [a] -> Q [[a]]-groupWith f as = map snd (groupWithKey f as)--sortWith :: (QA a,QA b,Ord b) => (Q a -> Q b) -> Q [a] -> Q [a]-sortWith f (Q as) = Q (AppE SortWith (PairE (LamE (toLam f)) as))--last :: (QA a) => Q [a] -> Q a-last (Q as) = Q (AppE Last as)--init :: (QA a) => Q [a] -> Q [a]-init (Q as) = Q (AppE Init as)--null :: (QA a) => Q [a] -> Q Bool-null (Q as) = Q (AppE Null as)--length :: (QA a) => Q [a] -> Q Integer-length (Q as) = Q (AppE Length as)--index :: (QA a) => Q [a] -> Q Integer -> Q a-index (Q as) (Q i) = Q (AppE Index (PairE as i))--(!!) :: (QA a) => Q [a] -> Q Integer -> Q a-(!!) = index--reverse :: (QA a) => Q [a] -> Q [a]-reverse (Q as) = Q (AppE Reverse as)---- * Special folds--and :: Q [Bool] -> Q Bool-and (Q bs) = Q (AppE And bs)--or :: Q [Bool] -> Q Bool-or (Q bs) = Q (AppE Or bs)--any :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q Bool-any f = or . map f--all :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q Bool-all f = and . map f--sum :: (QA a,Num a) => Q [a] -> Q a-sum (Q as) = Q (AppE Sum as)--concat :: (QA a) => Q [[a]] -> Q [a]-concat (Q ass) = Q (AppE Concat ass)--concatMap :: (QA a,QA b) => (Q a -> Q [b]) -> Q [a] -> Q [b]-concatMap f as = concat (map f as)--maximum :: (QA a,Ord a) => Q [a] -> Q a-maximum (Q as) = Q (AppE Maximum as)--minimum :: (QA a,Ord a) => Q [a] -> Q a-minimum (Q as) = Q (AppE Minimum as)---- * Sublists--splitAt :: (QA a) => Q Integer -> Q [a] -> Q ([a],[a])-splitAt (Q i) (Q as) = Q (AppE SplitAt (PairE i as))--takeWhile :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]-takeWhile f (Q as) = Q (AppE TakeWhile (PairE (LamE (toLam f)) as))--dropWhile :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]-dropWhile f (Q as) = Q (AppE DropWhile (PairE (LamE (toLam f)) as))--span :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q ([a],[a])-span f as = pair (takeWhile f as) (dropWhile f as)--break :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q ([a],[a])-break f = span (not . f)---- * Searching Lists--elem :: (QA a,Eq a) => Q a -> Q [a] -> Q Bool-elem a as = null (filter (a ==) as) ? (false,true)--notElem :: (QA a,Eq a) => Q a -> Q [a] -> Q Bool-notElem a as = not (a `elem` as)--lookup :: (QA a,QA b,Eq a) => Q a -> Q [(a, b)] -> Q (Maybe b)-lookup a  = listToMaybe . map snd . filter ((a ==) . fst)---- * Zipping and Unzipping Lists--zip :: (QA a,QA b) => Q [a] -> Q [b] -> Q [(a,b)]-zip (Q as) (Q bs) = Q (AppE Zip (PairE as bs))--zipWith :: (QA a,QA b,QA c) => (Q a -> Q b -> Q c) -> Q [a] -> Q [b] -> Q [c]-zipWith f as bs = map (\e -> f (fst e) (snd e)) (zip as bs)--unzip :: (QA a,QA b) => Q [(a,b)] -> Q ([a],[b])-unzip as = pair (map fst as) (map snd as)--zip3 :: (QA a,QA b,QA c) => Q [a] -> Q [b] -> Q [c] -> Q [(a,b,c)]-zip3 as bs cs = map (\abc -> triple (fst abc) (fst (snd abc)) (snd (snd abc))) (zip as (zip bs cs))--zipWith3 :: (QA a,QA b,QA c,QA d) => (Q a -> Q b -> Q c -> Q d) -> Q [a] -> Q [b] -> Q [c] -> Q [d]-zipWith3 f as bs cs = map (\e -> (case view e of (a,b,c) -> f a b c))-                          (zip3 as bs cs)--unzip3 :: (QA a,QA b,QA c) => Q [(a,b,c)] -> Q ([a],[b],[c])-unzip3 abcs = triple (map (\e -> (case view e of (a,_,_) -> a)) abcs)-                     (map (\e -> (case view e of (_,b,_) -> b)) abcs)-                     (map (\e -> (case view e of (_,_,c) -> c)) abcs)---- * Set-oriented operations--nub :: (QA a,Eq a) => Q [a] -> Q [a]-nub (Q as) = Q (AppE Nub as)---- * Tuple Projection Functions--fst :: (QA a,QA b) => Q (a,b) -> Q a-fst (Q e) = Q (AppE Fst e)--snd :: (QA a,QA b) => Q (a,b) -> Q b-snd (Q e) = Q (AppE Snd e)---- * Conversions between numeric types--integerToDouble :: Q Integer -> Q Double-integerToDouble (Q i) = Q (AppE IntegerToDouble i)---- * Rebind Monadic Combinators--return :: (QA a) => Q a -> Q [a]-return = singleton--(>>=) :: (QA a,QA b) => Q [a] -> (Q a -> Q [b]) -> Q [b]-(>>=) ma f = concatMap f ma--(>>) :: (QA a,QA b) => Q [a] -> Q [b] -> Q [b]-(>>) ma mb = concatMap (\_ -> mb) ma--mzip :: (QA a,QA b) => Q [a] -> Q [b] -> Q [(a,b)]-mzip = zip--guard :: Q Bool -> Q [()]-guard c = cond c (singleton unit) nil---- * Construction of tuples--pair :: (QA a,QA b) => Q a -> Q b -> Q (a,b)-pair (Q a) (Q b) = Q (PairE a b)--triple :: (QA a,QA b,QA c) => Q a -> Q b -> Q c -> Q (a,b,c)-triple (Q a) (Q b) (Q c)= Q (PairE a (PairE b c))--infixl 9  !!-infixr 5  ++, <|, |>-infix  4  ==, /=, <, <=, >=, >-infixr 3  &&-infixr 2  ||-infix  0  ?--deriveTupleRangeQA                4 7-deriveTupleRangeTA                4 7-deriveTupleRangeView              4 7-deriveTupleRangeSmartConstructors 2 7---- * Missing functions---- $missing-{- $missing--This module offers most of the functions on lists given in PreludeList for the-'Q' type. Missing functions are:--General folds:--> foldl-> foldl1-> scanl-> scanl1-> foldr-> foldr1-> scanr-> scanr1--Infinit lists:--> iterate-> repeat-> cycle--String functions:--> lines-> words-> unlines-> unwords---}
+ src/Database/DSH/FKL/Kure.hs view
@@ -0,0 +1,446 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE InstanceSigs          #-}+{-# LANGUAGE FlexibleContexts      #-}++-- | Infrastructure for KURE-based rewrites on FKL expressions+module Database.DSH.FKL.Kure+    ( -- * Re-export relevant KURE modules+      module Language.KURE+    , module Language.KURE.Lens++      -- * The KURE monad+    , RewriteM, RewriteStateM, TransformF, RewriteF, LensF+    +      -- * Setters and getters for the translation state+    , get, put, modify, initialCtx+    +      -- * Changing between stateful and non-stateful transforms+    , statefulT, liftstateT++      -- * The KURE context+    , FlatCtx(..), CrumbF(..), PathF++      -- * Universes+    , FKL(..)++      -- * Congruence combinators+    , tableT, papp1T, papp2T, papp3T, binopT, unopT+    , ifT, constExprT, varT, letT++    , tableR, papp1R, papp2R, papp3R, binopR, unopR+    , ifR, constExprR, varR, letR++    , inScopeNames, freeIn, boundIn, freshNameT+    +    ) where+    +       +import           Control.Monad+import           Data.Monoid++import           Language.KURE+import           Language.KURE.Lens+       +import           Database.DSH.Common.RewriteM+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Lang+import           Database.DSH.Common.Type+import           Database.DSH.Common.Pretty+import           Database.DSH.FKL.Lang+                 +--------------------------------------------------------------------------------+-- Convenience type aliases++type TransformF a b = Transform FlatCtx (RewriteM Int) a b+type RewriteF a     = TransformF a a+type LensF a b      = Lens FlatCtx (RewriteM Int) a b++--------------------------------------------------------------------------------++data CrumbF = AppFun+            | PApp1Arg+            | PApp2Arg1+            | PApp2Arg2+            | PApp3Arg1+            | PApp3Arg2+            | PApp3Arg3+            | BinOpArg1+            | BinOpArg2+            | UnOpArg+            | IfCond+            | IfThen+            | IfElse+            | ImprintArg1+            | ImprintArg2+            | ForgetArg+            | BroadcastArg1+            | BroadcastArg2+            | BroadcastLArg1+            | BroadcastLArg2+            | LetBind+            | LetBody+            | TupleElem Int+            | ExtExpr+            deriving (Eq, Show)++type AbsPathF = AbsolutePath CrumbF++type PathF = Path CrumbF++-- | The context for KURE-based FKL rewrites+data FlatCtx = FlatCtx { fkl_path     :: AbsPathF+                       , fkl_bindings :: [Ident]+                       }+                       +instance ExtendPath FlatCtx CrumbF where+    c@@n = c { fkl_path = fkl_path c @@ n }+    +instance ReadPath FlatCtx CrumbF where+    absPath c = fkl_path c++initialCtx :: FlatCtx+initialCtx = FlatCtx { fkl_path = mempty, fkl_bindings = [] }++-- | Record a variable binding in the context+bindVar :: Ident -> FlatCtx -> FlatCtx+bindVar n ctx = ctx { fkl_bindings = n : fkl_bindings ctx }++inScopeNames :: FlatCtx -> [Ident]+inScopeNames = fkl_bindings++boundIn :: Ident -> FlatCtx -> Bool+boundIn n ctx = n `elem` (fkl_bindings ctx)++freeIn :: Ident -> FlatCtx -> Bool+freeIn n ctx = n `notElem` (fkl_bindings ctx)++-- | Generate a fresh name that is not bound in the current context.+freshNameT :: [Ident] -> TransformF a Ident+freshNameT avoidNames = do+    ctx <- contextT+    constT $ freshName (avoidNames ++ inScopeNames ctx)++--------------------------------------------------------------------------------+-- Support for stateful transforms++-- | Run a stateful transform with an initial state and turn it into a regular+-- (non-stateful) transform+statefulT :: s -> Transform FlatCtx (RewriteStateM s) a b -> TransformF a (s, b)+statefulT s t = resultT (stateful s) t++-- | Turn a regular rewrite into a stateful rewrite+liftstateT :: Transform FlatCtx (RewriteM Int) a b -> Transform FlatCtx (RewriteStateM s) a b+liftstateT t = resultT liftstate t++--------------------------------------------------------------------------------+-- Congruence combinators for FKL lexpressions++tableT :: Monad m => (Type -> String -> [Column] -> TableHints -> b)+                  -> Transform FlatCtx m (ExprTempl l e) b+tableT f = contextfreeT $ \expr -> case expr of+                      Table ty n cs ks -> return $ f ty n cs ks+                      _                -> fail "not a table node"+{-# INLINE tableT #-}                      ++                      +tableR :: Monad m => Rewrite FlatCtx m (ExprTempl l e)+tableR = tableT Table+{-# INLINE tableR #-}++ifT :: Monad m => Transform FlatCtx m (ExprTempl l e) a1+               -> Transform FlatCtx m (ExprTempl l e) a2+               -> Transform FlatCtx m (ExprTempl l e) a3+               -> (Type -> a1 -> a2 -> a3 -> b)+               -> Transform FlatCtx m (ExprTempl l e) b+ifT t1 t2 t3 f = transform $ \c expr -> case expr of+                    If ty e1 e2 e3 -> f ty <$> applyT t1 (c@@IfCond) e1               +                                           <*> applyT t2 (c@@IfThen) e2+                                           <*> applyT t3 (c@@IfElse) e3+                    _              -> fail "not an if expression"+{-# INLINE ifT #-}                      +                    +ifR :: Monad m => Rewrite FlatCtx m (ExprTempl l e)+               -> Rewrite FlatCtx m (ExprTempl l e)+               -> Rewrite FlatCtx m (ExprTempl l e)+               -> Rewrite FlatCtx m (ExprTempl l e)+ifR t1 t2 t3 = ifT t1 t2 t3 If               +{-# INLINE ifR #-}                      ++{- FIXME will be needed again when let-bindings are added.+varT :: Monad m => (Type -> Ident -> b) -> Transform FlatCtx m (Expr l) b+varT f = contextfreeT $ \expr -> case expr of+                    Var ty n -> return $ f ty n+                    _        -> fail "not a variable"+{-# INLINE varT #-}                      +                    +varR :: Monad m => Rewrite FlatCtx m (Expr l)+varR = varT Var+{-# INLINE varR #-}                      +-}++binopT :: Monad m => Transform FlatCtx m (ExprTempl l e) a1+                  -> Transform FlatCtx m (ExprTempl l e) a2+                  -> (Type -> ScalarBinOp -> l -> a1 -> a2 -> b)+                  -> Transform FlatCtx m (ExprTempl l e) b+binopT t1 t2 f = transform $ \c expr -> case expr of+                     BinOp ty op l e1 e2 -> f ty op l <$> applyT t1 (c@@BinOpArg1) e1 <*> applyT t2 (c@@BinOpArg2) e2+                     _                   -> fail "not a binary operator application"+{-# INLINE binopT #-}                      ++binopR :: Monad m => Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e)+binopR t1 t2 = binopT t1 t2 BinOp+{-# INLINE binopR #-}                      ++unopT :: Monad m => Transform FlatCtx m (ExprTempl l e) a+                 -> (Type -> ScalarUnOp -> l -> a -> b)+                 -> Transform FlatCtx m (ExprTempl l e) b+unopT t f = transform $ \ctx expr -> case expr of+                     UnOp ty op l e -> f ty op l <$> applyT t (ctx@@UnOpArg) e+                     _              -> fail "not an unary operator application"+{-# INLINE unopT #-}++unopR :: Monad m => Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e)+unopR t = unopT t UnOp+{-# INLINE unopR #-}+                     +papp1T :: Monad m => Transform FlatCtx m (ExprTempl l e) a+                  -> (Type -> Prim1 -> l -> a -> b)+                  -> Transform FlatCtx m (ExprTempl l e) b+papp1T t f = transform $ \c expr -> case expr of+                      PApp1 ty p l e -> f ty p l <$> applyT t (c@@PApp1Arg) e                  +                      _              -> fail "not a unary primitive application"+{-# INLINE papp1T #-}                      +                      +papp1R :: Monad m => Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e)+papp1R t = papp1T t PApp1+{-# INLINE papp1R #-}                      ++papp2T :: Monad m => Transform FlatCtx m (ExprTempl l e) a1+                  -> Transform FlatCtx m (ExprTempl l e) a2+                  -> (Type -> Prim2 -> l -> a1 -> a2 -> b)+                  -> Transform FlatCtx m (ExprTempl l e) b+papp2T t1 t2 f = transform $ \c expr -> case expr of+                     PApp2 ty p l e1 e2 -> f ty p l <$> applyT t1 (c@@PApp2Arg1) e1 <*> applyT t2 (c@@PApp2Arg2) e2+                     _                  -> fail "not a binary primitive application"+{-# INLINE papp2T #-}                      ++papp2R :: Monad m => Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e)+papp2R t1 t2 = papp2T t1 t2 PApp2+{-# INLINE papp2R #-}                      ++papp3T :: Monad m => Transform FlatCtx m (ExprTempl l e) a1+                  -> Transform FlatCtx m (ExprTempl l e) a2+                  -> Transform FlatCtx m (ExprTempl l e) a3+                  -> (Type -> Prim3 -> l -> a1 -> a2 -> a3 -> b)+                  -> Transform FlatCtx m (ExprTempl l e) b+papp3T t1 t2 t3 f = transform $ \c expr -> case expr of+                     PApp3 ty p l e1 e2 e3 -> f ty p l+                                              <$> applyT t1 (c@@PApp3Arg1) e1 +                                              <*> applyT t2 (c@@PApp3Arg2) e2+                                              <*> applyT t3 (c@@PApp3Arg3) e3+                     _                     -> fail "not a ternary primitive application"+{-# INLINE papp3T #-}                      ++papp3R :: Monad m +       => Rewrite FlatCtx m (ExprTempl l e) +       -> Rewrite FlatCtx m (ExprTempl l e) +       -> Rewrite FlatCtx m (ExprTempl l e) +       -> Rewrite FlatCtx m (ExprTempl l e)+papp3R t1 t2 t3 = papp3T t1 t2 t3 PApp3+{-# INLINE papp3R #-}                      ++constExprT :: Monad m => (Type -> Val -> b) -> Transform FlatCtx m (ExprTempl l e) b+constExprT f = contextfreeT $ \expr -> case expr of+                    Const ty v -> return $ f ty v+                    _          -> fail "not a constant"+{-# INLINE constExprT #-}                      +                    +constExprR :: Monad m => Rewrite FlatCtx m (ExprTempl l e)+constExprR = constExprT Const+{-# INLINE constExprR #-}                      ++letT :: Monad m => Transform FlatCtx m (ExprTempl l e) a1+                -> Transform FlatCtx m (ExprTempl l e) a2+                -> (Type -> Ident -> a1 -> a2 -> b) +                -> Transform FlatCtx m (ExprTempl l e) b+letT t1 t2 f = transform $ \c expr -> case expr of+                 Let ty x xs e -> f ty x <$> applyT t1 (c@@LetBind) xs +                                         <*> applyT t2 (bindVar x $ c@@LetBody) e+                 _             -> fail "not a let expression"+{-# INLINE letT #-}++letR :: Monad m => Rewrite FlatCtx m (ExprTempl l e) +                -> Rewrite FlatCtx m (ExprTempl l e) +                -> Rewrite FlatCtx m (ExprTempl l e)+letR r1 r2 = letT r1 r2 Let+{-# INLINE letR #-}++varT :: Monad m => (Type -> Ident -> b) -> Transform FlatCtx m (ExprTempl l e) b+varT f = contextfreeT $ \expr -> case expr of+             Var ty n -> return $ f ty n+             _        -> fail "not a variable"+{-# INLINE varT #-}++varR :: Monad m => Rewrite FlatCtx m (ExprTempl l e)+varR = varT Var+{-# INLINE varR #-}++mkTupleT :: Monad m => Transform FlatCtx m (ExprTempl l e) a+                    -> (Type -> l -> [a] -> b)+                    -> Transform FlatCtx m (ExprTempl l e) b+mkTupleT t f = transform $ \c expr -> case expr of+                   MkTuple ty l es -> f ty l <$> zipWithM (\e i -> applyT t (c@@TupleElem i) e) es [1..]+                   _               -> fail "not a tuple constructor"+{-# INLINE mkTupleT #-}++mkTupleR :: Monad m => Rewrite FlatCtx m (ExprTempl l e) -> Rewrite FlatCtx m (ExprTempl l e)+mkTupleR r = mkTupleT r MkTuple++extT :: Monad m => Transform FlatCtx m e a+                -> (a -> b)+                -> Transform FlatCtx m (ExprTempl l e) b+extT t f = transform $ \c expr -> case expr of+    Ext e -> f <$> applyT t (c@@ExtExpr) e                  +    _     -> fail "not an extension mode"+{-# INLINE extT #-}+++extR :: Monad m => Rewrite FlatCtx m e -> Rewrite FlatCtx m (ExprTempl l e)+extR r = extT r Ext+{-# INLINE extR #-}++--------------------------------------------------------------------------------++forgetT :: Monad m => Transform FlatCtx m FExpr a+                   -> (Nat -> Type -> a -> b)+                   -> Transform FlatCtx m ShapeExt b+forgetT t f = transform $ \c expr -> case expr of+                        Forget n ty e -> f n ty <$> applyT t (c@@ForgetArg) e                  +                        _             -> fail "not a forget application"+{-# INLINE forgetT #-}                      +                      +forgetR :: Monad m => Rewrite FlatCtx m FExpr -> Rewrite FlatCtx m ShapeExt +forgetR t = forgetT t Forget+{-# INLINE forgetR #-}                      ++imprintT :: Monad m => Transform FlatCtx m FExpr a1+                    -> Transform FlatCtx m FExpr a2+                    -> (Nat -> Type -> a1 -> a2 -> b)+                    -> Transform FlatCtx m ShapeExt b+imprintT t1 t2 f = transform $ \c expr -> case expr of+                     Imprint n ty e1 e2 -> f n ty <$> applyT t1 (c@@ImprintArg1) e1 +                                                  <*> applyT t2 (c@@ImprintArg2) e2+                     _                  -> fail "not a imprint call"+{-# INLINE imprintT #-}                      ++imprintR :: Monad m => Rewrite FlatCtx m FExpr -> Rewrite FlatCtx m FExpr -> Rewrite FlatCtx m ShapeExt +imprintR t1 t2 = imprintT t1 t2 Imprint+{-# INLINE imprintR #-}                      ++--------------------------------------------------------------------------------++broadcastT :: Monad m => Transform FlatCtx m LExpr a1+                      -> Transform FlatCtx m LExpr a2+                      -> (Nat -> Type -> a1 -> a2 -> b)+                      -> Transform FlatCtx m BroadcastExt b+broadcastT t1 t2 f = transform $ \c expr -> case expr of+    Broadcast n ty e1 e2 -> f n ty <$> applyT t1 (c@@BroadcastArg1) e1+                                   <*> applyT t2 (c@@BroadcastArg2) e2+{-# INLINE broadcastT #-}++broadcastR :: Monad m => Rewrite FlatCtx m LExpr +                      -> Rewrite FlatCtx m LExpr +                      -> Rewrite FlatCtx m BroadcastExt+broadcastR r1 r2 = broadcastT r1 r2 Broadcast+{-# INLINE broadcastR #-}++--------------------------------------------------------------------------------++data FKL l e = ExprFKL (ExprTempl l e)+             | ExtFKL e++instance (Pretty e, Pretty l) => Pretty (FKL l e) where+    pretty (ExprFKL e) = pretty e+    pretty (ExtFKL o)  = pretty o++instance Injection FExpr (FKL Lifted ShapeExt) where+    inject              = ExprFKL++    project (ExprFKL e) = Just e+    project _           = Nothing++instance Injection ShapeExt (FKL Lifted ShapeExt) where+    inject             = ExtFKL++    project (ExtFKL s) = Just s+    project _          = Nothing++--------------------------------------------------------------------------------++instance Injection LExpr (FKL LiftedN BroadcastExt) where+    inject              = ExprFKL++    project (ExprFKL e) = Just e+    project _           = Nothing++instance Injection BroadcastExt (FKL LiftedN BroadcastExt) where+    inject             = ExtFKL++    project (ExtFKL s) = Just s+    project _          = Nothing+   ++--------------------------------------------------------------------------------++instance Walker FlatCtx (FKL Lifted ShapeExt) where+    allR r = +        rewrite $ \c fkl -> case fkl of+            ExprFKL expr -> inject <$> applyT (allRExpr r) c expr+            ExtFKL o     -> inject <$> applyT allRShape c o++      where+        allRShape = readerT $ \o -> case o of+                Imprint{} -> imprintR (extractR r) (extractR r)+                Forget{}  -> forgetR (extractR r)++instance Walker FlatCtx (FKL LiftedN BroadcastExt) where+    allR r = +        rewrite $ \c fkl -> case fkl of+            ExprFKL expr -> inject <$> applyT (allRExpr r) c expr+            ExtFKL o     -> inject <$> applyT allRBC c o++      where+        allRBC = readerT $ \o -> case o of+                Broadcast{}  -> broadcastR (extractR r) (extractR r)++allRExpr :: (Injection (ExprTempl t t1) g, Injection t1 g, Monad m)+         => Rewrite FlatCtx m g+         -> Transform FlatCtx m (ExprTempl t t1) (ExprTempl t t1)+allRExpr r = readerT $ \e -> case e of+        Table{}       -> idR+        PApp1{}       -> papp1R (extractR r)+        PApp2{}       -> papp2R (extractR r) (extractR r)+        PApp3{}       -> papp3R (extractR r) (extractR r) (extractR r)+        BinOp{}       -> binopR (extractR r) (extractR r)+        UnOp{}        -> unopR (extractR r)+        If{}          -> ifR (extractR r) (extractR r) (extractR r)+        Const{}       -> idR+        Let{}         -> letR (extractR r) (extractR r)+        Var{}         -> idR+        MkTuple{}     -> mkTupleR (extractR r)+        Ext{}         -> extR (extractR r)+++--------------------------------------------------------------------------------+-- I find it annoying that Applicative is not a superclass of Monad.++(<$>) :: Monad m => (a -> b) -> m a -> m b+(<$>) = liftM+{-# INLINE (<$>) #-}++(<*>) :: Monad m => m (a -> b) -> m a -> m b+(<*>) = ap+{-# INLINE (<*>) #-}+
+ src/Database/DSH/FKL/Lang.hs view
@@ -0,0 +1,242 @@+{-# LANGUAGE FlexibleInstances  #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TemplateHaskell    #-}++module Database.DSH.FKL.Lang where++import           Text.PrettyPrint.ANSI.Leijen+import           Text.Printf++import           Database.DSH.Impossible+import           Database.DSH.Common.Pretty+import           Database.DSH.Common.Nat+import qualified Database.DSH.Common.Lang   as L+import           Database.DSH.Common.Type   (Type, Typed, typeOf)+++-- | 'LiftedN' defines an FKL dialect in which primitives and+-- operators might be lifted to arbitrary levels.+data LiftedN = LiftedN Nat deriving (Show)++-- | 'Lifted' defines an FKL dialect in which primitives and operators+-- occur either unlifted or lifted once.+data Lifted = Lifted | NotLifted deriving (Show)++-- | 'FExpr' is the target language of the flattening transformation.+data ExprTempl l e = Table Type String [L.Column] L.TableHints+                   | PApp1 Type Prim1 l (ExprTempl l e)+                   | PApp2 Type Prim2 l (ExprTempl l e) (ExprTempl l e)+                   | PApp3 Type Prim3 l (ExprTempl l e) (ExprTempl l e) (ExprTempl l e)+                   | If Type (ExprTempl l e) (ExprTempl l e) (ExprTempl l e)+                   | BinOp Type L.ScalarBinOp l (ExprTempl l e) (ExprTempl l e)+                   | UnOp Type L.ScalarUnOp l (ExprTempl l e)+                   | Const Type L.Val+                   | Ext e+                   | Let Type L.Ident (ExprTempl l e) (ExprTempl l e)+                   | Var Type L.Ident+                   | MkTuple Type l [(ExprTempl l e)]++data BroadcastExt = Broadcast Nat Type LExpr LExpr++data ShapeExt = Forget Nat Type FExpr+              | Imprint Nat Type FExpr FExpr++type FExpr = ExprTempl Lifted ShapeExt+type LExpr = ExprTempl LiftedN BroadcastExt++-- | Forget does not unsegment the vector. That is: the descriptor+-- might not be normalized and segment descriptors other than 1 might+-- occur. This is propably ok when we know that a concated vector will+-- be unconcated again. We know this statically when introducing+-- concat/unconcat for higher-lifted primitives.++data Prim1 = Length+           | Concat+           | TupElem TupleIndex+           | Sum+           | Avg+           | Minimum+           | Maximum+           | The+           | Tail+           | Reverse+           | And+           | Or+           | Init+           | Last+           | Nub+           | Number+           | Singleton+           | Transpose+           | Reshape Integer+    deriving (Show, Eq)++data Prim2 = Group+           | Sort+           | Restrict+           | Append+           | Index+           | Zip+           | CartProduct+           | NestProduct+           | ThetaJoin (L.JoinPredicate L.JoinExpr)+           | NestJoin (L.JoinPredicate L.JoinExpr)+           | SemiJoin (L.JoinPredicate L.JoinExpr)+           | AntiJoin (L.JoinPredicate L.JoinExpr)+           | Dist+           deriving (Show, Eq)++data Prim3 = Combine+    deriving (Show, Eq)++instance Typed e => Typed (ExprTempl l e) where+    typeOf (Var t _)           = t+    typeOf (Let t _ _ _)       = t+    typeOf (Table t _ _ _)     = t+    typeOf (PApp1 t _ _ _)     = t+    typeOf (PApp2 t _ _ _ _)   = t+    typeOf (PApp3 t _ _ _ _ _) = t+    typeOf (If t _ _ _)        = t+    typeOf (BinOp t _ _ _ _)   = t+    typeOf (UnOp t _ _ _)      = t+    typeOf (Const t _)         = t+    typeOf (MkTuple t _ _)     = t+    typeOf (Ext o)             = typeOf o++instance Typed BroadcastExt where+    typeOf (Broadcast _ t _ _) = t++instance Typed ShapeExt where+    typeOf (Forget _ t _)    = t+    typeOf (Imprint _ t _ _) = t++--------------------------------------------------------------------------------+-- Pretty-printing of FKL dialects++superscript :: Int -> Doc+superscript 1 = char '¹'+superscript 2 = char '²'+superscript 3 = char '³'+superscript 4 = char '⁴'+superscript 5 = char '⁵'+superscript 6 = char '⁶'+superscript n = char '^' <> int n++instance Pretty Lifted where+    pretty Lifted    = text "ᴸ"+    pretty NotLifted = empty++instance Pretty LiftedN where+    pretty (LiftedN Zero) = empty+    pretty (LiftedN n)    = superscript (intFromNat n)++instance Pretty Prim1 where+    pretty Length       = text "length"+    pretty Concat       = text "concat"+    pretty Sum          = text "sum"+    pretty Avg          = text "avg"+    pretty The          = text "the"+    pretty Minimum      = text "minimum"+    pretty Maximum      = text "maximum"+    pretty Tail         = text "tail"+    pretty Reverse      = text "reverse"+    pretty And          = text "and"+    pretty Or           = text "or"+    pretty Init         = text "init"+    pretty Last         = text "last"+    pretty Nub          = text "nub"+    pretty Number       = text "number"+    pretty Transpose    = text "transpose"+    pretty (Reshape n)  = text $ printf "reshape(%d)" n+    pretty Singleton    = text "sng"+    pretty TupElem{}    = $impossible++instance Pretty Prim2 where+    pretty Group           = text "group"+    pretty Sort            = text "sort"+    pretty Dist            = text "dist"+    pretty Restrict        = text "restrict"+    pretty Append          = text "append"+    pretty Index           = text "index"+    pretty Zip             = text "zip"+    pretty CartProduct     = text "⨯"+    pretty NestProduct     = text "▽"+    pretty (ThetaJoin p)   = text $ printf "⨝_%s" (pp p)+    pretty (NestJoin p)    = text $ printf "△_%s" (pp p)+    pretty (SemiJoin p)    = text $ printf "⋉_%s" (pp p)+    pretty (AntiJoin p)    = text $ printf "▷_%s" (pp p)++instance Pretty Prim3 where+    pretty Combine = text "combine"++instance (Pretty l, Pretty e) => Pretty (ExprTempl l e) where+    pretty (MkTuple _ l es) = (tupled $ map pretty es) <> pretty l++    pretty (Var _ n) = text n+    pretty (Let _ x e1 e) = +        align $ text "let" <+> text x {- <> colon <> colon <> pretty (typeOf e1) -} <+> char '=' <+> pretty e1+                <$>+                text "in" <+> pretty e++    pretty (Table _ n _c _k) = text "table" <> parens (text n)++    pretty (PApp1 _ (TupElem n) l e1) = +        parenthize e1 <> dot <> int (tupleIndex n) <> pretty l++    pretty (PApp1 _ f l e1) =+        pretty f <> pretty l <+> (parenthize e1)++    pretty (PApp2 _ f l e1 e2) =+        pretty f <> pretty l <+> (align $ (parenthize e1) </> (parenthize e2))++    pretty (PApp3 _ f l e1 e2 e3) =+        pretty f <> pretty l+        <+> (align $ (parenthize e1) +                     </> (parenthize e2) +                     </> (parenthize e3))+    pretty (If _ e1 e2 e3) =+        let e1' = pretty e1+            e2' = pretty e2+            e3' = pretty e3+        in text "if" <+> e1'+           </> (nest 2 $ text "then" <+> e2')+           </> (nest 2 $ text "else" <+> e3')++    pretty (BinOp _ o l e1 e2) =+        align $ parenthize e1 </> pretty o <> pretty l </> parenthize e2++    pretty (UnOp _ o l e) =+        pretty o <> pretty l <> parens (pretty e)++    pretty (Const _ v) = pretty v++    pretty (Ext o) = pretty o++instance Pretty ShapeExt where+    pretty (Forget n _ e) = +        text "forget" +        <> (angles $ int $ intFromNat n)+        <+> (parenthize e)++    pretty (Imprint n _ e1 e2) = +        text "imprint" +        <> (angles $ int $ intFromNat n) +        <+> (align $ (parenthize e1) +                     </> (parenthize e2))+    +instance Pretty BroadcastExt where+    pretty (Broadcast n _ e1 e2) = +        text "forget" +        <> (angles $ int $ intFromNat n)+        <+> (align $ (parenthize e1)+                     </> (parenthize e2))++parenthize :: (Pretty l, Pretty e) => ExprTempl l e -> Doc+parenthize e =+    case e of+        Const{}                 -> pretty e+        Table{}                 -> pretty e+        Var{}                   -> pretty e+        PApp1 _ (TupElem _) _ _ -> pretty e+        _                       -> parens $ pretty e+
+ src/Database/DSH/FKL/Primitives.hs view
@@ -0,0 +1,258 @@+{-# LANGUAGE TemplateHaskell #-}++-- | Smart constructors for FKL functions and operators+module Database.DSH.FKL.Primitives where++import           Prelude                    hiding (concat, fst, snd)++import           Text.Printf++import           Database.DSH.Common.Lang+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Pretty+import           Database.DSH.Common.Type+import           Database.DSH.FKL.Lang+import           Database.DSH.Impossible++--------------------------------------------------------------------------------+-- Smart constructors for primitive combinators in the lifting FKL dialect++-- tranpose :: [[a]] -> [[a]]+transpose :: LExpr -> Nat -> LExpr+transpose e d =+    let t = unliftTypeN d $ typeOf e+    in PApp1 (liftTypeN d t) Transpose (LiftedN d) e++-- transpose :: [a] -> [[a]]+reshape :: Integer -> LExpr -> Nat -> LExpr+reshape n e d =+    let t = unliftTypeN d $ typeOf e+    in PApp1 (liftTypeN d $ ListT t) (Reshape n) (LiftedN d) e++-- group :: [a] -> [b] -> [(b, [a])]+group :: LExpr -> LExpr -> Nat -> LExpr+group xs gs d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT gt = unliftTypeN d $ typeOf gs+        rt             = listT (pairT gt (listT xt))+    in PApp2 (liftTypeN d rt) Group (LiftedN d) xs gs++-- sort :: [a] -> [b] -> [a]+sort :: LExpr -> LExpr -> Nat -> LExpr+sort xs ss d =+    let xst = unliftTypeN d $ typeOf xs+    in PApp2 (liftTypeN d xst) Sort (LiftedN d) xs ss++sng :: LExpr -> Nat -> LExpr+sng e d =+    let t = unliftTypeN d $ typeOf e+    in PApp1 (liftTypeN d t) Singleton (LiftedN d) e++tuple :: [LExpr] -> Nat -> LExpr+tuple es d =+    let ts = map (unliftTypeN d . typeOf) es+        rt = TupleT ts+    in MkTuple (liftTypeN d rt) (LiftedN d) es++-- zip :: [a] -> [b] -> [(a, b)]+zip :: LExpr -> LExpr -> Nat -> LExpr+zip xs ys d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT yt = unliftTypeN d $ typeOf ys+    in PApp2 (liftTypeN d $ listT (pairT xt yt)) Zip (LiftedN d) xs ys++cartProduct :: LExpr -> LExpr -> Nat -> LExpr+cartProduct xs ys d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT yt = typeOf ys+    in PApp2 (liftTypeN d $ listT (pairT xt yt)) CartProduct (LiftedN d) xs ys++-- nestProduct :: [a] -> [b] -> [(a, [(a, b)])]+nestProduct :: LExpr -> LExpr -> Nat -> LExpr+nestProduct xs ys d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT yt = unliftTypeN d $ typeOf ys+        rt       = listT (pairT xt (listT (pairT xt yt)))+    in PApp2 (liftTypeN d rt) NestProduct (LiftedN d) xs ys++thetaJoin :: JoinPredicate JoinExpr  -> LExpr -> LExpr -> Nat -> LExpr+thetaJoin p xs ys d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT yt = unliftTypeN d $ typeOf ys+    in PApp2 (liftTypeN d $ listT (pairT xt yt)) (ThetaJoin p) (LiftedN d) xs ys++nestJoin :: JoinPredicate JoinExpr  -> LExpr -> LExpr -> Nat -> LExpr+nestJoin p xs ys d =+    let ListT xt = unliftTypeN d $ typeOf xs+        ListT yt = unliftTypeN d $ typeOf ys+        rt       = listT (pairT xt (listT (pairT xt yt)))+    in PApp2 (liftTypeN d rt) (NestJoin p) (LiftedN d) xs ys++semiJoin :: JoinPredicate JoinExpr  -> LExpr -> LExpr -> Nat -> LExpr+semiJoin p e1 e2 d =+    let t1 = unliftTypeN d $ typeOf e1+    in PApp2 (liftTypeN d t1) (SemiJoin p) (LiftedN d) e1 e2++antiJoin :: JoinPredicate JoinExpr  -> LExpr -> LExpr -> Nat -> LExpr+antiJoin p e1 e2 d =+    let t1 = unliftTypeN d $ typeOf e1+    in PApp2 (liftTypeN d t1) (AntiJoin p) (LiftedN d) e1 e2++append :: LExpr -> LExpr -> Nat -> LExpr+append e1 e2 d =+    let t1 = unliftTypeN d $ typeOf e1+    in PApp2 (liftTypeN d t1) Append (LiftedN d) e1 e2++index :: LExpr -> LExpr -> Nat -> LExpr+index e1 e2 d =+    let ListT t = unliftTypeN d $ typeOf e1+    in PApp2 (liftTypeN d t) Index (LiftedN d) e1 e2++length :: LExpr -> Nat -> LExpr+length e1 d = PApp1 (liftTypeN d intT) Length (LiftedN d) e1++-- FIXME this is not the right place to perform this step. If at all,+-- do it during compilation to VL.+head :: LExpr -> Nat -> LExpr+head = the++the :: LExpr -> Nat -> LExpr+the e1 d =+    let ListT t1 = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) The (LiftedN d) e1++last :: LExpr -> Nat -> LExpr+last e1 d =+    let ListT t1 = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) Last (LiftedN d) e1++tail :: LExpr -> Nat -> LExpr+tail e1 d =+    let t1@(ListT _) = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) Tail (LiftedN d) e1++nub :: LExpr -> Nat -> LExpr+nub e1 d =+    let t1@(ListT _) = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) Nub (LiftedN d) e1++number :: LExpr -> Nat -> LExpr+number e1 d =+    let ListT t = unliftTypeN d $ typeOf e1+        rt      = (ListT (pairT t IntT ))+    in PApp1 (liftTypeN d rt) Number (LiftedN d) e1++init :: LExpr -> Nat -> LExpr+init e1 d =+    let t1@(ListT _) = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) Init (LiftedN d) e1++reverse :: LExpr -> Nat -> LExpr+reverse e1 d =+    let t1@(ListT _) = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t1) Reverse (LiftedN d) e1++and :: LExpr -> Nat -> LExpr+and e1 d = PApp1 (liftTypeN d BoolT) And (LiftedN d) e1++or :: LExpr -> Nat -> LExpr+or e1 d = PApp1 (liftTypeN d BoolT) Or (LiftedN d) e1++sum :: LExpr -> Nat -> LExpr+sum e1 d =+    let ListT t = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t) Sum (LiftedN d) e1++avg :: LExpr -> Nat -> LExpr+avg e1 d = PApp1 (liftTypeN d DoubleT) Avg (LiftedN d) e1++minimum :: LExpr -> Nat -> LExpr+minimum e1 d =+    let ListT t = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t) Minimum (LiftedN d) e1++maximum :: LExpr -> Nat -> LExpr+maximum e1 d =+    let ListT t = unliftTypeN d $ typeOf e1+    in PApp1 (liftTypeN d t) Maximum (LiftedN d) e1++concat :: LExpr -> Nat -> LExpr+concat e d =+    let ListT rt@(ListT _) = unliftTypeN d $ typeOf e+    in PApp1 (liftTypeN d rt) Concat (LiftedN d) e++dist :: LExpr -> LExpr -> Nat -> LExpr+dist e1 e2 d =+    let t1 = typeOf e1+    in PApp2 (listT t1) Dist (LiftedN d) e1 e2++restrict :: LExpr -> LExpr -> Nat -> LExpr+restrict xs bs d =+    let xst = unliftTypeN d $ typeOf xs+    in PApp2 (liftTypeN d xst) Restrict (LiftedN d) xs bs++-- combine :: [Bool] -> [a] -> [a] -> [a]+combine :: LExpr -> LExpr -> LExpr -> Nat -> LExpr+combine e1 e2 e3 d =+    let xst = unliftTypeN d $ typeOf e2+    in PApp3 (liftTypeN d xst) Combine (LiftedN d) e1 e2 e3++tupElem :: TupleIndex -> LExpr -> Nat -> LExpr+tupElem f e d = +    let t = tupleElemT (unliftTypeN d $ typeOf e) f+    in PApp1 (liftTypeN d t) (TupElem f) (LiftedN d) e++if_ :: Typed e => ExprTempl l e -> ExprTempl l e -> ExprTempl l e -> ExprTempl l e+if_ eb et ee =+    let (BoolT, tt, te) = (typeOf eb, typeOf et, typeOf ee)+    in if tt == te+       then If tt eb et ee+       else error $ printf "FKL.if: incompatible types: %s %s" (pp tt) (pp te)++let_ :: Typed e => Ident -> ExprTempl l e -> ExprTempl l e -> ExprTempl l e+let_ x e1 e2 = Let (typeOf e2) x e1 e2++--------------------------------------------------------------------------------+-- Smart constructors for binary and unary operators.++-- FIXME typing of binary operators is not correct+bin :: Type -> ScalarBinOp -> LExpr -> LExpr -> Nat -> LExpr+bin t o e1 e2 d = BinOp (liftTypeN d t) o (LiftedN d) e1 e2++un :: Type -> ScalarUnOp -> LExpr -> Nat -> LExpr+un t o e d = UnOp (liftTypeN d t) o (LiftedN d) e+++--------------------------------------------------------------------------------+-- Smart constructors for special forms in the flat FKL dialect++forget :: Nat -> FExpr -> FExpr+forget n xs =+    let xst = typeOf xs+    in Ext $ Forget n (unwrapListType n xst) xs++unwrapListType :: Nat -> Type -> Type+unwrapListType Zero t               = t+unwrapListType (Succ n') (ListT xt) = unwrapListType n' xt+unwrapListType _         _          = $impossible++imprint :: Nat -> FExpr -> FExpr -> FExpr+imprint n shape bottom = Ext $ Imprint n (wrapListType n bt) shape bottom+  where+    bt = typeOf bottom++wrapListType :: Nat -> Type -> Type+wrapListType Zero t     = t+wrapListType (Succ n') t = wrapListType n' (listT t)++-- | A regular single 'dist' in the normalized FKL dialect+fdist :: FExpr -> FExpr -> FExpr+fdist e1 e2 = PApp2 (listT $ typeOf e1) Dist NotLifted e1 e2++--------------------------------------------------------------------------------+-- Smart constructors for special forms in the flat FKL dialect++broadcast :: LExpr -> LExpr -> Nat -> LExpr+broadcast e1 e2 d = Ext $ Broadcast d ty e1 e2+  where+    ty = wrapListType d (typeOf e1)
+ src/Database/DSH/FKL/Rewrite.hs view
@@ -0,0 +1,135 @@+{-# LANGUAGE FlexibleContexts    #-}++module Database.DSH.FKL.Rewrite+    ( optimizeFKL+    ) where++import Data.Monoid+import Data.List+import Control.Arrow++import Database.DSH.Common.RewriteM+import Database.DSH.Common.Lang+import Database.DSH.Common.Type+import Database.DSH.Common.Kure+import Database.DSH.Common.Pretty+import Database.DSH.FKL.Lang+import Database.DSH.FKL.Kure++-- | Run a translate on an expression without context+applyExpr :: (Injection (ExprTempl l e) (FKL l e))+          => TransformF (FKL l e) b -> ExprTempl l e -> Either String b+applyExpr f e = runRewriteM $ applyT f initialCtx (inject e)++--------------------------------------------------------------------------------+-- Computation of free and bound variables++freeVarsT :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e)) +          => TransformF (FKL l e) [Ident]+freeVarsT = fmap nub +            $ crushbuT +            $ do (ctx, ExprFKL (Var _ v)) <- exposeT+                 guardM (v `freeIn` ctx)+                 return [v]++-- | Compute free variables of the given expression+freeVars :: (Walker FlatCtx (FKL l e), Injection (ExprTempl l e) (FKL l e))+         => ExprTempl l e -> [Ident]+freeVars = either error id . applyExpr freeVarsT+++--------------------------------------------------------------------------------+-- Substitution++alphaLetR :: ( Injection (ExprTempl l e) (FKL l e)+             , Walker FlatCtx (FKL l e)+             , Typed e)+          => [Ident] -> RewriteF (FKL l e)+alphaLetR avoidNames = do+    ExprFKL (Let _ x e1 e2) <- idR+    x'                      <- freshNameT (x : freeVars e2 ++ avoidNames)+    let varTy = typeOf e1+    childR LetBody (tryR $ substR x (Var varTy x'))++substR :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e), Typed e)+       => Ident -> ExprTempl l e -> RewriteF (FKL l e)+substR v s = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    ExprFKL (Var _ n) | n == v                          -> return $ inject s++    -- Some other variable+    ExprFKL (Var _ _)                                   -> idR++    ExprFKL (Let _ x _ e2) | x /= v && v `elem` freeVars e2 ->+        if x `elem` freeVars s+        then alphaLetR (freeVars s) >>> substR v s+        else anyR $ substR v s++    -- A let binding which shadows v -> don't descend into the body+    ExprFKL (Let _ x _ _) | v == x                      -> tryR $ childR LetBind (substR v s)+    _                                                   -> anyR $ substR v s++--------------------------------------------------------------------------------+-- Simple optimizations++-- | Count all occurences of an identifier for let-inlining.+countVarRefT :: Walker FlatCtx (FKL l e) => Ident -> TransformF (FKL l e) (Sum Int)+countVarRefT v = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    ExprFKL (Var _ n) | n == v         -> return 1+    ExprFKL (Var _ _) | otherwise      -> return 0+    ExprFKL Table{}                    -> return 0+    ExprFKL Const{}                    -> return 0++    ExprFKL (Let _ n _ _) | n == v     -> childT LetBody (countVarRefT v)++    ExprFKL Let{}         | otherwise  -> allT (countVarRefT v)++    _                                  -> allT (countVarRefT v)+++-- | Remove a let-binding that is not referenced.+unusedBindingR :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e)) +               => RewriteF (FKL l e)+unusedBindingR = do+    ExprFKL (Let _ x _ e2) <- idR+    0            <- childT LetBody $ countVarRefT x+    return $ inject e2+++-- | Inline a let-binding that is only referenced once.+referencedOnceR :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e), Typed e)+                => RewriteF (FKL l e)+referencedOnceR = do+    ExprFKL (Let _ x e1 _) <- idR+    1            <- childT LetBody $ countVarRefT x+    childT LetBody $ substR x e1++simpleExpr :: ExprTempl l e -> Bool+simpleExpr Table{}                   = True+simpleExpr Var{}                     = True+simpleExpr (PApp1 _ (TupElem _) _ e) = simpleExpr e+simpleExpr _                         = False++-- | Inline a let-binding that binds a simple expression.+simpleBindingR :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e), Typed e)+               => RewriteF (FKL l e)+simpleBindingR = do+    ExprFKL (Let _ x e1 _) <- idR+    guardM $ simpleExpr e1+    childT LetBody $ substR x e1++fklOptimizations :: (Injection (ExprTempl l e) (FKL l e), Walker FlatCtx (FKL l e), Typed e)+                 => RewriteF (FKL l e)+fklOptimizations = anybuR $ unusedBindingR +                            <+ referencedOnceR+                            <+ simpleBindingR++optimizeFKL :: ( Injection (ExprTempl l e) (FKL l e)+               , Walker FlatCtx (FKL l e)+               , Typed e, Pretty (ExprTempl l e)+               ) +            => String -> ExprTempl l e -> ExprTempl l e+optimizeFKL stage expr = debugOpt stage expr expr'+  where+    expr' = applyExpr (fklOptimizations >>> projectT) expr
+ src/Database/DSH/Frontend/Externals.hs view
@@ -0,0 +1,719 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE ViewPatterns          #-}++module Database.DSH.Frontend.Externals where+       +import Database.DSH.Frontend.Internals+import Database.DSH.Frontend.Funs+import Database.DSH.Impossible+import Database.DSH.Frontend.TupleTypes++import Prelude ( Eq, Ord, Num(..), Fractional(..), Floating(..)+               , Bool(..), Char, Integer, Double, Maybe(..), Either(..)+               , id, ($), (.))+import qualified Prelude as P++import Data.String+import Data.Text (Text)+import qualified Data.Text as T++-- QA Instances++instance QA () where+  type Rep () = ()+  toExp () = UnitE+  frExp UnitE = ()+  frExp _ = $impossible++instance QA Bool where+  type Rep Bool = Bool+  toExp = BoolE+  frExp (BoolE b) = b+  frExp _ = $impossible++instance QA Char where+  type Rep Char = Char+  toExp = CharE+  frExp (CharE c) = c+  frExp _ = $impossible++instance QA Integer where+  type Rep Integer = Integer+  toExp = IntegerE+  frExp (IntegerE i) = i+  frExp _ = $impossible++instance QA Double where+  type Rep Double = Double+  toExp = DoubleE+  frExp (DoubleE d) = d+  frExp _ = $impossible++instance QA Text where+  type Rep Text = Text+  toExp = TextE+  frExp (TextE t) = t+  frExp _ = $impossible++instance (QA a) => QA [a] where+  type Rep [a] = [Rep a]+  toExp as = ListE (P.map toExp as)+  frExp (ListE as) = P.map frExp as+  frExp _ = $impossible++instance (QA a) => QA (Maybe a) where+  type Rep (Maybe a) = [Rep a]+  toExp Nothing = ListE []+  toExp (Just a) = ListE [toExp a]+  frExp (ListE []) = Nothing+  frExp (ListE (a : _)) = Just (frExp a)+  frExp _ = $impossible++instance (QA a,QA b) => QA (Either a b) where+  type Rep (Either a b) = ([Rep a],[Rep b])+  toExp (Left a) = pairE (ListE [toExp a]) (ListE [])+  toExp (Right b) = pairE (ListE []) (ListE [toExp b])+  frExp (TupleConstE (Tuple2E (ListE (a : _)) _)) = Left (frExp a)+  frExp (TupleConstE (Tuple2E _ (ListE (a : _)))) = Right (frExp a)+  frExp _ = $impossible++-- Elim instances++instance (QA r) => Elim () r where+  type Eliminator () r = Q r -> Q r+  elim _ r = r++instance (QA r) => Elim Bool r where+  type Eliminator Bool r = Q r -> Q r -> Q r+  elim (Q e) (Q e1) (Q e2) = Q (AppE Cond (TupleConstE (Tuple3E e e1 e2)))++instance (QA r) => Elim Char r where+  type Eliminator Char r = (Q Char -> Q r) -> Q r+  elim q f = f q++instance (QA r) => Elim Integer r where+  type Eliminator Integer r = (Q Integer -> Q r) -> Q r+  elim q f = f q++instance (QA r) => Elim Double r where+  type Eliminator Double r = (Q Double -> Q r) -> Q r+  elim q f = f q++instance (QA r) => Elim Text r where+  type Eliminator Text r = (Q Text -> Q r) -> Q r+  elim q f = f q++instance (QA a,QA b,QA r) => Elim (a,b) r where+  type Eliminator (a,b) r = (Q a -> Q b -> Q r) -> Q r+  elim q f = f (fst q) (snd q)++instance (QA a,QA r) => Elim (Maybe a) r where+  type Eliminator (Maybe a) r = Q r -> (Q a -> Q r) -> Q r+  elim q r f = maybe r f q++instance (QA a,QA b,QA r) => Elim (Either a b) r where+  type Eliminator (Either a b) r = (Q a -> Q r) -> (Q b -> Q r) -> Q r+  elim q f g = either f g q++-- BasicType instances++instance BasicType () where+instance BasicType Bool where+instance BasicType Char where+instance BasicType Integer where+instance BasicType Double where+instance BasicType Text where++-- TA instances++instance TA () where+instance TA Bool where+instance TA Char where+instance TA Integer where+instance TA Double where+instance TA Text where++-- Num and Fractional instances++instance Num (Exp Integer) where+  (+) e1 e2 = AppE Add (pairE e1 e2)+  (*) e1 e2 = AppE Mul (pairE e1 e2)+  (-) e1 e2 = AppE Sub (pairE e1 e2)++  fromInteger = IntegerE++  abs e = let c = AppE Lt (pairE e 0)+          in AppE Cond (tripleE c (negate e) e)++  signum e = let c1 = AppE Lt  (pairE e 0)+                 c2 = AppE Equ (pairE e 0)+                 e' = AppE Cond (tripleE c2 0 1)+             in AppE Cond (tripleE c1 (-1) e')++instance Num (Exp Double) where+  (+) e1 e2 = AppE Add (pairE e1 e2)+  (*) e1 e2 = AppE Mul (pairE e1 e2)+  (-) e1 e2 = AppE Sub (pairE e1 e2)++  fromInteger = DoubleE . fromInteger++  abs e = let c = AppE Lt (pairE e 0)+          in  AppE Cond (tripleE c (negate e) e)++  signum e = let c1 = AppE Lt  (pairE e 0.0)+                 c2 = AppE Equ (pairE e 0.0)+                 e' = AppE Cond (tripleE c2 0 1)+             in  AppE Cond (tripleE c1 (-1) e')++instance Fractional (Exp Double) where+  (/) e1 e2    = AppE Div (pairE e1 e2)+  fromRational = DoubleE . fromRational++instance Floating (Exp Double) where+  pi     = DoubleE 3.141592653589793+  sin e  = AppE Sin e+  cos e  = AppE Cos e+  tan e  = AppE Tan e+  sqrt e = AppE Sqrt e+  exp e  = AppE Exp e+  log e  = AppE Log e+  asin e = AppE ASin e+  acos e = AppE ACos e+  atan e = AppE ATan e+  sinh   = $unimplemented+  cosh   = $unimplemented+  asinh  = $unimplemented+  atanh  = $unimplemented+  acosh  = $unimplemented++instance Num (Q Integer) where+  (+) (Q e1) (Q e2) = Q (e1 + e2)+  (*) (Q e1) (Q e2) = Q (e1 * e2)+  (-) (Q e1) (Q e2) = Q (e1 - e2)+  fromInteger       = Q . IntegerE+  abs (Q e)         = Q (abs e)+  signum (Q e)      = Q (signum e)++instance Num (Q Double) where+  (+) (Q e1) (Q e2) = Q (e1 + e2)+  (*) (Q e1) (Q e2) = Q (e1 * e2)+  (-) (Q e1) (Q e2) = Q (e1 - e2)+  fromInteger       = Q . DoubleE . fromInteger+  abs (Q e)         = Q (abs e)+  signum (Q e)      = Q (signum e)++instance Fractional (Q Double) where+  (/) (Q e1) (Q e2) = Q (e1 / e2)+  fromRational = Q . DoubleE . fromRational++instance Floating (Q Double) where+  pi         = Q pi+  sin (Q e)  = Q (sin e)+  cos (Q e)  = Q (cos e)+  tan (Q e)  = Q (tan e)+  asin (Q e) = Q (asin e)+  acos (Q e) = Q (acos e)+  atan (Q e) = Q (atan e)+  exp (Q e)  = Q (exp e)+  log (Q e)  = Q (log e)+  sqrt (Q e) = Q (sqrt e)+  sinh   = $unimplemented+  cosh   = $unimplemented+  asinh  = $unimplemented+  atanh  = $unimplemented+  acosh  = $unimplemented++-- View instances++instance View (Q ()) where+  type ToView (Q ()) = Q ()+  view = id++instance View (Q Bool) where+  type ToView (Q Bool) = Q Bool+  view = id++instance View (Q Char) where+  type ToView (Q Char) = Q Char+  view = id++instance View (Q Integer) where+  type ToView (Q Integer) = Q Integer+  view = id++instance View (Q Double) where+  type ToView (Q Double) = Q Double+  view = id++instance View (Q Text) where+  type ToView (Q Text) = Q Text+  view = id++-- IsString instances++instance IsString (Q Text) where+  fromString = Q . TextE . T.pack++-- * Referring to persistent tables++defaultHints :: TableHints+defaultHints = TableHints [] PossiblyEmpty++table :: (QA a, TA a) => String -> TableHints -> Q [a]+table name hints = Q (TableE (TableDB name hints))++-- * toQ++toQ :: (QA a) => a -> Q a+toQ = Q . toExp++-- * Unit++unit :: Q ()+unit = Q UnitE++-- * Boolean logic++false :: Q Bool+false = Q (BoolE False)++true :: Q Bool+true = Q (BoolE True)++not :: Q Bool -> Q Bool+not (Q e) = Q (AppE Not e)++(&&) :: Q Bool -> Q Bool -> Q Bool+(&&) (Q a) (Q b) = Q (AppE Conj (pairE a b))++(||) :: Q Bool -> Q Bool -> Q Bool+(||) (Q a) (Q b) = Q (AppE Disj (TupleConstE (Tuple2E a b)))++-- * Equality and Ordering++eq :: (QA a,Eq a,TA a) => Q a -> Q a -> Q Bool+eq (Q a) (Q b) = Q (AppE Equ (TupleConstE (Tuple2E a b)))++(==) :: (QA a,Eq a,TA a) => Q a -> Q a -> Q Bool+(==) = eq++neq :: (QA a,Eq a,TA a) => Q a -> Q a -> Q Bool+neq (Q a) (Q b) = Q (AppE NEq (pairE a b))++(/=) :: (QA a,Eq a,TA a) => Q a -> Q a -> Q Bool+(/=) = neq++lt :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+lt (Q a) (Q b) = Q (AppE Lt (pairE a b))++(<) :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+(<) = lt++lte :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+lte (Q a) (Q b) = Q (AppE Lte (pairE a b))++(<=) :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+(<=) = lte++gte :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+gte (Q a) (Q b) = Q (AppE Gte (pairE a b))++(>=) :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+(>=) = gte++gt :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+gt (Q a) (Q b) = Q (AppE Gt (pairE a b))++(>) :: (QA a,Ord a,TA a) => Q a -> Q a -> Q Bool+(>) = gt++min :: (QA a,Ord a,TA a) => Q a -> Q a -> Q a+min a b = cond (a < b) a b++max :: (QA a,Ord a,TA a) => Q a -> Q a -> Q a+max a b = cond (a > b) a b+    +mod :: Q Integer -> Q Integer -> Q Integer+mod (Q a) (Q b) = Q (AppE Mod (pairE a b))++div :: Q Integer -> Q Integer -> Q Integer+div (Q a) (Q b) = Q (AppE Div (pairE a b))++-- * Conditionals++bool :: (QA a) => Q a -> Q a -> Q Bool -> Q a+bool f t b = cond b t f++cond :: (QA a) => Q Bool -> Q a -> Q a -> Q a+cond (Q c) (Q a) (Q b) = Q (AppE Cond (TupleConstE (Tuple3E c a b)))++ifThenElse :: (QA a) => Q Bool -> Q a -> Q a -> Q a+ifThenElse = cond++(?) :: (QA a) => Q Bool -> (Q a,Q a) -> Q a+(?) c (a,b) = cond c a b++-- * Maybe++listToMaybe :: (QA a) => Q [a] -> Q (Maybe a)+listToMaybe (Q as) = Q as++maybeToList :: (QA a) => Q (Maybe a) -> Q [a]+maybeToList (Q ma) = Q ma++nothing :: (QA a) => Q (Maybe a)+nothing = listToMaybe nil++just :: (QA a) => Q a -> Q (Maybe a)+just a = listToMaybe (singleton a)++isNothing :: (QA a) => Q (Maybe a) -> Q Bool+isNothing ma = null (maybeToList ma)++isJust :: (QA a) => Q (Maybe a) -> Q Bool+isJust ma = not (isNothing ma)++fromJust :: (QA a) => Q (Maybe a) -> Q a+fromJust ma = head (maybeToList ma)++maybe :: (QA a,QA b) => Q b -> (Q a -> Q b) -> Q (Maybe a) -> Q b+maybe b f ma = isNothing ma ? (b,f (fromJust ma))++fromMaybe :: (QA a) => Q a -> Q (Maybe a) -> Q a+fromMaybe a ma = isNothing ma ? (a,fromJust ma)++catMaybes :: (QA a) => Q [Maybe a] -> Q [a]+catMaybes = concatMap maybeToList++mapMaybe :: (QA a,QA b) => (Q a -> Q (Maybe b)) -> Q [a] -> Q [b]+mapMaybe f = concatMap (maybeToList . f)++-- * Either++pairToEither :: (QA a,QA b) => Q ([a],[b]) -> Q (Either a b)+pairToEither (Q a) = Q a++eitherToPair :: (QA a,QA b) => Q (Either a b) -> Q ([a],[b])+eitherToPair (Q a) = Q a++left :: (QA a,QA b) => Q a -> Q (Either a b)+left a = pairToEither (pair (singleton a) nil)++right :: (QA a,QA b) => Q b -> Q (Either a b)+right a = pairToEither (pair nil (singleton a))++isLeft :: (QA a,QA b) => Q (Either a b) -> Q Bool+isLeft = null . snd . eitherToPair++isRight :: (QA a,QA b) => Q (Either a b) -> Q Bool+isRight = null . fst . eitherToPair++either :: (QA a,QA b,QA c) => (Q a -> Q c) -> (Q b -> Q c) -> Q (Either a b) -> Q c+either lf rf e =+  let p = eitherToPair e+  in  head (map lf (fst p) ++ map rf (snd p))++lefts :: (QA a,QA b) => Q [Either a b] -> Q [a]+lefts = concatMap (fst . eitherToPair)++rights :: (QA a,QA b) => Q [Either a b] -> Q [b]+rights = concatMap (snd . eitherToPair)++partitionEithers :: (QA a,QA b) => Q [Either a b] -> Q ([a], [b])+partitionEithers es = pair (lefts es) (rights es)++-- * List Construction++nil :: (QA a) => Q [a]+nil = Q (ListE [])++empty :: (QA a) => Q [a]+empty = nil++cons :: (QA a) => Q a -> Q [a] -> Q [a]+cons (Q a) (Q as) = Q (AppE Cons (pairE a as))++(<|) :: (QA a) => Q a -> Q [a] -> Q [a]+(<|) = cons++snoc :: (QA a) => Q [a] -> Q a -> Q [a]+snoc as a = append as (singleton a)++(|>) :: (QA a) => Q [a] -> Q a -> Q [a]+(|>) = snoc++singleton :: (QA a) => Q a -> Q [a]+singleton (Q e) = cons (Q e) nil++-- * List Operations++head :: (QA a) => Q [a] -> Q a+head (Q as) = Q (AppE Head as)++tail :: (QA a) => Q [a] -> Q [a]+tail (Q as) = Q (AppE Tail as)++take :: (QA a) => Q Integer -> Q [a] -> Q [a]+take i xs = map fst $ filter (\xp -> snd xp <= i) $ number xs++drop :: (QA a) => Q Integer -> Q [a] -> Q [a]+drop i xs = map fst $ filter (\xp -> snd xp > i) $ number xs++map :: (QA a,QA b) => (Q a -> Q b) ->  Q [a] -> Q [b]+map f (Q as) = Q (AppE Map (pairE (LamE (toLam f)) as))++append :: (QA a) => Q [a] -> Q [a] -> Q [a]+append (Q as) (Q bs) = Q (AppE Append (pairE as bs))++(++) :: (QA a) => Q [a] -> Q [a] -> Q [a]+(++) = append++filter :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]+filter f (Q as) = Q (AppE Filter (pairE (LamE (toLam f)) as))++-- | Partition a list into groups according to the supplied projection+-- function.+groupWithKey :: (QA a,QA b,Ord b, TA b) => (Q a -> Q b) -> Q [a] -> Q [(b,[a])]+groupWithKey f (Q as) = Q (AppE GroupWithKey (pairE (LamE (toLam f)) as))++groupWith :: (QA a,QA b,Ord b, TA b) => (Q a -> Q b) -> Q [a] -> Q [[a]]+groupWith f as = map snd (groupWithKey f as)++sortWith :: (QA a,QA b,Ord b, TA b) => (Q a -> Q b) -> Q [a] -> Q [a]+sortWith f (Q as) = Q (AppE SortWith (pairE (LamE (toLam f)) as))++last :: (QA a) => Q [a] -> Q a+last (Q as) = Q (AppE Last as)++init :: (QA a) => Q [a] -> Q [a]+init (Q as) = Q (AppE Init as)++null :: (QA a) => Q [a] -> Q Bool+null (Q as) = Q (AppE Null as)++length :: (QA a) => Q [a] -> Q Integer+length (Q as) = Q (AppE Length as)++index :: (QA a) => Q [a] -> Q Integer -> Q a+index (Q as) (Q i) = Q (AppE Index (pairE as i))++(!!) :: (QA a) => Q [a] -> Q Integer -> Q a+(!!) = index++reverse :: (QA a) => Q [a] -> Q [a]+reverse (Q as) = Q (AppE Reverse as)++number :: (QA a) => Q [a] -> Q [(a, Integer)]+number (Q as) = Q (AppE Number as)++-- * Special folds++and :: Q [Bool] -> Q Bool+and (Q bs) = Q (AppE And bs)++or :: Q [Bool] -> Q Bool+or (Q bs) = Q (AppE Or bs)++any :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q Bool+any f = or . map f++all :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q Bool+all f = and . map f++sum :: (QA a,Num a) => Q [a] -> Q a+sum (Q as) = Q (AppE Sum as)++avg :: (QA a,Num a) => Q [a] -> Q Double+avg (Q as) = Q (AppE Avg as)++concat :: (QA a) => Q [[a]] -> Q [a]+concat (Q ass) = Q (AppE Concat ass)++concatMap :: (QA a,QA b) => (Q a -> Q [b]) -> Q [a] -> Q [b]+concatMap f (Q as) = Q (AppE ConcatMap (pairE (LamE (toLam f)) as))++maximum :: (QA a,Ord a,TA a) => Q [a] -> Q a+maximum (Q as) = Q (AppE Maximum as)++minimum :: (QA a,Ord a,TA a) => Q [a] -> Q a+minimum (Q as) = Q (AppE Minimum as)++-- * Sublists++splitAt :: (QA a) => Q Integer -> Q [a] -> Q ([a],[a])+splitAt i xs = pair (take i xs) (drop i xs)++-- FIXME might be implemented using non-dense numbering!+takeWhile :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]+takeWhile p xs = +    let ys            = map (\xpos -> pair xpos (p $ fst xpos)) $ number xs+        notQualifying = filter (\xposp -> not (snd xposp)) ys+        maxPos = minimum $ map (\xposp -> snd $ fst xposp) notQualifying+     +    in cond (null notQualifying) +            xs+            (map (\xposp -> fst $ fst xposp) $ filter (\xposp -> (snd $ fst xposp) < maxPos) ys)++-- FIXME might be implemented using non-dense numbering!+dropWhile :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q [a]+dropWhile p xs = +    let ys  = map (\xpos -> pair xpos (p $ fst xpos)) $ number xs+        minPos = minimum $ map (\xposp -> snd $ fst xposp) $ filter (\xposp -> not (snd xposp)) ys+    in map (\xposp -> fst $ fst xposp) $ filter (\xposp -> (snd $ fst xposp) >= minPos) ys++span :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q ([a],[a])+span f as = pair (takeWhile f as) (dropWhile f as)++break :: (QA a) => (Q a -> Q Bool) -> Q [a] -> Q ([a],[a])+break f = span (not . f)++-- * Searching Lists++elem :: (QA a,Eq a,TA a) => Q a -> Q [a] -> Q Bool+elem a as = any (a ==) as++notElem :: (QA a,Eq a,TA a) => Q a -> Q [a] -> Q Bool+notElem a as = not (a `elem` as)++lookup :: (QA a,QA b,Eq a,TA a) => Q a -> Q [(a, b)] -> Q (Maybe b)+lookup a  = listToMaybe . map snd . filter ((a ==) . fst)++-- * Zipping and Unzipping Lists++zip :: (QA a,QA b) => Q [a] -> Q [b] -> Q [(a,b)]+zip (Q as) (Q bs) = Q (AppE Zip (pairE as bs))++zipWith :: (QA a,QA b,QA c) => (Q a -> Q b -> Q c) -> Q [a] -> Q [b] -> Q [c]+zipWith f as bs = map (\e -> f (fst e) (snd e)) (zip as bs)++unzip :: (QA a,QA b) => Q [(a,b)] -> Q ([a],[b])+unzip as = pair (map fst as) (map snd as)++zip3 :: (QA a,QA b,QA c) => Q [a] -> Q [b] -> Q [c] -> Q [(a,b,c)]+zip3 as bs cs = map (\abc -> triple (fst abc) (fst (snd abc)) (snd (snd abc))) (zip as (zip bs cs))++zipWith3 :: (QA a,QA b,QA c,QA d) => (Q a -> Q b -> Q c -> Q d) -> Q [a] -> Q [b] -> Q [c] -> Q [d]+zipWith3 f as bs cs = map (\e -> (case view e of (a,b,c) -> f a b c))+                          (zip3 as bs cs)++unzip3 :: (QA a,QA b,QA c) => Q [(a,b,c)] -> Q ([a],[b],[c])+unzip3 abcs = triple (map (\e -> (case view e of (a,_,_) -> a)) abcs)+                     (map (\e -> (case view e of (_,b,_) -> b)) abcs)+                     (map (\e -> (case view e of (_,_,c) -> c)) abcs)++-- * Set-oriented operations++nub :: (QA a,Eq a,TA a) => Q [a] -> Q [a]+nub (Q as) = Q (AppE Nub as)++-- * Tuple Projection Functions++fst :: (QA a,QA b) => Q (a,b) -> Q a+fst (Q e) = Q (AppE Fst e)++snd :: (QA a,QA b) => Q (a,b) -> Q b+snd (Q e) = Q (AppE Snd e)++-- * Conversions between numeric types++integerToDouble :: Q Integer -> Q Double+integerToDouble (Q i) = Q (AppE IntegerToDouble i)++-- * Text Functions++-- | 'like' matches a string (first argument) against a pattern (second+-- argument). The pattern must be a SQL LIKE pattern, that is use '_' for single+-- character wildcards and '_' for multi-character wildcards.+like :: Q Text -> Q Text -> Q Bool+like (Q t) (Q p) = Q (AppE Like (pairE t p))++subString :: Integer -> Integer -> Q Text -> Q Text+subString from to (Q t) = Q (AppE (SubString from to) t)++-- * Matrix/Vector-like operators++-- | Transpose a matrix in nested-list representation+transpose :: QA a => Q [[a]] -> Q [[a]]+transpose (Q ass) = Q (AppE Transpose ass)++-- | Divide the list into sublists of length 'n'+-- FIXME should propably have a constraint to flat types+reshape :: QA a => Integer -> Q [a] -> Q [[a]]+reshape n (Q e) = Q (AppE (Reshape n) e)++-- * Rebind Monadic Combinators++return :: (QA a) => Q a -> Q [a]+return = singleton++(>>=) :: (QA a,QA b) => Q [a] -> (Q a -> Q [b]) -> Q [b]+(>>=) ma f = concatMap f ma++(>>) :: (QA a,QA b) => Q [a] -> Q [b] -> Q [b]+(>>) ma mb = concatMap (\_ -> mb) ma++mzip :: (QA a,QA b) => Q [a] -> Q [b] -> Q [(a,b)]+mzip = zip++guard :: Q Bool -> Q [()]+guard (Q c) = Q (AppE Guard c)++-- * Construction of tuples++pair :: (QA a,QA b) => Q a -> Q b -> Q (a,b)+pair (Q a) (Q b) = Q (pairE a b)++triple :: (QA a,QA b,QA c) => Q a -> Q b -> Q c -> Q (a,b,c)+triple (Q a) (Q b) (Q c)= Q (TupleConstE (Tuple3E a b c))++infixl 9  !!+infixr 5  ++, <|, |>+infix  4  ==, /=, <, <=, >=, >+infixr 3  &&+infixr 2  ||+infix  0  ?++-- * Generate instances and constructor functions for tuple types++mkQAInstances       16+mkTAInstances       16+mkViewInstances     16+mkTupleConstructors 16++-- * Missing functions++-- $missing+{- $missing++This module offers most of the functions on lists given in PreludeList for the+'Q' type. Missing functions are:++General folds:++> foldl+> foldl1+> scanl+> scanl1+> foldr+> foldr1+> scanr+> scanr1++Infinit lists:++> iterate+> repeat+> cycle++String functions:++> lines+> words+> unlines+> unwords++-}
+ src/Database/DSH/Frontend/Funs.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE GADTs                     #-}+{-# LANGUAGE TemplateHaskell           #-}++module Database.DSH.Frontend.Funs+    ( Fun(..)+    , TupElem(..)+    ) where++import Data.Text (Text)++import Database.DSH.Frontend.TupleTypes++-- Splice in the type for tuple element accessors+$(mkTupElemType 16)++data Fun a b where+    Not             :: Fun Bool Bool+    IntegerToDouble :: Fun Integer Double+    And             :: Fun [Bool] Bool+    Or              :: Fun [Bool] Bool+    Concat          :: Fun [[a]] [a]+    Head            :: Fun [a] a+    Tail            :: Fun [a] [a]+    Init            :: Fun [a] [a]+    Last            :: Fun [a] a+    Null            :: Fun [a] Bool+    Length          :: Fun [a] Integer+    Guard           :: Fun Bool [()]+    Reverse         :: Fun [a] [a]+    Number          :: Fun [a] [(a, Integer)]+    Fst             :: Fun (a,b) a+    Snd             :: Fun (a,b) b+    Sum             :: Fun [a] a+    Avg             :: Fun [a] Double+    Maximum         :: Fun [a] a+    Minimum         :: Fun [a] a+    Nub             :: Fun [a] [a]+    Append          :: Fun ([a], [a]) [a]+    Add             :: Fun (a,a) a+    Mul             :: Fun (a,a) a+    Sub             :: Fun (a,a) a+    Div             :: Fun (a,a) a+    Mod             :: Fun (Integer,Integer) Integer+    Lt              :: Fun (a,a) Bool+    Lte             :: Fun (a,a) Bool+    Equ             :: Fun (a,a) Bool+    NEq             :: Fun (a,a) Bool+    Gte             :: Fun (a,a) Bool+    Gt              :: Fun (a,a) Bool+    Conj            :: Fun (Bool,Bool) Bool+    Disj            :: Fun (Bool,Bool) Bool+    Cons            :: Fun (a,[a]) [a]+    Index           :: Fun ([a],Integer) a+    Zip             :: Fun ([a],[b]) [(a,b)]+    Map             :: Fun (a -> b,[a]) [b]+    ConcatMap       :: Fun (a -> [b],[a]) [b]+    Filter          :: Fun (a -> Bool,[a]) [a]+    GroupWithKey    :: Fun (a -> b,[a]) [(b, [a])]+    SortWith        :: Fun (a -> b,[a]) [a]+    Cond            :: Fun (Bool,a,a) a+    Like            :: Fun (Text,Text) Bool+    SubString       :: Integer -> Integer -> Fun Text Text +    Transpose       :: Fun [[a]] [[a]]+    Reshape         :: Integer -> Fun [a] [[a]]+    Sin             :: Fun Double Double+    Cos             :: Fun Double Double+    Tan             :: Fun Double Double+    Sqrt            :: Fun Double Double+    Exp             :: Fun Double Double+    Log             :: Fun Double Double+    ASin            :: Fun Double Double+    ACos            :: Fun Double Double+    ATan            :: Fun Double Double+    TupElem         :: TupElem a b -> Fun a b
+ src/Database/DSH/Frontend/Internals.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-}++module Database.DSH.Frontend.Internals where++import           Data.Text                        (Text)+import           Text.PrettyPrint.ANSI.Leijen++import           Database.DSH.Impossible+import           Database.DSH.Frontend.Funs+import           Database.DSH.Frontend.TupleTypes++--------------------------------------------------------------------------------+-- Typed frontend ASTs++-- Generate the data types 'TupleConst' and 'TupleType' for tuple term+-- and type construction.+$(mkTupleAstComponents 16)++data Exp a where+  UnitE       :: Exp ()+  BoolE       :: Bool    -> Exp Bool+  CharE       :: Char    -> Exp Char+  IntegerE    :: Integer -> Exp Integer+  DoubleE     :: Double  -> Exp Double+  TextE       :: Text    -> Exp Text+  ListE       :: (Reify a)           => [Exp a] -> Exp [a]+  AppE        :: (Reify a, Reify b)  => Fun a b -> Exp a -> Exp b+  LamE        :: (Reify a, Reify b)  => (Exp a -> Exp b) -> Exp (a -> b)+  VarE        :: (Reify a)           => Integer -> Exp a+  TableE      :: (Reify a)           => Table -> Exp [a]+  TupleConstE :: TupleConst a -> Exp a++data Type a where+  UnitT     :: Type ()+  BoolT     :: Type Bool+  CharT     :: Type Char+  IntegerT  :: Type Integer+  DoubleT   :: Type Double+  TextT     :: Type Text+  ListT     :: (Reify a)          => Type a -> Type [a]+  ArrowT    :: (Reify a,Reify b)  => Type a -> Type b -> Type (a -> b)+  TupleT    :: TupleType a -> Type a++instance Pretty (Type a) where+    pretty UnitT          = text "()"+    pretty BoolT          = text "Bool"+    pretty CharT          = text "Char"+    pretty IntegerT       = text "Integer"+    pretty DoubleT        = text "Double"+    pretty TextT          = text "Text"+    pretty (ListT t)      = brackets $ pretty t+    pretty (ArrowT t1 t2) = parens $ pretty t1 <+> text "->" <+> pretty t2+    pretty (TupleT t)     = pretty t++-- FIXME generate with TH+instance Pretty (TupleType a) where+    pretty (Tuple2T t1 t2) = tupled $ [pretty t1, pretty t2]+    pretty _               = $unimplemented++--------------------------------------------------------------------------------+-- Classes++class Reify a where+  reify :: a -> Type a++class (Reify (Rep a)) => QA a where+  type Rep a+  toExp :: a -> Exp (Rep a)+  frExp :: Exp (Rep a) -> a++class (QA a,QA r) => Elim a r where+  type Eliminator a r+  elim :: Q a -> Eliminator a r++class BasicType a where++class TA a where++class View a where+  type ToView a+  view :: a -> ToView a++newtype Q a = Q (Exp (Rep a))++pairE :: (Reify a, Reify b) => Exp a -> Exp b -> Exp (a, b)+pairE a b = TupleConstE (Tuple2E a b)++tripleE :: (Reify a, Reify b, Reify c) => Exp a -> Exp b -> Exp c -> Exp (a, b, c)+tripleE a b c = TupleConstE (Tuple3E a b c)++-- | A combination of column names that form a candidate key+newtype Key = Key [String] deriving (Eq, Ord, Show)++-- | Is the table guaranteed to be not empty?+data Emptiness = NonEmpty+               | PossiblyEmpty+               deriving (Eq, Ord, Show)++-- | Catalog information hints that users may give to DSH+data TableHints = TableHints+    { keysHint     :: [Key]+    , nonEmptyHint :: Emptiness+    } deriving (Eq, Ord, Show)++data Table = TableDB String TableHints++-- Reify instances++instance Reify () where+  reify _ = UnitT++instance Reify Bool where+  reify _ = BoolT++instance Reify Char where+  reify _ = CharT++instance Reify Integer where+  reify _ = IntegerT++instance Reify Double where+  reify _ = DoubleT++instance Reify Text where+  reify _ = TextT++instance (Reify a) => Reify [a] where+  reify _ = ListT (reify (undefined :: a))++instance (Reify a, Reify b) => Reify (a -> b) where+  reify _ = ArrowT (reify (undefined :: a)) (reify (undefined :: b))++-- Utility functions++unQ :: Q a -> Exp (Rep a)+unQ (Q e) = e++toLam :: (QA a,QA b) => (Q a -> Q b) -> Exp (Rep a) -> Exp (Rep b)+toLam f = unQ . f . Q++-- * Generate Reify instances for tuple types+mkReifyInstances 16
+ src/Database/DSH/Frontend/Schema.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE TemplateHaskell #-}++-- | This module contains functionality to retrieve information about+-- the schema of actual database tables.+module Database.DSH.Frontend.Schema+    ( getTableInfo+    ) where++import qualified Data.List                as L+import           GHC.Exts+import           Text.Printf++import qualified Database.HDBC            as H++import qualified Database.DSH.Common.Type as T++-- | Retrieve through the given database connection information on the+-- table (columns with their types) which name is given as the second+-- argument.+getTableInfo :: H.IConnection conn => conn -> String -> IO [(String, String, (T.Type -> Bool))]+getTableInfo conn tableName = do+    info <- H.describeTable conn tableName+    case info of+        []    -> error $ printf "Unknown table %s" tableName+        _ : _ -> return $ toTableDescr info++  where+    toTableDescr :: [(String, H.SqlColDesc)] -> [(String, String, T.Type -> Bool)]+    toTableDescr cols = sortWith (\(n, _, _) -> n)+                        [ (name, show colTy, compatibleType colTy)+                        | (name, props) <- cols+                        , let colTy = H.colType props+                        ]+++    compatibleType :: H.SqlTypeId -> T.Type -> Bool+    compatibleType dbT hsT =+        case hsT of+            T.UnitT   -> True+            T.BoolT   -> L.elem dbT [H.SqlSmallIntT, H.SqlIntegerT, H.SqlBitT]+            T.StringT -> L.elem dbT [H.SqlCharT, H.SqlWCharT, H.SqlVarCharT]+            T.IntT    -> L.elem dbT [H.SqlSmallIntT, H.SqlIntegerT, H.SqlTinyIntT, H.SqlBigIntT, H.SqlNumericT]+            T.DoubleT -> L.elem dbT [H.SqlDecimalT, H.SqlRealT, H.SqlFloatT, H.SqlDoubleT]+            t         -> error $ printf "Unsupported column type %s for table %s" (show t) (show tableName)
+ src/Database/DSH/Frontend/TH.hs view
@@ -0,0 +1,564 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Frontend.TH +    ( deriveDSH+    , deriveQA+    , deriveTA+    , deriveView+    , deriveElim+    , deriveSmartConstructors+    , generateTableSelectors+    -- FIXME don't expose tuple constructors but use qualified names+    , DSH.TupleConst(..)+    , F.TupElem(..)+    , DSH.Exp(..)+    , F.Fun(..)+    ) where++import           Control.Monad+import           Control.Applicative+import           Data.Char+import           Data.List++import           Language.Haskell.TH+import           Language.Haskell.TH.Syntax++import qualified Database.DSH.Frontend.Internals  as DSH+import           Database.DSH.Frontend.TupleTypes+import qualified Database.DSH.Frontend.Funs       as F+import           Database.DSH.Impossible+++-----------------------------------------+-- Deriving all DSH-relevant instances --+-----------------------------------------++deriveDSH :: Name -> Q [Dec]+deriveDSH n = do+  qaDecs    <- deriveQA n+  -- elimDecs  <- deriveElim n+  cc        <- countConstructors n+  viewDecs  <- if cc == 1+                  then deriveView n+                  else return []+  scDecs    <- deriveSmartConstructors n+  return (qaDecs {- ++ elimDecs -} ++ viewDecs ++ scDecs)++-----------------+-- Deriving QA --+-----------------++-- | Derive QA instances for data types and newtypes.+deriveQA :: Name -> Q [Dec]+deriveQA name = do+  info <- reify name+  case info of+    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->+      deriveTyConQA name1 tyVarBndrs cons+    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->+      deriveTyConQA name1 tyVarBndrs [con]+    _ -> fail errMsgExoticType++deriveTyConQA :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]+deriveTyConQA name tyVarBndrs cons = do+  let context       = map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)])+                          tyVarBndrs+  let typ           = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)+  let instanceHead  = AppT (ConT ''DSH.QA) typ+  let repDec        = deriveRep typ cons+  toExpDec <- deriveToExp cons+  frExpDec <- deriveFrExp cons+  return [InstanceD context instanceHead [repDec,toExpDec,frExpDec]]++-- Deriving the Rep type function++-- | Derive the representation type 'Rep' for a data type+deriveRep :: Type -> [Con] -> Dec+-- GHC-7.8.2 (template-haskell-2.9.0.0) has a trivial but incompatible+-- modification: two arguments of TySynInstD are now encapsulated in a+-- TySynEqn constructor+#if MIN_VERSION_template_haskell(2,9,0)+deriveRep typ cons = TySynInstD ''DSH.Rep $ TySynEqn [typ] (deriveRepCons cons)+#else+deriveRep typ cons = TySynInstD ''DSH.Rep [typ] (deriveRepCons cons)+#endif++-- | Derive the representation type 'Rep' for the complete type (all+-- constructors).+deriveRepCons :: [Con] -> Type+deriveRepCons []                   = error errMsgExoticType+-- The representation of a type with only one constructor is the+-- representation of that constructor.+deriveRepCons [c]                  = deriveRepCon c+-- The representation of a type with multiple constructors is a tuple+-- of the representation types for all individual constructors (each+-- wrapped in a list).+deriveRepCons cs | length cs <= 16 = mkTupleType $ map (AppT (ConT ''[]) . deriveRepCon) cs+deriveRepCons _                    = error errMsgTypeTooBroad+++-- | Derive the representation type 'Rep' for a single constructor+deriveRepCon :: Con -> Type+deriveRepCon con = case conToTypes con of+  -- A constructor without fields is represented by the empty type+  []                   -> ConT ''()+  -- The representation of a constructor with only one field is the+  -- field type itself.+  [t]                  -> t+  -- Constructors with more fields (up to 16) are represented by a+  -- tuple that contains values for all fields.+  ts | length ts <= 16 -> mkTupleType $ map (AppT (ConT ''DSH.Rep)) ts+  _  | otherwise       -> error errMsgTypeTooBroad++-- Deriving the toExp function of the QA class++deriveToExp :: [Con] -> Q Dec+deriveToExp [] = fail errMsgExoticType+deriveToExp cons = do+  clauses <- sequence (zipWith3 deriveToExpClause (repeat (length cons)) [0 .. ] cons)+  return (FunD 'DSH.toExp clauses)++deriveToExpClause :: Int -- Total number of constructors+                  -> Int -- Index of the constructor+                  -> Con+                  -> Q Clause+deriveToExpClause 0 _ _ = fail errMsgExoticType+deriveToExpClause 1 _ con = do+  (pat1,names1) <- conToPattern con+  exp1 <- deriveToExpMainExp names1+  let body1 = NormalB exp1+  return (Clause [pat1] body1 [])+-- FIXME adapt code for types with multiple constructors to new tuple+-- regime.+deriveToExpClause n i con = $unimplemented+{-+  (pat1,names1) <- conToPattern con+  let exp1 = deriveToExpMainExp names1+  expList1 <- [| DSH.ListE [ $(return exp1) ] |]+  expEmptyList <- [| DSH.ListE [] |]+  let lists = concat [ replicate i expEmptyList+                     , [expList1]+                     , replicate (n - i - 1) expEmptyList]+  let exp2 = foldr1 (AppE . AppE (ConE 'DSH.PairE)) lists+  let body1 = NormalB exp2+  return (Clause [pat1] body1 [])+-}++deriveToExpMainExp :: [Name] -> Q Exp+deriveToExpMainExp []     = return $ ConE 'DSH.UnitE+deriveToExpMainExp [name] = return $ AppE (VarE 'DSH.toExp) (VarE name)+deriveToExpMainExp names  = mkTupConstTerm $ map (AppE (VarE 'DSH.toExp) . VarE) names++-- Deriving to frExp function of the QA class++deriveFrExp :: [Con] -> Q Dec+deriveFrExp cons = do+  clauses <- sequence (zipWith3 deriveFrExpClause (repeat (length cons)) [0 .. ] cons)+  imp     <- impossible+  let lastClause = Clause [WildP] (NormalB imp) []+  return (FunD 'DSH.frExp (clauses ++ [lastClause]))++deriveFrExpClause :: Int -- Total number of constructors+                  -> Int -- Index of the constructor+                  -> Con+                  -> Q Clause+deriveFrExpClause 1 _ con = do+  (_,names1) <- conToPattern con+  let pat1 = deriveFrExpMainPat names1+  let exp1 = foldl AppE+                   (ConE (conToName con))+                   (map (AppE (VarE 'DSH.frExp) . VarE) names1)+  let body1 = NormalB exp1+  return (Clause [pat1] body1 [])+-- FIXME adapt code for types with multiple constructors to new tuple+-- regime.+deriveFrExpClause n i con = $unimplemented+{-+  (_,names1) <- conToPattern con+  let pat1 = deriveFrExpMainPat names1+  let patList1 = ConP 'DSH.ListE [ConP '(:) [pat1,WildP]]+  let lists = replicate i WildP ++ [patList1] ++ replicate (n - i - 1) WildP+  let pat2 = foldr1 (\p1 p2 -> ConP 'DSH.PairE [p1,p2]) lists+  let exp1 = foldl AppE+                   (ConE (conToName con))+                   (map (AppE (VarE 'DSH.frExp) . VarE) names1)+  let body1 = NormalB exp1+  return (Clause [pat2] body1 [])+-}++deriveFrExpMainPat :: [Name] -> Pat+deriveFrExpMainPat [] = ConP 'DSH.UnitE []+deriveFrExpMainPat [name] = VarP name+deriveFrExpMainPat names  = mkTuplePat names++-----------------+-- Deriving TA --+-----------------++deriveTA :: Name -> Q [Dec]+deriveTA name = do+  info <- reify name+  case info of+    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->+      deriveTyConTA name1 tyVarBndrs cons+    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->+      deriveTyConTA name1 tyVarBndrs [con]+    _ -> fail errMsgExoticType++deriveTyConTA :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]+deriveTyConTA name tyVarBndrs _cons = do+  let context       = map (\tv -> ClassP ''DSH.BasicType [VarT (tyVarBndrToName tv)])+                          tyVarBndrs+  let typ           = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)+  let instanceHead  = AppT (ConT ''DSH.TA) typ+  return [InstanceD context instanceHead []]++-------------------+-- Deriving View --+-------------------++deriveView :: Name -> Q [Dec]+deriveView name = do+  info <- reify name+  case info of+    TyConI (DataD    _cxt name1 tyVarBndrs [con] _names) ->+      deriveTyConView name1 tyVarBndrs con+    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->+      deriveTyConView name1 tyVarBndrs con+    _ -> fail errMsgExoticType++deriveTyConView :: Name -> [TyVarBndr] -> Con -> Q [Dec]+deriveTyConView name tyVarBndrs con = do+  let context = map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)]) tyVarBndrs+  let typ1 = AppT (ConT ''DSH.Q)+                  (foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs))+  let instanceHead = AppT (ConT ''DSH.View) typ1+  let typs = conToTypes con+  let typ2 = if null typs+                then AppT (ConT ''DSH.Q) (ConT ''())+                else foldl AppT (TupleT (length typs)) (map (AppT (ConT ''DSH.Q)) typs)+#if MIN_VERSION_template_haskell(2,9,0)+  let toViewDecTF = TySynInstD ''DSH.ToView $ TySynEqn [typ1] typ2+#else+  let toViewDecTF = TySynInstD ''DSH.ToView [typ1] typ2+#endif+  viewDec <- deriveToView (length typs)+  return [InstanceD context instanceHead [toViewDecTF, viewDec]]++deriveToView :: Int -> Q Dec+deriveToView n = do+  en <- newName "e"+  let ep = VarP en+  let pat1 = ConP 'DSH.Q [ep]++  tupElems <- mapM (\i -> [| DSH.Q $ $(mkTupElemTerm n i (VarE en)) |]) [1..n]++  let body1 = TupE $ tupElems+  let clause1 = Clause [pat1] (NormalB body1) []+  return (FunD 'DSH.view [clause1])++-------------------+-- Deriving Elim --+-------------------++deriveElim :: Name -> Q [Dec]+deriveElim name = do+  info <- reify name+  case info of+    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->+      deriveTyConElim name1 tyVarBndrs cons+    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->+      deriveTyConElim name1 tyVarBndrs [con]+    _ -> fail errMsgExoticType++deriveTyConElim :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]+deriveTyConElim name tyVarBndrs cons = do+  resultTyName <- newName "r"+  let resTy = VarT resultTyName+  let ty = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)+  let context = ClassP ''DSH.QA [resTy] :+                map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)]) tyVarBndrs+  let instanceHead = AppT (AppT (ConT ''DSH.Elim) ty) resTy+  let eliminatorDec = deriveEliminator ty resTy cons+  elimDec <- deriveElimFun cons+  return [InstanceD context instanceHead [eliminatorDec,elimDec]]++-- Deriving the Eliminator type function++deriveEliminator :: Type -> Type -> [Con] -> Dec+deriveEliminator typ resTy cons =+#if MIN_VERSION_template_haskell(2,9,0)+  TySynInstD ''DSH.Eliminator $ TySynEqn [typ,resTy] (deriveEliminatorCons resTy cons)+#else+  TySynInstD ''DSH.Eliminator [typ,resTy] (deriveEliminatorCons resTy cons)+#endif+++deriveEliminatorCons :: Type -> [Con] -> Type+deriveEliminatorCons _ []  = error errMsgExoticType+deriveEliminatorCons resTy cs  =+  foldr (AppT . AppT ArrowT . deriveEliminatorCon resTy)+        (AppT (ConT ''DSH.Q) resTy)+        cs++deriveEliminatorCon :: Type -> Con -> Type+deriveEliminatorCon resTy con =+  foldr (AppT . AppT ArrowT . AppT (ConT ''DSH.Q))+        (AppT (ConT ''DSH.Q) resTy)+        (conToTypes con)++-- Deriving the elim function of the Elim type class++deriveElimFun :: [Con] -> Q Dec+deriveElimFun cons = do+  clause1 <- deriveElimFunClause cons+  return (FunD 'DSH.elim [clause1])++deriveElimFunClause :: [Con] -> Q Clause+deriveElimFunClause cons = do+  en  <- newName "e"+  fns <- mapM (\ _ -> newName "f") cons+  let fes = map VarE fns+  let pats1 = ConP 'DSH.Q [VarP en] : map VarP fns++  fes2 <- zipWithM deriveElimToLamExp fes (map (length . conToTypes) cons)++  let e       = VarE en+  liste <- [| DSH.ListE $(listE $ deriveElimFunClauseExp (return e) (map return fes2)) |]+  let concate = AppE (AppE (ConE 'DSH.AppE) (ConE 'F.Concat)) liste+  let heade   = AppE (AppE (ConE 'DSH.AppE) (ConE 'F.Head)) concate+  let qe      = AppE (ConE 'DSH.Q) heade+  return (Clause pats1 (NormalB qe) [])++deriveElimToLamExp :: Exp -> Int -> Q Exp+deriveElimToLamExp f 0 =+  return (AppE (VarE 'const) (AppE (VarE 'DSH.unQ) f))+deriveElimToLamExp f 1 = do+  xn <- newName "x"+  let xe = VarE xn+  let xp = VarP xn+  let qe = AppE (ConE 'DSH.Q) xe+  let fappe = AppE f qe+  let unqe = AppE (VarE 'DSH.unQ) fappe+  return (LamE [xp] unqe)+deriveElimToLamExp f n = do+  xn <- newName "x"+  let xe = VarE xn+  let xp = VarP xn+  let fste = AppE (AppE (ConE 'DSH.AppE) (ConE 'F.Fst)) xe+  let snde = AppE (AppE (ConE 'DSH.AppE) (ConE 'F.Snd)) xe+  let qe = AppE (ConE 'DSH.Q) fste+  let fappe = AppE f qe+  f' <- deriveElimToLamExp fappe (n - 1)+  return (LamE [xp] (AppE f' snde))++deriveElimFunClauseExp :: Q Exp -> [Q Exp] -> [Q Exp]+deriveElimFunClauseExp _ [] = error errMsgExoticType+deriveElimFunClauseExp e [f] = [ [| DSH.ListE [$f $e] |] ]+deriveElimFunClauseExp e fs = go e fs+  where+  go :: Q Exp -> [Q Exp] -> [Q Exp]+  go _ []  = error errMsgExoticType+  -- FIXME PairE+  go e1 [f1] = do+    [ [| DSH.AppE F.Map (DSH.TupleConstE (DSH.Tuple2E (DSH.LamE $f1) $e1)) |] ]+  go e1 (f1 : fs1) = do+    let mape = [| DSH.AppE F.Map (DSH.TupleConstE (DSH.Tuple2E (DSH.LamE $f1) (DSH.AppE F.Fst $e1))) |]+    let snde = [| DSH.AppE F.Snd $e1 |]+    mape : go snde fs1++---------------------------------+-- Deriving Smart Constructors --+---------------------------------++deriveSmartConstructors :: Name -> Q [Dec]+deriveSmartConstructors name = do+  info <- reify name+  case info of+    TyConI (DataD    _cxt typConName tyVarBndrs cons _names) -> do+      decss <- zipWithM (deriveSmartConstructor typConName tyVarBndrs (length cons))+                        [0 .. ]+                        cons+      return (concat decss)+    TyConI (NewtypeD _cxt typConName tyVarBndrs con  _names) ->+      deriveSmartConstructor typConName tyVarBndrs 1 0 con+    _ -> fail errMsgExoticType++deriveSmartConstructor :: Name -> [TyVarBndr] -> Int -> Int -> Con -> Q [Dec]+deriveSmartConstructor typConName tyVarBndrs n i con = do+  let smartConName = toSmartConName (conToName con)++  let boundTyps = map (VarT . tyVarBndrToName) tyVarBndrs++  let resTyp = AppT (ConT ''DSH.Q) (foldl AppT (ConT typConName) boundTyps)++  let smartConContext = map (ClassP ''DSH.QA . return) boundTyps++  let smartConTyp = foldr (AppT . AppT ArrowT . AppT (ConT ''DSH.Q))+                          resTyp+                          (conToTypes con)++  let smartConDec = SigD smartConName (ForallT tyVarBndrs smartConContext smartConTyp)++  ns <- mapM (\_ -> newName "e") (conToTypes con)+  let es = map VarE ns++  let smartConPat = map (ConP 'DSH.Q . return . VarP) ns++  -- FIXME PairE -> TupleE+  smartConExp <- if null es+                 then return $ ConE 'DSH.UnitE+                 else mkTupConstTerm es +  smartConBody <- deriveSmartConBody n i smartConExp+  let smartConClause = Clause smartConPat (NormalB smartConBody) []++  let funDec = FunD smartConName [smartConClause]++  return [smartConDec,funDec]++deriveSmartConBody :: Int -- Total number of constructors+                   -> Int -- Index of the constructor+                   -> Exp+                   -> Q Exp+deriveSmartConBody 0 _ _ = fail errMsgExoticType+deriveSmartConBody 1 _ e = return (AppE (ConE 'DSH.Q) e)+deriveSmartConBody n i e = do+  listExp <- [| DSH.ListE [ $(return e) ] |]+  emptyListExp <- [| DSH.ListE [] |]+  let lists = concat [ replicate i emptyListExp+                     , [listExp]+                     , replicate (n - i - 1) emptyListExp+                     ]+  tupleExp <- mkTupConstTerm lists+  return $ AppE (ConE 'DSH.Q) tupleExp++toSmartConName :: Name -> Name+toSmartConName name1 = case nameBase name1 of+  "()"                -> mkName "unit"+  '(' : cs            -> mkName ("tuple" ++ show (length (filter (== ',') cs) + 1))+  c : cs | isAlpha c  -> mkName (toLower c : cs)+  cs                  -> mkName (':' : cs)+  +----------------------------------------+-- Generating lifted record selectors --+----------------------------------------+   +{-++For a record declaration like++data R = R { a :: Integer, b :: Text }++we generate the following lifted selectors:++aQ :: Q R -> Q Integer+aQ (view -> (a, _)) = a++bQ :: Q R -> Q Text+bQ (view -> (_, b)) = b++-}+  +-- | Create lifted record selectors+generateTableSelectors :: Name -> Q [Dec]+generateTableSelectors name = do+  info <- reify name+  case info of+    TyConI (DataD _ typName [] [RecC _ fields] _) -> concat <$> mapM instSelectors fields+      where fieldNames    = map (\(f, _, _) -> f) fields+            instSelectors = generateTableSelector typName fieldNames+    _ -> fail errMsgBaseRecCons+    +generateTableSelector :: Name -> [Name] -> VarStrictType -> Q [Dec]+generateTableSelector typeName allFieldNames (fieldName, _strict, typ) = do+  let selName = case fieldName of+                  Name (OccName n) _ -> mkName $ n ++ "Q"+  +  let selType = AppT (AppT ArrowT (AppT (ConT ''DSH.Q) (ConT typeName))) (AppT (ConT ''DSH.Q) typ)+      sigDec  = SigD selName selType+  +  fieldVarName <- newName "x"+  let projectField f | f == fieldName = VarP fieldVarName+      projectField _                  = WildP+  +      tupPat   = map projectField allFieldNames++      argPat   = ViewP (VarE 'DSH.view) (TupP tupPat)+      +      bodyExp  = NormalB $ VarE fieldVarName+      +      funDec   = FunD selName [Clause [argPat] bodyExp []]+      +  +  return [sigDec, funDec]++-- Helper Functions+++-- | From a list of operand patterns, construct a DSH tuple term+-- pattern.+-- @+-- TupleE (Tuple3E a b) -> ...+-- @+mkTuplePat :: [Name] -> Pat+mkTuplePat names = ConP 'DSH.TupleConstE [ConP (innerConst $ length names) (map VarP names)]++-- | Generate a (flat) tuple type from the list of element types.+mkTupleType :: [Type] -> Type+mkTupleType ts = foldl' AppT (TupleT $ length ts) ts++-- | Return the types of all fields of a constructor.+conToTypes :: Con -> [Type]+conToTypes (NormalC _name strictTypes) = map snd strictTypes+conToTypes (RecC _name varStrictTypes) = map (\(_,_,t) -> t) varStrictTypes+conToTypes (InfixC st1 _name st2) = [snd st1,snd st2]+conToTypes (ForallC _tyVarBndrs _cxt con) = conToTypes con++tyVarBndrToName :: TyVarBndr -> Name+tyVarBndrToName (PlainTV name) = name+tyVarBndrToName (KindedTV name _kind) = name++-- | For a given constructor, create a pattern that matches the+-- constructor and binds all fields to the names returned.+conToPattern :: Con -> Q (Pat,[Name])+conToPattern (NormalC name strictTypes) = do+  ns <- mapM (\ _ -> newName "x") strictTypes+  return (ConP name (map VarP ns),ns)+conToPattern (RecC name varStrictTypes) = do+  ns <- mapM (\ _ -> newName "x") varStrictTypes+  return (ConP name (map VarP ns),ns)+conToPattern (InfixC st1 name st2) = do+  ns <- mapM (\ _ -> newName "x") [st1,st2]+  return (ConP name (map VarP ns),ns)+conToPattern (ForallC _tyVarBndr _cxt con) = conToPattern con++conToName :: Con -> Name+conToName (NormalC name _) = name+conToName (RecC name _) = name+conToName (InfixC _ name _) = name+conToName (ForallC _ _ con)	= conToName con++countConstructors :: Name -> Q Int+countConstructors name = do+  info <- reify name+  case info of+    TyConI (DataD    _ _ _ cons _)  -> return (length cons)+    TyConI (NewtypeD {})            -> return 1+    _ -> fail errMsgExoticType++-- Error messages++errMsgExoticType :: String+errMsgExoticType =+  "Automatic derivation of DSH related type class instances only works for Haskell 98\n"+  ++ "types. Derivation of View patterns is only supported for single-constructor data\n"+  ++ "types."++errMsgBaseRecCons :: String+errMsgBaseRecCons =+  "Generation of lifted record selectors is only supported for records of base types."++errMsgTypeTooBroad :: String+errMsgTypeTooBroad =+  "DSH currently supports data types with up to 16 constructors and in which \n"+  ++ "all constructors have up to 16 fields."
+ src/Database/DSH/Frontend/TupleTypes.hs view
@@ -0,0 +1,499 @@+{-# LANGUAGE TemplateHaskell #-}++-- | Generate AST types, functions and instances for tuples.+module Database.DSH.Frontend.TupleTypes+    ( -- * Generate tuple types, functions and instances+      mkQAInstances+    , mkTAInstances+    , mkTupleConstructors+    , mkTupElemType+    , mkTupElemCompile+    , mkReifyInstances+    , mkTranslateTupleTerm+    , mkTranslateType+    , mkViewInstances+    , mkTupleAstComponents+    -- * Helper functions+    , innerConst+    , outerConst+    , tupAccName+    , mkTupElemTerm+    , mkTupConstTerm+    , tupTyConstName+    ) where++import           Control.Applicative+import           Data.List+import           Text.Printf++import           Language.Haskell.TH++import           Database.DSH.Impossible+import           Database.DSH.Common.Nat+import qualified Database.DSH.Common.Type   as T+import qualified Database.DSH.CL.Primitives as CP+import qualified Database.DSH.CL.Lang       as CL++--------------------------------------------------------------------------------+-- Tuple Accessors++-- | Generate all constructors for a given tuple width.+mkTupElemCons :: Name -> Name -> Int -> Q [Con]+mkTupElemCons aTyVar bTyVar width = do+    boundTyVars <- mapM (\i -> newName $ printf "t%d" i) [1..width-1]+    mapM (mkTupElemCon aTyVar bTyVar boundTyVars width) [1..width]++mkTupType :: Int -> Int -> [Name] -> Name -> Type+mkTupType elemIdx width boundTyVars bTyVar =+    let elemTys = map VarT $ take (elemIdx - 1) boundTyVars +                             ++ [bTyVar] +                             ++ drop (elemIdx - 1) boundTyVars+    in foldl' AppT (TupleT width) elemTys++mkTupElemCon :: Name -> Name -> [Name] -> Int -> Int -> Q Con+mkTupElemCon aTyVar bTyVar boundTyVars width elemIdx = do+    let binders = map PlainTV boundTyVars+    let tupTy   = mkTupType elemIdx width boundTyVars bTyVar+    let con     = tupAccName width elemIdx+    let ctx     = [EqualP (VarT aTyVar) tupTy]+    return $ ForallC binders ctx (NormalC con [])++-- | Generate the complete type of tuple acccessors for all tuple+-- widths.+-- +-- @+-- data TupElem a b where +--     Tup2_1 :: TupElem (a, b) a +--     Tup2_2 :: TupElem (a, b) b +--     Tup3_1 :: TupElem (a, b, c) a +--     Tup3_2 :: TupElem (a, b, c) b +--     Tup3_3 :: TupElem (a, b, c) c +--     ...+-- @+-- +-- Due to the lack of support for proper GADT syntax in TH, we have+-- to work with explicit universal quantification:+-- +-- @+-- data TupElem a b =+--     | forall d. a ~ (b, d) => Tup2_1+--     | forall d. a ~ (d, b) => Tup2_2+-- +--     | forall d e. a ~ (b, d, e) => Tup3_1+--     | forall d e. a ~ (d, b, e) => Tup3_2+--     | forall d e. a ~ (d, e, b) => Tup3_3+--     ...+-- @+mkTupElemType :: Int -> Q [Dec]+mkTupElemType maxWidth = do+    let tyName = mkName "TupElem"++    aTyVar <- newName "a"+    bTyVar <- newName "b"+    let tyVars = map PlainTV [aTyVar, bTyVar]++    cons   <- concat <$> mapM (mkTupElemCons aTyVar bTyVar) [2..maxWidth]++    return $ [DataD [] tyName tyVars cons []]+ +--------------------------------------------------------------------------------+-- Translation of tuple accessors to CL++mkCompileMatch :: Name -> (Name, Int) -> Q Match+mkCompileMatch exprName (con, elemIdx) = do+    let translateVar = return $ VarE $ mkName "translate"+        exprVar      = return $ VarE exprName+        idxLit       = return $ LitE $ IntegerL $ fromIntegral elemIdx+    bodyExp  <- [| CP.tupElem (intIndex $idxLit) <$> $translateVar $exprVar |]+    let body = NormalB $ bodyExp+    return $ Match (ConP con []) body []++mkTupElemCompile :: Int -> Q Exp+mkTupElemCompile maxWidth = do+    let cons = concat [ [ (tupAccName width idx, idx)+                        | idx <- [1..width] +                        ] +                      | width <- [2..maxWidth] +                      ]++    exprName <- newName "e"+    opName   <- newName "te"++    matches  <- mapM (mkCompileMatch exprName) cons++    let lamBody = CaseE (VarE opName) matches+    return $ LamE [VarP opName, VarP exprName] lamBody++--------------------------------------------------------------------------------+-- Reify instances for tuple types++reifyType :: Name -> Exp+reifyType tyName = AppE (VarE $ mkName "reify") (SigE (VarE 'undefined) (VarT tyName))++mkReifyFun :: [Name] -> Dec+mkReifyFun tyNames =+    let argTys         = map reifyType tyNames+        body           = AppE (ConE $ mkName "TupleT") +                         $ foldl' AppE (ConE $ tupTyConstName $ length tyNames) argTys+    in FunD (mkName "reify") [Clause [WildP] (NormalB body) []]++mkReifyInstance :: Int -> Dec+mkReifyInstance width =+    let tyNames  = map (\i -> mkName $ "t" ++ show i) [1..width]+        instTy   = AppT (ConT $ mkName "Reify") $ tupleType $ map VarT tyNames+        reifyCxt = map (\tyName -> ClassP (mkName "Reify") [VarT tyName]) tyNames+        +    in InstanceD reifyCxt instTy [mkReifyFun tyNames]++mkReifyInstances :: Int -> Q [Dec]+mkReifyInstances maxWidth = return $ map mkReifyInstance [2..maxWidth]++--------------------------------------------------------------------------------+-- QA instances for tuple types++mkToExp :: Int -> [Name] -> Dec+mkToExp width elemNames =+    let toExpVar   = VarE $ mkName "toExp"+        elemArgs   = map (\n -> AppE toExpVar (VarE n)) elemNames+        body       = NormalB $ AppE (ConE outerConst) +                             $ foldl' AppE (ConE $ innerConst width) elemArgs+        tupClause  = Clause [TupP $ map VarP elemNames] body []+    in FunD (mkName "toExp") [tupClause]++mkFrExp :: Int -> [Name] -> Q Dec+mkFrExp width elemNames = do+    impossibleExpr <- [| error $(litE $ StringL $ printf "frExp %d" width) |]+    let tupPattern       = ConP outerConst [ConP (innerConst width) (map VarP elemNames) ]+        tupleExpr        = TupE $ map (\n -> AppE (VarE $ mkName "frExp") (VarE n)) elemNames+        tupleClause      = Clause [tupPattern] (NormalB tupleExpr) []+        impossibleClause = Clause [WildP] (NormalB impossibleExpr) []+    return $ FunD (mkName "frExp") [tupleClause, impossibleClause]++mkRep :: Int -> [Name] -> Type -> Dec+mkRep width tyNames tupTyPat =+    let resTy    = foldl' AppT (TupleT width)+                   $ map (AppT $ ConT $ mkName "Rep") +                   $ map VarT tyNames+    in TySynInstD (mkName "Rep") (TySynEqn [tupTyPat] resTy)++mkQAInstance :: Int -> Q Dec+mkQAInstance width = do+    let tyNames = map (\i -> mkName $ "t" ++ show i) [1..width]+        tupTy   = tupleType $ map VarT tyNames+        instTy  = AppT (ConT $ mkName "QA") tupTy+        qaCxt   = map (\tyName -> ClassP (mkName "QA") [VarT tyName]) tyNames+        rep     = mkRep width tyNames tupTy+        toExp   = mkToExp width tyNames+    frExp <- mkFrExp width tyNames+    return $ InstanceD qaCxt instTy [rep, toExp, frExp]++-- | Generate QA instances for tuple types according to the following template:+-- +-- @+-- instance (QA t1, ..., QA tn) => QA (t1, ..., tn) where+--   type Rep (t1, ..., tn) = (Rep t1, ..., Rep tn)+--   toExp (v1, ..., vn) = TupleConstE (Tuple<n>E (toExp v1) ... (toExp vn))+--   frExp (TupleConstE (Tuple<n>E v1 ... vn)) = (frExp v1, ... b, frExp vn)+--   frExp _ = $impossible+-- @+mkQAInstances :: Int -> Q [Dec]+mkQAInstances maxWidth = mapM mkQAInstance [2..maxWidth]++--------------------------------------------------------------------------------+-- TA instances for tuple types++mkTAInstance :: Int -> Dec+mkTAInstance width =+    let tyNames = map (\i -> mkName $ "t" ++ show i) [1..width]+        tupTy   = foldl' AppT (TupleT width) $ map VarT tyNames+        instTy  = AppT (ConT $ mkName "TA") tupTy+        taCxt   = map (\tyName -> ClassP (mkName "BasicType") [VarT tyName]) tyNames+    in InstanceD taCxt instTy []++-- | Generate TA instances for tuple types according to the following template:+-- +-- @+-- instance (BasicType t1, ..., BasicType tn) => TA (t1, ..., tn) where+-- @+mkTAInstances :: Int -> Q [Dec]+mkTAInstances maxWidth = return $ map mkTAInstance [2..maxWidth]++--------------------------------------------------------------------------------+-- Smart constructors for tuple values++tupConName :: Int -> Name+tupConName width = mkName $ printf "tup%d" width++mkArrowTy :: Type -> Type -> Type+mkArrowTy domTy coDomTy = AppT (AppT ArrowT domTy) coDomTy++mkTupleConstructor :: Int -> [Dec]+mkTupleConstructor width =+    let tyNames   = map (\i -> mkName $ "t" ++ show i) [1..width]++        -- Type stuff+        tupTy     = AppT (ConT qName) $ foldl' AppT (TupleT width) $ map VarT tyNames+        elemTys   = map (AppT (ConT qName)) $ map VarT tyNames+        arrowTy   = foldr mkArrowTy tupTy elemTys+        qaConstr  = map (\n -> ClassP (mkName "QA") [VarT n]) tyNames+        funTy     = ForallT (map PlainTV tyNames) qaConstr arrowTy++        -- Term stuff+        qPats     = map (\n -> ConP qName [VarP n]) tyNames +        tupConApp = foldl' AppE (ConE $ innerConst width) $ map VarE tyNames+        bodyExp   = AppE (ConE qName) (AppE (ConE outerConst) tupConApp)++        sig       = SigD (tupConName width) funTy+        body      = FunD (tupConName width) [Clause qPats (NormalB bodyExp) []]+    in [sig, body]++-- | Construct smart constructors for tuple types according to the+-- following template.+-- +-- @+-- tup<n> :: (QA t1, ...,QA tn) => Q t1 -> ... -> Q tn -> Q (t1, ..., tn)+-- tup<n> (Q v1) ... (Q vn)= Q (TupleConstE (Tuple<n>E v1 ... vn))+-- @+mkTupleConstructors :: Int -> Q [Dec]+mkTupleConstructors maxWidth = return $ concatMap mkTupleConstructor [2..maxWidth]++--------------------------------------------------------------------------------+-- Translation function for tuple constructors in terms++{-+\t -> case t of+    Tuple2E a b -> do+        a' <- translate a+        b' <- translate b+        return $ CL.MkTuple (T.TupleT $ map T.typeOf [a', b']) [a', b']+    Tuple3E a b c -> ...+-}++mkTransBind :: Name -> Name -> Stmt+mkTransBind argName resName =+    BindS (VarP resName) (AppE (VarE $ mkName "translate") (VarE argName))++-- | Generate the translation case for a particular tuple value+-- constructor.+mkTranslateTermMatch :: Int -> Q Match+mkTranslateTermMatch width = do+    let names          = map (\c -> [c]) $ take width ['a' .. 'z']+        subTermNames   = map mkName names+        transTermNames = map (mkName . (++ "'")) names+        transBinds     = zipWith mkTransBind subTermNames transTermNames+        +        transTerms     = listE $ map varE transTermNames+    conStmt <- NoBindS <$> +               [| return $ CL.MkTuple (T.TupleT $ map T.typeOf $transTerms) $transTerms |]+    let matchBody = DoE $ transBinds ++ [conStmt]+        matchPat  = ConP (innerConst width) (map VarP subTermNames)+    return $ Match matchPat (NormalB matchBody) []++-- | Generate the lambda expression that translates frontend tuple+-- value constructors into CL tuple constructors.+mkTranslateTupleTerm :: Int -> Q Exp+mkTranslateTupleTerm maxWidth = do+    lamArgName <- newName "tupleConst"++    matches    <- mapM mkTranslateTermMatch [2..maxWidth]++    let lamBody = CaseE (VarE lamArgName) matches+    return $ LamE [VarP lamArgName] lamBody++--------------------------------------------------------------------------------+-- Translation function for tuple types++{-+\t -> case t of+    Tuple3T t1 t2 t3 -> T.TupleT [translateType t1, translateType t2, translateType t3]+-}++mkTranslateTypeMatch :: Int -> Q Match+mkTranslateTypeMatch width = do+    let subTyNames   = map mkName $ map (\c -> [c]) $ take width ['a' .. 'z']+        matchPat     = ConP (tupTyConstName width) (map VarP subTyNames)+        transElemTys = ListE $ map (\n -> AppE (VarE $ mkName "translateType") (VarE n)) subTyNames++    let matchBody  = AppE (ConE 'T.TupleT) transElemTys+        +    return $ Match matchPat (NormalB matchBody) []++mkTranslateType :: Int -> Q Exp+mkTranslateType maxWidth = do+    lamArgName <- newName "typeConst"+    matches    <- mapM mkTranslateTypeMatch [2..maxWidth]++    let lamBody = CaseE (VarE lamArgName) matches+    return $ LamE [VarP lamArgName] lamBody++--------------------------------------------------------------------------------+-- View instances++{-+instance (QA a,QA b,QA c) => View (Q (a,b,c)) where+    type ToView (Q (a,b,c)) = (Q a,Q b,Q c)+    view (Q e) = ( Q (AppE (TupElem Tup3_1) e)+                 , Q (AppE (TupElem Tup3_2) e)+                 , Q (AppE (TupElem Tup3_3) e)+                 )+-}++mkToView :: [Name] -> Type -> Dec+mkToView names tupTyPat =+    let qTupPat  = AppT (ConT qName) tupTyPat+        resTupTy = tupleType $ map (\n -> AppT (ConT qName) (VarT n)) names+    in TySynInstD (mkName "ToView") (TySynEqn [qTupPat] resTupTy)++mkViewFun :: Int -> Q Dec+mkViewFun width = do+    expName <- newName "e"+    let expVar      = VarE expName+        qPat        = ConP qName [VarP expName]++    viewBodyExp <- TupE <$> mapM (\idx -> appE (conE qName) $ mkTupElemTerm width idx expVar)+                                 [1..width] ++    let viewClause  = Clause [qPat] (NormalB viewBodyExp) []+        +    return $ FunD (mkName "view") [viewClause]++mkViewInstance :: Int -> Q Dec+mkViewInstance width = do+    let names     = map (\i -> mkName $ "t" ++ show i) [1..width]+        tupTy     = tupleType $ map VarT names+        instTy    = AppT (ConT $ mkName "View") (AppT (ConT qName) tupTy)++        viewCxt   = map (\n -> ClassP (mkName "QA") [VarT n]) names+        toViewDec = mkToView names tupTy+    viewDec <- mkViewFun width+    return $ InstanceD viewCxt instTy [toViewDec, viewDec]++mkViewInstances :: Int -> Q [Dec]+mkViewInstances maxWidth = mapM mkViewInstance [2..maxWidth]++--------------------------------------------------------------------------------+-- Generate the 'TupleConst' type++tupElemTyName :: Int -> Q Name+tupElemTyName i = newName $ printf "t%d" i++-- | Generate a single constructor for the 'TabTuple' type.+mkTupleCons :: Name -> (Int -> Name) -> (Type -> Type) -> Int -> Q Con+mkTupleCons tupTyName conName elemTyCons width = do++    tupElemTyNames <- mapM tupElemTyName [1..width]++    let tyVarBinders     = map PlainTV tupElemTyNames++        -- (t1, ..., t<n>)+        tupTy            = foldl' AppT (TupleT width)+                           $ map VarT tupElemTyNames+    +        -- a ~ (t1, ..., t<n>)+        tupConstraint    = EqualP (VarT tupTyName) tupTy++        -- Reify t1, ..., Reify t<n>+        reifyConstraints = map (\n -> ClassP (mkName "Reify") [VarT n]) tupElemTyNames++        constraints      = tupConstraint : reifyConstraints ++    let -- '(Exp/Type t1) ... (Exp/Type t<n>)'+        elemTys = [ (NotStrict, elemTyCons (VarT t))+                  | t <- tupElemTyNames+                  ]+    +    return $ ForallC tyVarBinders constraints+           $ NormalC (conName width) elemTys++-- | Generate the types for AST type and term tuple constructors: 'TupleConst' and +-- 'TupleType'. The first parameter is the name of the type. The second parameter+-- is the type constructor for element fields and the third parameter generates+-- the constructor name for a given tuple width.+-- +-- @+-- data TupleConst a where+--     Tuple<n>E :: (Reify t1, ..., Reify t<n>) => Exp t1 +--                                              -> ... +--                                              -> Exp t<n> +--                                              -> TupleConst (t1, ..., t<n>)+-- @+-- +-- Because TH does not directly support GADT syntax, we have to+-- emulate it using explicit universal quantification:+-- +-- @+-- data TupleConst a =+--     forall t1, ..., t<n>. a ~ (t1, ..., t<n>),+--                           Reify t1,+--                           ...+--                           Reify t<n> =>+--                           Exp t1 -> ... -> Exp t<n>+-- @+mkTupleASTTy :: Name -> (Type -> Type) -> (Int -> Name) -> Int -> Q [Dec]+mkTupleASTTy tyName elemTyCons conName maxWidth = do+    tupTyName <- newName "a"+    cons      <- mapM (mkTupleCons tupTyName conName elemTyCons) [2..maxWidth]+    +    return $ [DataD [] tyName  [PlainTV tupTyName] cons []]++-- | Generate the 'TupleConst' AST type for tuple term construction+mkAstTupleConst :: Int -> Q [Dec]+mkAstTupleConst maxWidth =+    mkTupleASTTy (mkName "TupleConst") expCon innerConst maxWidth+  where+    expCon = AppT $ ConT $ mkName "Exp"++-- | Generate the 'TupleConst' AST type for tuple term construction+mkAstTupleType :: Int -> Q [Dec]+mkAstTupleType maxWidth =+    mkTupleASTTy (mkName "TupleType") expCon tupTyConstName maxWidth+  where+    expCon = AppT $ ConT $ mkName "Type"++mkTupleAstComponents :: Int -> Q [Dec]+mkTupleAstComponents maxWidth = (++) <$> mkAstTupleConst maxWidth <*> mkAstTupleType maxWidth++++--------------------------------------------------------------------------------+-- Helper functions++-- | The name of the constructor that constructs a tuple construction+-- term.+outerConst :: Name+outerConst = mkName "TupleConstE"++-- | The name of the constructor for a given tuple width.+innerConst :: Int -> Name+innerConst width = mkName $ printf "Tuple%dE" width++-- | The name of a tuple access constructor for a given tuple width+-- and element index.+tupAccName :: Int -> Int -> Name+tupAccName width elemIdx = mkName $ printf "Tup%d_%d" width elemIdx+    +-- | The name of the tuple type constructor for a given tuple width.+tupTyConstName :: Int -> Name+tupTyConstName width = mkName $ printf "Tuple%dT" width++-- |+tupleType :: [Type] -> Type+tupleType elemTypes = foldl' AppT (TupleT width) elemTypes+  where+    width = length elemTypes++qName :: Name+qName = mkName "Q"++-- | Construct a DSH term that accesses a specificed tuple element.+mkTupElemTerm :: Int -> Int -> Exp -> Q Exp+mkTupElemTerm width idx arg = do+    let ta = ConE $ tupAccName width idx+    return $ AppE (AppE (ConE $ mkName "AppE") (AppE (ConE $ mkName "TupElem") ta)) arg++-- | From a list of operand terms, construct a DSH tuple term.+mkTupConstTerm :: [Exp] -> Q Exp+mkTupConstTerm ts +    | length ts <= 16 = return $ AppE (ConE $ mkName "TupleConstE") +                               $ foldl' AppE (ConE $ innerConst $ length ts) ts+    | otherwise       = impossible
src/Database/DSH/Impossible.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -module Database.DSH.Impossible (impossible) where+module Database.DSH.Impossible (impossible, unimplemented) where  import qualified Language.Haskell.TH as TH @@ -8,5 +8,12 @@ impossible = do   loc <- TH.location   let pos =  (TH.loc_filename loc, fst (TH.loc_start loc), snd (TH.loc_start loc))-  let message = "DSH: Impossbile happend at " ++ show pos+  let message = "DSH: Impossible happened at " ++ show pos+  return (TH.AppE (TH.VarE 'error) (TH.LitE (TH.StringL message)))++unimplemented :: TH.ExpQ+unimplemented = do+  loc <- TH.location+  let pos =  (TH.loc_filename loc, fst (TH.loc_start loc), snd (TH.loc_start loc))+  let message = "DSH: Unimplemented at " ++ show pos   return (TH.AppE (TH.VarE 'error) (TH.LitE (TH.StringL message)))
− src/Database/DSH/Internals.hs
@@ -1,201 +0,0 @@-{-# LANGUAGE FlexibleContexts      #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies          #-}--module Database.DSH.Internals where--import Data.Text (Text)--data Exp a where-  UnitE     :: Exp ()-  BoolE     :: Bool    -> Exp Bool-  CharE     :: Char    -> Exp Char-  IntegerE  :: Integer -> Exp Integer-  DoubleE   :: Double  -> Exp Double-  TextE     :: Text    -> Exp Text-  PairE     :: (Reify a, Reify b)  => Exp a -> Exp b -> Exp (a,b)-  ListE     :: (Reify a)           => [Exp a] -> Exp [a]-  AppE      :: (Reify a, Reify b)  => Fun a b -> Exp a -> Exp b-  LamE      :: (Reify a, Reify b)  => (Exp a -> Exp b) -> Exp (a -> b)-  VarE      :: (Reify a)           => Integer -> Exp a-  TableE    :: (Reify a)           => Table -> Exp [a]--data Table = TableDB String [[String]] | TableCSV  String deriving (Eq, Ord, Show)--data Type a where-  UnitT     :: Type ()-  BoolT     :: Type Bool-  CharT     :: Type Char-  IntegerT  :: Type Integer-  DoubleT   :: Type Double-  TextT     :: Type Text-  PairT     :: (Reify a,Reify b)  => Type a -> Type b -> Type (a,b)-  ListT     :: (Reify a)          => Type a -> Type [a]-  ArrowT    :: (Reify a,Reify b)  => Type a -> Type b -> Type (a -> b)--data Fun a b where-    Not             :: Fun Bool Bool-    IntegerToDouble :: Fun Integer Double-    And             :: Fun [Bool] Bool-    Or              :: Fun [Bool] Bool-    Concat          :: (Reify a) => Fun [[a]] [a]-    Head            :: Fun [a] a-    Tail            :: Fun [a] [a]-    Init            :: Fun [a] [a]-    Last            :: Fun [a] a-    Null            :: Fun [a] Bool-    Length          :: Fun [a] Integer-    Reverse         :: Fun [a] [a]-    Fst             :: Fun (a,b) a-    Snd             :: Fun (a,b) b-    Sum             :: Fun [a] a-    Maximum         :: Fun [a] a-    Minimum         :: Fun [a] a-    Nub             :: Fun [a] [a]-    Add             :: Fun (a,a) a-    Mul             :: Fun (a,a) a-    Sub             :: Fun (a,a) a-    Div             :: Fun (a,a) a-    Lt              :: Fun (a,a) Bool-    Lte             :: Fun (a,a) Bool-    Equ             :: Fun (a,a) Bool-    Gte             :: Fun (a,a) Bool-    Gt              :: Fun (a,a) Bool-    Conj            :: Fun (Bool,Bool) Bool-    Disj            :: Fun (Bool,Bool) Bool-    Min             :: Fun (a,a) a-    Max             :: Fun (a,a) a-    Cons            :: Fun (a,[a]) [a]-    Take            :: Fun (Integer,[a]) [a]-    Drop            :: Fun (Integer,[a]) [a]-    Index           :: Fun ([a],Integer) a-    SplitAt         :: Fun (Integer,[a]) ([a],[a])-    Zip             :: Fun ([a],[b]) [(a,b)]-    Map             :: Fun (a -> b,[a]) [b]-    Filter          :: Fun (a -> Bool,[a]) [a]-    GroupWithKey    :: Fun (a -> b,[a]) [(b,[a])]-    SortWith        :: Fun (a -> b,[a]) [a]-    TakeWhile       :: Fun (a -> Bool,[a]) [a]-    DropWhile       :: Fun (a -> Bool,[a]) [a]-    Cond            :: Fun (Bool,(a,a)) a--newtype Q a = Q (Exp (Rep a))---- Classes--class Reify a where-  reify :: a -> Type a--class (Reify (Rep a)) => QA a where-  type Rep a-  toExp :: a -> Exp (Rep a)-  frExp :: Exp (Rep a) -> a--class (QA a,QA r) => Elim a r where-  type Eliminator a r-  elim :: Q a -> Eliminator a r--class BasicType a where--class TA a where--class View a where-  type ToView a-  view :: a -> ToView a---- Show instances--instance Show (Type a) where-  show UnitT = "()"-  show BoolT = "Bool"-  show CharT = "Char"-  show IntegerT = "Integer"-  show DoubleT = "Double"-  show TextT = "Text"-  show (PairT l r) = "(" ++ show l ++ ", " ++ show r ++ ")"-  show (ListT t) = "[" ++ show t ++ "]"-  show (ArrowT t1 t2) = "(" ++ show t1 ++ " -> " ++ show t2 ++ ")"--instance Show (Fun a b) where-    show Fst = "fst"-    show Snd = "snd"-    show Not = "not"-    show Concat = "concat"-    show Head = "head"-    show Tail = "tail"-    show Init = "init"-    show Last = "last"-    show Null = "null"-    show Length = "length"-    show Reverse = "reverse"-    show And = "and"-    show Or = "or"-    show Sum = "sum"-    show Maximum = "maximum"-    show Minimum = "minimum"-    show Nub = "nub"-    show IntegerToDouble = "integerToDouble"-    show Add = "+"-    show Mul = "*"-    show Sub = "-"-    show Div = "/"-    show Lt  = "<"-    show Lte = "<="-    show Equ = "=="-    show Gte = ">="-    show Gt  = ">"-    show Conj = "&&"-    show Disj = "||"-    show Min  = "min"-    show Max  = "max"-    show Cons = "cons"-    show Take = "take"-    show Drop = "drop"-    show Index = "index"-    show SplitAt = "splitAt"-    show Zip = "zip"-    show Map = "map"-    show Filter = "filter"-    show GroupWithKey = "groupWithKey"-    show SortWith = "sortWith"-    show TakeWhile = "takeWhile"-    show DropWhile = "dropWhile"-    show Cond = "cond"---- Reify instances--instance Reify () where-  reify _ = UnitT--instance Reify Bool where-  reify _ = BoolT--instance Reify Char where-  reify _ = CharT--instance Reify Integer where-  reify _ = IntegerT--instance Reify Double where-  reify _ = DoubleT--instance Reify Text where-  reify _ = TextT--instance (Reify a, Reify b) => Reify (a,b) where-  reify _ = PairT (reify (undefined :: a)) (reify (undefined :: b))--instance (Reify a) => Reify [a] where-  reify _ = ListT (reify (undefined :: a))--instance (Reify a, Reify b) => Reify (a -> b) where-  reify _ = ArrowT (reify (undefined :: a)) (reify (undefined :: b))---- Utility functions--unQ :: Q a -> Exp (Rep a)-unQ (Q e) = e--toLam :: (QA a,QA b) => (Q a -> Q b) -> Exp (Rep a) -> Exp (Rep b)-toLam f = unQ . f . Q
− src/Database/DSH/Interpreter.hs
@@ -1,382 +0,0 @@-{-# LANGUAGE GADTs               #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell     #-}---- | This module provides the reference implementation of DSH by interpreting--- the embedded representation.--module Database.DSH.Interpreter (fromQ) where--import Database.DSH.Internals-import Database.DSH.Impossible-import Database.DSH.CSV--import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import Database.HDBC-import Data.List--fromQ :: (QA a, IConnection conn) => conn -> Q a -> IO a-fromQ c (Q e) = fmap frExp (evaluate c e)--evaluate :: forall a conn. (Reify a, IConnection conn) => conn -> Exp a -> IO (Exp a)-evaluate c e = case e of-    UnitE -> return UnitE-    BoolE b -> return $ BoolE b-    CharE ch -> return $ CharE ch-    IntegerE i -> return $ IntegerE i-    DoubleE d -> return $ DoubleE d-    TextE t -> return $ TextE t -    VarE _ -> $impossible-    LamE _ -> $impossible-    PairE e1 e2 -> do-      e1' <- evaluate c e1-      e2' <- evaluate c e2-      return (PairE e1' e2')-    ListE es -> do-       es1 <- mapM (evaluate c) es-       return $ ListE es1 -    AppE Cond (PairE cond (PairE a b)) -> do-      (BoolE c1) <- evaluate c cond-      if c1 then evaluate c a else evaluate c b-    AppE Cons (PairE a as) -> do-      a1 <- evaluate c a-      (ListE as1) <- evaluate c as-      return $ ListE (a1 : as1)-    AppE Head as -> do-      (ListE as1) <- evaluate c as-      return $ head as1-    AppE Tail as -> do-      (ListE as1) <- evaluate c as-      return $ ListE (tail as1)-    AppE Take (PairE i as) -> do-      (IntegerE i1) <- evaluate c i-      (ListE as1) <- evaluate c as-      return $ ListE (take (fromIntegral i1) as1)-    AppE Drop (PairE i as) -> do-      (IntegerE i1) <- evaluate c i-      (ListE as1) <- evaluate c as-      return $ ListE (drop (fromIntegral i1) as1)-    AppE Map (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      evaluate c $ ListE (map f as1)-    AppE Filter (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      (ListE as2) <- evaluate c (ListE (map f as1))-      return $ ListE (map fst (filter (\(_,BoolE b) -> b) (zip as1 as2))) -    AppE GroupWithKey (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      (ListE ks1) <- evaluate c (ListE (map f as1))-      return $ ListE-             $ map (\kas1 -> PairE (fst (head kas1)) (ListE (map snd kas1)))-             $ groupBy (\(k1,_) (k2,_) -> equExp k1 k2)-             $ sortBy (\(k1,_) (k2,_) -> compareExp k1 k2)-             $ zip ks1 as1-    AppE SortWith (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      (ListE as2) <- evaluate c $ ListE (map f as1) -      return $ ListE -             $ map fst-             $ sortBy (\(_,a1) (_,a2) -> compareExp a1 a2)-             $ zip as1 as2-    (AppE Max (PairE e1 e2)) ->-      case reify (undefined :: a) of-          IntegerT -> do (IntegerE v1) <- evaluate c e1-                         (IntegerE v2) <- evaluate c e2-                         return $ IntegerE (max v1 v2)-          DoubleT  -> do (DoubleE v1) <- evaluate c e1-                         (DoubleE v2) <- evaluate c e2-                         return $ DoubleE (max v1 v2)-          _ -> $impossible-    (AppE Min (PairE e1 e2)) ->-      case reify (undefined :: a) of-          IntegerT -> do (IntegerE v1) <- evaluate c e1-                         (IntegerE v2) <- evaluate c e2-                         return $ IntegerE (min v1 v2)-          DoubleT  -> do (DoubleE v1) <- evaluate c e1-                         (DoubleE v2) <- evaluate c e2-                         return $ DoubleE (min v1 v2)-          _ -> $impossible-    AppE Last as -> do-      (ListE as1) <- evaluate c as-      return $ last as1-    AppE Init as -> do-      (ListE as1) <- evaluate c as-      return $ ListE (init as1)-    AppE Null as -> do-      (ListE as1) <- evaluate c as-      return $ BoolE (null as1)-    AppE Length as -> do-     (ListE as1) <- evaluate c as-     return $ IntegerE (fromIntegral $ length as1)-    AppE Index (PairE as i) -> do-     (IntegerE i1) <- evaluate c i-     (ListE as1) <- evaluate c as-     return $ as1 !! fromIntegral i1-    AppE Reverse as -> do-      (ListE as1) <- evaluate c as-      return $ ListE (reverse as1)-    AppE And as -> do-      (ListE as1) <- evaluate c as-      return $ BoolE (all (\(BoolE b) -> b) as1)-    AppE Or as -> do-      (ListE as1) <- evaluate c as-      return $ BoolE (any (\(BoolE b) -> b) as1)-    (AppE Sum as) -> do-      let ty = reify (undefined :: a)-      (ListE as1) <- evaluate c as-      case ty of-          IntegerT -> return $ IntegerE (sum $ map (\(IntegerE i) -> i) as1)-          DoubleT  -> return $ DoubleE (sum $ map (\(DoubleE d) -> d) as1)-          _ -> $impossible-    AppE Concat as -> do-      (ListE as1) <- evaluate c as-      return $ ListE (concatMap (\(ListE as2) -> as2) as1)-    AppE Maximum as -> do-      (ListE as1) <- evaluate c as-      return $ maximumBy compareExp as1-    AppE Minimum as -> do-      (ListE as1) <- evaluate c as-      return $ minimumBy compareExp as1-    AppE SplitAt (PairE i as) -> do-      (IntegerE i1) <- evaluate c i-      (ListE as1) <- evaluate c as-      let r = splitAt (fromIntegral i1) as1-      return $ PairE (ListE (fst r)) (ListE (snd r)) -    AppE TakeWhile (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      (ListE as2) <- evaluate c (ListE (map f as1))-      return $ ListE (map fst $ takeWhile (\(_,BoolE b) -> b) $ zip as1 as2)-    AppE DropWhile (PairE (LamE f) as) -> do-      (ListE as1) <- evaluate c as-      (ListE as2) <- evaluate c (ListE (map f as1))-      return $ ListE (map fst $ dropWhile (\(_,BoolE b) -> b) $ zip as1 as2)-    AppE Zip (PairE as bs) -> do-      (ListE as1) <- evaluate c as-      (ListE bs1) <- evaluate c bs-      return $ ListE (zipWith PairE as1 bs1)-    AppE Nub as -> do-      (ListE as1) <- evaluate c as-      return $ ListE (nubBy equExp as1)-    AppE Fst a -> do-      (PairE a1 _) <- evaluate c a-      return a1-    AppE Snd a -> do-      (PairE _ a1) <- evaluate c a-      return a1-    (AppE Add (PairE e1 e2)) -> do-      let ty = reify (undefined :: a)-      case ty of-         IntegerT -> do-                      (IntegerE i1) <- evaluate c e1-                      (IntegerE i2) <- evaluate c e2-                      return $ IntegerE (i1 + i2)-         DoubleT  -> do-                      (DoubleE d1) <- evaluate c e1-                      (DoubleE d2) <- evaluate c e2-                      return $ DoubleE (d1 + d2)-         _ -> $impossible-    (AppE Sub (PairE e1 e2)) -> do-      let ty = reify (undefined :: a)-      case ty of-         IntegerT -> do-                      (IntegerE i1) <- evaluate c e1-                      (IntegerE i2) <- evaluate c e2-                      return $ IntegerE (i1 - i2)-         DoubleT  -> do-                      (DoubleE d1) <- evaluate c e1-                      (DoubleE d2) <- evaluate c e2-                      return $ DoubleE (d1 - d2)-         _ -> $impossible-    (AppE Mul (PairE e1 e2)) -> do-      let ty = reify (undefined :: a)-      case ty of-         IntegerT -> do-                      (IntegerE i1) <- evaluate c e1-                      (IntegerE i2) <- evaluate c e2-                      return $ IntegerE (i1 * i2)-         DoubleT  -> do-                      (DoubleE d1) <- evaluate c e1-                      (DoubleE d2) <- evaluate c e2-                      return $ DoubleE (d1 * d2)-         _ -> $impossible-    (AppE Div (PairE e1 e2)) -> do-      let ty = reify (undefined :: a)-      case ty of-         DoubleT  -> do-                      (DoubleE d1) <- evaluate c e1-                      (DoubleE d2) <- evaluate c e2-                      return $ DoubleE (d1 / d2)-         _ -> $impossible-    AppE IntegerToDouble e1 -> do-      (IntegerE i1) <- evaluate c e1-      return $ DoubleE (fromInteger i1)-    AppE Equ (PairE e1 e2) -> do-      e3 <- evaluate c e1-      e4 <- evaluate c e2-      return $ BoolE $ equExp e3 e4-    AppE Lt (PairE e1 e2) -> do-      e3 <- evaluate c e1-      e4 <- evaluate c e2-      return $ BoolE $ ltExp e3 e4-    AppE Lte (PairE e1 e2) -> do-      e3 <- evaluate c e1-      e4 <- evaluate c e2-      return $ BoolE $ lteExp e3 e4-    AppE Gte (PairE e1 e2) -> do-      e3 <- evaluate c e1-      e4 <- evaluate c e2-      return $ BoolE $ gteExp e3 e4-    AppE Gt (PairE e1 e2) -> do-      e3 <- evaluate c e1-      e4 <- evaluate c e2-      return $ BoolE $ gtExp e3 e4-    AppE Not e1 -> do-      (BoolE b1) <- evaluate c e1-      return $ BoolE (not b1)-    AppE Conj (PairE e1 e2) -> do-      (BoolE b1) <- evaluate c e1-      (BoolE b2) <- evaluate c e2-      return $ BoolE (b1 && b2)-    AppE Disj (PairE e1 e2) -> do-      (BoolE b1) <- evaluate c e1-      (BoolE b2) <- evaluate c e2-      return $ BoolE (b1 || b2) -    (TableE (TableDB tName _)) -> -      let ty = reify (undefined :: a)-      in case ty of-          ListT tType -> do-            tDesc <- describeTable c (escape tName)-            let columnNames = intercalate " , " $ map (\s -> "\"" ++ s ++ "\"") $ sort $ map fst tDesc-            let query = "SELECT " ++ columnNames ++ " FROM " ++ "\"" ++ escape tName ++ "\""-            -- print query-            fmap (sqlToExpWithType (escape tName) tType) (quickQuery c query [])-          _ -> $impossible-    (TableE (TableCSV filename)) -> csvImport filename (reify (undefined :: a))-    _ -> $impossible--compareExp :: Exp a -> Exp a -> Ordering-compareExp UnitE UnitE                       = EQ-compareExp (BoolE v1) (BoolE v2)             = compare v1 v2-compareExp (CharE v1) (CharE v2)             = compare v1 v2-compareExp (IntegerE v1) (IntegerE v2)       = compare v1 v2-compareExp (DoubleE v1) (DoubleE v2)         = compare v1 v2-compareExp (TextE v1) (TextE v2)             = compare v1 v2-compareExp (PairE a1 b1) (PairE a2 b2)       = case compareExp a1 a2 of-                                                 EQ -> compareExp b1 b2-                                                 LT -> LT-                                                 GT -> GT-compareExp (ListE []) (ListE [])             = EQ-compareExp (ListE (_ : _)) (ListE [])        = GT-compareExp (ListE []) (ListE (_ : _))        = LT-compareExp (ListE (a : as)) (ListE (b : bs)) = case compareExp a b of-                                                 EQ -> compareExp (ListE as) (ListE bs)-                                                 LT -> LT-                                                 GT -> GT-compareExp _ _ = $impossible--equExp :: Exp a -> Exp a -> Bool-equExp a b = case compareExp a b of-              EQ -> True-              _  -> False--ltExp :: Exp a -> Exp a -> Bool-ltExp a b = case compareExp a b of-              LT -> True-              _  -> False--lteExp :: Exp a -> Exp a -> Bool-lteExp a b = case compareExp a b of-               GT -> False-               _  -> True--gteExp :: Exp a -> Exp a -> Bool-gteExp a b = case compareExp a b of-               LT -> False-               _  -> True--gtExp :: Exp a -> Exp a -> Bool-gtExp a b = case compareExp a b of-               GT -> True-               _  -> False--escape :: String -> String-escape []                  = []-escape (c : cs) | c == '"' = '\\' : '"' : escape cs-escape (c : cs)            =          c : escape cs---- | Read SQL values into 'Norm' values-sqlToExpWithType :: (Reify a)-                 => String  -- ^ Table name, used to generate more informative error messages-                 -> Type a-                 -> [[SqlValue]]-                 -> Exp [a]-sqlToExpWithType tName ty = ListE . map (sqlValueToNorm ty)-  where-    sqlValueToNorm :: Type a -> [SqlValue] -> Exp a-    sqlValueToNorm (PairT t1 t2) s = let v1 = sqlValueToNorm t1 $ take (sizeOfType t1) s-                                         v2 = sqlValueToNorm t2 $ drop (sizeOfType t1) s-                                      in PairE v1 v2-    -- On a single value, just compare the 'Type' and convert the 'SqlValue' to-    -- a Norm value on match-    sqlValueToNorm t [s] = if t `typeMatch` s-                      then convert s t-                      else typeError t [s]-    -- Everything else will raise an error-    sqlValueToNorm t s = typeError t s--    typeError :: Type a -> [SqlValue] -> b-    typeError t s = error $-        "ferry: Type mismatch on table \"" ++ tName ++ "\":"-        ++ "\n\tExpected table type: " ++ show t-        ++ "\n\tTable entry: " ++ show s--convert :: SqlValue -> Type a -> Exp a-convert SqlNull         UnitT    = UnitE-convert (SqlInteger i)  IntegerT = IntegerE i-convert (SqlInt32 i)    IntegerT = IntegerE $ fromIntegral i-convert (SqlInt64 i)    IntegerT = IntegerE $ fromIntegral i-convert (SqlWord32 i)   IntegerT = IntegerE $ fromIntegral i-convert (SqlWord64 i)   IntegerT = IntegerE $ fromIntegral i-convert (SqlDouble d)  DoubleT  = DoubleE d-convert (SqlRational d) DoubleT = DoubleE $ fromRational d-convert (SqlInteger d)  DoubleT = DoubleE $ fromIntegral d-convert (SqlInt32 d)    DoubleT = DoubleE $ fromIntegral d-convert (SqlInt64 d)    DoubleT = DoubleE $ fromIntegral d-convert (SqlWord32 d)   DoubleT = DoubleE $ fromIntegral d-convert (SqlWord64 d)   DoubleT = DoubleE $ fromIntegral d-convert (SqlBool b) BoolT       = BoolE b-convert (SqlInteger i) BoolT    = BoolE (i /= 0)-convert (SqlInt32 i)   BoolT    = BoolE (i /= 0)-convert (SqlInt64 i)   BoolT    = BoolE (i /= 0)-convert (SqlWord32 i)  BoolT    = BoolE (i /= 0)-convert (SqlWord64 i)  BoolT    = BoolE (i /= 0) -convert (SqlChar c) CharT       = CharE c-convert (SqlString (c:_)) CharT = CharE c-convert (SqlByteString c) CharT = CharE (head $ T.unpack $ T.decodeUtf8 c)-convert (SqlString t) TextT     = TextE (T.pack t) -convert (SqlByteString s) TextT = TextE (T.decodeUtf8 s)-convert sql                 _   = error $ "Unsupported SqlValue: "  ++ show sql--sizeOfType :: Type a -> Int-sizeOfType UnitT = 1-sizeOfType IntegerT = 1-sizeOfType DoubleT = 1-sizeOfType BoolT = 1-sizeOfType CharT = 1-sizeOfType TextT = 1-sizeOfType (PairT t1 t2) = sizeOfType t1 + sizeOfType t2-sizeOfType _ = error "sizeOfType: Not a record type"---- | Check if a 'SqlValue' matches a 'Type'-typeMatch :: Type a -> SqlValue -> Bool-typeMatch t s =-    case (t,s) of-         (UnitT         , SqlNull)          -> True-         (IntegerT      , SqlInteger _)     -> True-         (DoubleT       , SqlDouble _)      -> True-         (BoolT         , SqlBool _)        -> True-         (CharT         , SqlChar _)        -> True-         (TextT         , SqlString _)      -> True-         (TextT         , SqlByteString _)  -> True-         _                                  -> False
+ src/Database/DSH/NKL/Kure.hs view
@@ -0,0 +1,291 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE InstanceSigs          #-}++-- | Infrastructure for KURE-based rewrites on NKL expressions+module Database.DSH.NKL.Kure+    ( -- * Re-export relevant KURE modules+      module Language.KURE+    , module Language.KURE.Lens++      -- * The KURE monad+    , RewriteM, RewriteStateM, TransformN, RewriteN, LensN, freshNameT+    +      -- * Setters and getters for the translation state+    , get, put, modify+    +      -- * Changing between stateful and non-stateful transforms+    , statefulT, liftstateT++      -- * The KURE context+    , NestedCtx(..), CrumbN(..), PathN, initialCtx, freeIn, boundIn+    , inScopeNames, bindVar++      -- * Congruence combinators+    , tableT, appe1T, appe2T, binopT, ifT, constExprT, varT, iteratorT, letT+    , tableR, appe1R, appe2R, binopR, ifR, litR, varR, iteratorR, letR+    +    ) where+    +       +import           Control.Monad+import           Data.Monoid++import           Language.KURE+import           Language.KURE.Lens+       +import           Database.DSH.Common.RewriteM+import           Database.DSH.Common.Lang+import           Database.DSH.Common.Type+import           Database.DSH.NKL.Lang+                 +--------------------------------------------------------------------------------+-- Convenience type aliases++type TransformN a b = Transform NestedCtx (RewriteM Int) a b+type RewriteN a     = TransformN a a+type LensN a b      = Lens NestedCtx (RewriteM Int) a b++--------------------------------------------------------------------------------++data CrumbN = IteratorHead+            | IteratorSource+            | AppE1Arg+            | AppE2Arg1+            | AppE2Arg2+            | BinOpArg1+            | BinOpArg2+            | UnOpArg+            | LamBody+            | IfCond+            | IfThen+            | IfElse+            | LetBind+            | LetBody+            | TupleElem Int+            deriving (Eq, Show)++type AbsPathN = AbsolutePath CrumbN++type PathN = Path CrumbN++-- | The context for KURE-based NKL rewrites+data NestedCtx = NestedCtx { nkl_bindings :: [Ident]+                           , nkl_path     :: AbsPathN+                           }+                       +instance ExtendPath NestedCtx CrumbN where+    c@@n = c { nkl_path = nkl_path c @@ n }+    +instance ReadPath NestedCtx CrumbN where+    absPath c = nkl_path c++initialCtx :: [Ident] -> NestedCtx+initialCtx nameCtx = NestedCtx { nkl_bindings = nameCtx, nkl_path = mempty }++-- | Record a variable binding in the context+bindVar :: Ident -> NestedCtx -> NestedCtx+bindVar n ctx = ctx { nkl_bindings = n : nkl_bindings ctx }++inScopeNames :: NestedCtx -> [Ident]+inScopeNames = nkl_bindings++boundIn :: Ident -> NestedCtx -> Bool+boundIn n ctx = n `elem` (nkl_bindings ctx)++freeIn :: Ident -> NestedCtx -> Bool+freeIn n ctx = n `notElem` (nkl_bindings ctx)++-- | Generate a fresh name that is not bound in the current context.+freshNameT :: [Ident] -> TransformN a Ident+freshNameT avoidNames = do+    ctx <- contextT+    constT $ freshName (avoidNames ++ inScopeNames ctx)++--------------------------------------------------------------------------------+-- Support for stateful transforms++-- | Run a stateful transform with an initial state and turn it into a regular+-- (non-stateful) transform+statefulT :: s -> Transform NestedCtx (RewriteStateM s) a b -> TransformN a (s, b)+statefulT s t = resultT (stateful s) t++-- | Turn a regular rewrite into a stateful rewrite+liftstateT :: Transform NestedCtx (RewriteM Int) a b -> Transform NestedCtx (RewriteStateM s) a b+liftstateT t = resultT liftstate t++--------------------------------------------------------------------------------+-- Congruence combinators for CL expressions++tableT :: Monad m => (Type -> String -> [Column] -> TableHints -> b)+                  -> Transform NestedCtx m Expr b+tableT f = contextfreeT $ \expr -> case expr of+                      Table ty n cs ks -> return $ f ty n cs ks+                      _                -> fail "not a table node"+{-# INLINE tableT #-}                      +                      +tableR :: Monad m => Rewrite NestedCtx m Expr+tableR = tableT Table+{-# INLINE tableR #-}++iteratorT :: Monad m => Transform NestedCtx m Expr a1+                     -> Transform NestedCtx m Expr a2+                     -> (Type -> a1 -> Ident -> a2 -> b)+                     -> Transform NestedCtx m Expr b+iteratorT t1 t2 f = transform $ \c expr -> case expr of+                     Iterator ty h x xs -> f ty <$> applyT t1 (c@@IteratorHead) h +                                                <*> return x +                                                <*> applyT t2 (c@@IteratorSource) xs+                     _              -> fail "not an iterator node"+{-# INLINE iteratorT #-}++iteratorR :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+iteratorR t1 t2 = iteratorT t1 t2 Iterator+{-# INLINE iteratorR #-}+                                       +appe1T :: Monad m => Transform NestedCtx m Expr a+                  -> (Type -> Prim1 -> a -> b)+                  -> Transform NestedCtx m Expr b+appe1T t f = transform $ \c expr -> case expr of+                      AppE1 ty p e -> f ty p <$> applyT t (c@@AppE1Arg) e                  +                      _            -> fail "not a unary primitive application"+{-# INLINE appe1T #-}                      +                      +appe1R :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+appe1R t = appe1T t AppE1+{-# INLINE appe1R #-}                      +                      +appe2T :: Monad m => Transform NestedCtx m Expr a1+                  -> Transform NestedCtx m Expr a2+                  -> (Type -> Prim2 -> a1 -> a2 -> b)+                  -> Transform NestedCtx m Expr b+appe2T t1 t2 f = transform $ \c expr -> case expr of+                     AppE2 ty p e1 e2 -> f ty p <$> applyT t1 (c@@AppE2Arg1) e1 +                                                <*> applyT t2 (c@@AppE2Arg2) e2+                     _                -> fail "not a binary primitive application"+{-# INLINE appe2T #-}                      ++appe2R :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+appe2R t1 t2 = appe2T t1 t2 AppE2+{-# INLINE appe2R #-}                      +                     +binopT :: Monad m => Transform NestedCtx m Expr a1+                  -> Transform NestedCtx m Expr a2+                  -> (Type -> ScalarBinOp -> a1 -> a2 -> b)+                  -> Transform NestedCtx m Expr b+binopT t1 t2 f = transform $ \c expr -> case expr of+                     BinOp ty op e1 e2 -> f ty op <$> applyT t1 (c@@BinOpArg1) e1 +                                                  <*> applyT t2 (c@@BinOpArg2) e2+                     _                 -> fail "not a binary operator application"+{-# INLINE binopT #-}                      ++binopR :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+binopR t1 t2 = binopT t1 t2 BinOp+{-# INLINE binopR #-}                      ++unopT :: Monad m => Transform NestedCtx m Expr a+                 -> (Type -> ScalarUnOp -> a -> b)+                 -> Transform NestedCtx m Expr b+unopT t f = transform $ \ctx expr -> case expr of+                     UnOp ty op e -> f ty op <$> applyT t (ctx@@UnOpArg) e+                     _            -> fail "not an unary operator application"+{-# INLINE unopT #-}++unopR :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+unopR t = unopT t UnOp+{-# INLINE unopR #-}+                     +ifT :: Monad m => Transform NestedCtx m Expr a1+               -> Transform NestedCtx m Expr a2+               -> Transform NestedCtx m Expr a3+               -> (Type -> a1 -> a2 -> a3 -> b)+               -> Transform NestedCtx m Expr b+ifT t1 t2 t3 f = transform $ \c expr -> case expr of+                    If ty e1 e2 e3 -> f ty <$> applyT t1 (c@@IfCond) e1               +                                           <*> applyT t2 (c@@IfThen) e2+                                           <*> applyT t3 (c@@IfElse) e3+                    _              -> fail "not an if expression"+{-# INLINE ifT #-}                      +                    +ifR :: Monad m => Rewrite NestedCtx m Expr+               -> Rewrite NestedCtx m Expr+               -> Rewrite NestedCtx m Expr+               -> Rewrite NestedCtx m Expr+ifR t1 t2 t3 = ifT t1 t2 t3 If               +{-# INLINE ifR #-}                      +                    +constExprT :: Monad m => (Type -> Val -> b) -> Transform NestedCtx m Expr b+constExprT f = contextfreeT $ \expr -> case expr of+                    Const ty v -> return $ f ty v+                    _          -> fail "not a constant"+{-# INLINE constExprT #-}                      +                    +litR :: Monad m => Rewrite NestedCtx m Expr+litR = constExprT Const+{-# INLINE litR #-}                      +                    +varT :: Monad m => (Type -> Ident -> b) -> Transform NestedCtx m Expr b+varT f = contextfreeT $ \expr -> case expr of+                    Var ty n -> return $ f ty n+                    _        -> fail "not a variable"+{-# INLINE varT #-}                      +                    +varR :: Monad m => Rewrite NestedCtx m Expr+varR = varT Var+{-# INLINE varR #-}                      ++letT :: Monad m => Transform NestedCtx m Expr a1+                -> Transform NestedCtx m Expr a2+                -> (Type -> Ident -> a1 -> a2 -> b) +                -> Transform NestedCtx m Expr b+letT t1 t2 f = transform $ \c expr -> case expr of+                 Let ty x xs e -> f ty x <$> applyT t1 (c@@LetBind) xs +                                         <*> applyT t2 (bindVar x $ c@@LetBody) e+                 _             -> fail "not a let expression"++letR :: Monad m => Rewrite NestedCtx m Expr +                -> Rewrite NestedCtx m Expr +                -> Rewrite NestedCtx m Expr+letR r1 r2 = letT r1 r2 Let++mkTupleT :: Monad m => Transform NestedCtx m Expr a+                    -> (Type -> [a] -> b)+                    -> Transform NestedCtx m Expr b+mkTupleT t f = transform $ \c expr -> case expr of+                   MkTuple ty es -> f ty <$> zipWithM (\e i -> applyT t (c@@TupleElem i) e) es [1..]+                   _             -> fail "not a tuple constructor"+{-# INLINE mkTupleT #-}++mkTupleR :: Monad m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+mkTupleR r = mkTupleT r MkTuple+++--------------------------------------------------------------------------------+       +instance Walker NestedCtx Expr where+    allR :: forall m. MonadCatch m => Rewrite NestedCtx m Expr -> Rewrite NestedCtx m Expr+    allR r = readerT $ \e -> case e of+            Table{}    -> idR+            AppE1{}    -> appe1R (extractR r)+            AppE2{}    -> appe2R (extractR r) (extractR r)+            BinOp{}    -> binopR (extractR r) (extractR r)+            UnOp{}     -> unopR (extractR r)+            Iterator{} -> iteratorR (extractR r) (extractR r)+            If{}       -> ifR (extractR r) (extractR r) (extractR r)+            Const{}    -> idR+            Var{}      -> idR+            Let{}      -> letR (extractR r) (extractR r)+            MkTuple{}  -> mkTupleR (extractR r)+            +--------------------------------------------------------------------------------+-- I find it annoying that Applicative is not a superclass of Monad.++(<$>) :: Monad m => (a -> b) -> m a -> m b+(<$>) = liftM+{-# INLINE (<$>) #-}++(<*>) :: Monad m => m (a -> b) -> m a -> m b+(<*>) = ap+{-# INLINE (<*>) #-}+
+ src/Database/DSH/NKL/Lang.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE TemplateHaskell       #-}++module Database.DSH.NKL.Lang+  ( Expr(..)+  , Typed(..)+  , Prim1(..)+  , Prim2(..)+  ) where++import           Text.PrettyPrint.ANSI.Leijen+import           Text.Printf++import           Database.DSH.Impossible+import qualified Database.DSH.Common.Lang     as L+import           Database.DSH.Common.Pretty+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Type     (Type, Typed, typeOf)++-- | Nested Kernel Language (NKL) expressions+data Expr  = Table Type String [L.Column] L.TableHints+           | AppE1 Type Prim1 Expr+           | AppE2 Type Prim2 Expr Expr+           | BinOp Type L.ScalarBinOp Expr Expr+           | UnOp Type L.ScalarUnOp Expr+           | If Type Expr Expr Expr+           | Const Type L.Val+           | Var Type L.Ident+           | Iterator Type Expr L.Ident Expr+           | Let Type L.Ident Expr Expr+           | MkTuple Type [Expr]+           deriving (Show)++instance Typed Expr where+    typeOf (Table t _ _ _)    = t+    typeOf (AppE1 t _ _)      = t+    typeOf (AppE2 t _ _ _)    = t+    typeOf (If t _ _ _)       = t+    typeOf (BinOp t _ _ _)    = t+    typeOf (UnOp t _ _)       = t+    typeOf (Const t _)        = t+    typeOf (Var t _)          = t+    typeOf (Iterator t _ _ _) = t+    typeOf (Let t _ _ _)      = t+    typeOf (MkTuple t _)      = t++instance Pretty Expr where+    pretty (MkTuple _ es)      = tupled $ map pretty es+    pretty (AppE1 _ (TupElem n) e1) = +        parenthize e1 <> dot <> int (tupleIndex n)+    pretty (Table _ n _ _)     = text "table" <> parens (text n)+    pretty (AppE1 _ p1 e)      = (text $ show p1) <+> (parenthize e)+    pretty (AppE2 _ p1 e1 e2)  = (text $ show p1) <+> (align $ (parenthize e1) </> (parenthize e2))+    pretty (BinOp _ o e1 e2)   = (parenthize e1) <+> (pretty o) <+> (parenthize e2)+    pretty (UnOp _ o e)        = text (show o) <> parens (pretty e)+    pretty (If _ c t e)        = text "if"+                                 <+> pretty c+                                 <+> text "then"+                                 <+> (parenthize t)+                                 <+> text "else"+                                 <+> (parenthize e)+    pretty (Const _ v)         = pretty v+    pretty (Var _ s)           = text s+    pretty (Iterator _ e x xs) = align +                                $ brackets +                                $ enclose (char ' ') (char ' ') +                                $ pretty e </> char '|' <+> text x <+> text "<-" <+> pretty xs+    pretty (Let _ x e1 e)      = +        align $ text "let" <+> text x <+> char '=' <+> pretty e1+                </>+                text "in" <+> pretty e++parenthize :: Expr -> Doc+parenthize e =+    case e of+        Var _ _               -> pretty e+        Const _ _             -> pretty e+        Table _ _ _ _         -> pretty e+        Iterator _ _ _ _      -> pretty e+        AppE1 _ (TupElem _) _ -> pretty e+        _                     -> parens $ pretty e++data Prim1 = Singleton+           | Length +           | Concat+           | Sum +           | Avg +           | The +           | Head+           | Tail+           | Minimum +           | Maximum+           | Reverse +           | And +           | Or+           | Init +           | Last +           | Nub+           | Number+           | Reshape Integer+           | Transpose+           | TupElem TupleIndex+           deriving (Eq)++instance Show Prim1 where+    show Singleton       = "sng"+    show Length          = "length"+    show Concat          = "concat"+    show Sum             = "sum"+    show Avg             = "avg"+    show The             = "the"+    show Head            = "head"+    show Minimum         = "minimum"+    show Maximum         = "maximum"+    show Tail            = "tail"+    show Reverse         = "reverse"+    show And             = "and"+    show Or              = "or"+    show Init            = "init"+    show Last            = "last"+    show Nub             = "nub"+    show Number          = "number"+    show Transpose       = "transpose"+    show (Reshape n)     = printf "reshape(%d)" n+    -- tuple access is pretty-printed in a special way+    show TupElem{}       = $impossible+  +data Prim2 = Group+           | Sort+           | Restrict+           | Append+           | Index+           | Zip+           | CartProduct+           | NestProduct+           | ThetaJoin (L.JoinPredicate L.JoinExpr)+           | NestJoin (L.JoinPredicate L.JoinExpr)+           | SemiJoin (L.JoinPredicate L.JoinExpr)+           | AntiJoin (L.JoinPredicate L.JoinExpr)+           deriving (Eq)++instance Show Prim2 where+    show Group        = "group"+    show Sort         = "sort"+    show Restrict     = "restrict"+    show Append       = "append"+    show Index        = "index"+    show Zip          = "zip"+    show CartProduct  = "⨯"+    show NestProduct  = "▽"+    show (ThetaJoin p) = printf "⨝_%s" (pp p)+    show (NestJoin p)  = printf "△_%s" (pp p)+    show (SemiJoin p)  = printf "⋉_%s" (pp p)+    show (AntiJoin p)  = printf "▷_%s" (pp p)
+ src/Database/DSH/NKL/Primitives.hs view
@@ -0,0 +1,74 @@+-- | Smart constructors for NKL combinators+module Database.DSH.NKL.Primitives where++import           Prelude hiding (filter, map, concat, concatMap, fst, snd)+import qualified Prelude as P+import           Text.Printf++import           Database.DSH.Common.Type+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Pretty+import           Database.DSH.Common.Lang+import           Database.DSH.NKL.Lang++--------------------------------------------------------------------------------+-- Error reporting++tyErr :: P.String -> a+tyErr comb = P.error $ printf "NKL.Primitives type error in %s" comb++tyErrShow :: P.String -> [Type] -> a+tyErrShow comb ts = P.error (printf "NKL.Primitives type error in %s: %s" comb (P.show P.$ P.map pp ts))++--------------------------------------------------------------------------------+-- Smart constructors++tupElem :: TupleIndex -> Expr -> Expr+tupElem f e = +    let t = tupleElemT (typeOf e) f+    in AppE1 t (TupElem f) e++fst :: Expr -> Expr+fst e = tupElem First e++snd :: Expr -> Expr+snd e = tupElem (Next First) e++pair :: Expr -> Expr -> Expr+pair a b = tuple [a, b]++tuple :: [Expr] -> Expr+tuple es =+    let ts = P.map typeOf es+        rt = TupleT ts+    in MkTuple rt es++sng :: Expr -> Expr+sng x = AppE1 (listT $ typeOf x) Singleton x++concat :: Expr -> Expr+concat e = let t = typeOf e+            in if listDepth t P.> 1+               then AppE1 (unliftType t) Concat e+               else tyErrShow "concat" [t]++restrict :: Expr -> Expr -> Expr+restrict vs bs = let vst@(ListT _)     = typeOf vs+                 in AppE2 vst Restrict vs bs++sort :: Expr -> Expr -> Expr+sort vs ss = let vst@(ListT _) = typeOf vs+             in AppE2 vst Sort vs ss++-- FIXME type is not correct+group :: Expr -> Expr -> Expr+group vs gs = let vst@(ListT _) = typeOf vs+              in AppE2 vst Group vs gs++let_ :: Ident -> Expr -> Expr -> Expr+let_ x e1 e2 = let t = typeOf e1 in Let t x e1 e2++if_ :: Expr -> Expr -> Expr -> Expr+if_ c t e = if BoolT == typeOf c+            then If (typeOf t) c t e+            else tyErr "if_"
+ src/Database/DSH/NKL/Rewrite.hs view
@@ -0,0 +1,211 @@+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.NKL.Rewrite+    ( substR+    , subst+    , freeVars+    , boundVars+    , optimizeNKL+    ) where++import Control.Arrow+import Data.List+import Data.Monoid++import Database.DSH.Impossible+import Database.DSH.Common.Type+import Database.DSH.Common.Lang+import Database.DSH.Common.Kure+import Database.DSH.Common.RewriteM+import Database.DSH.NKL.Kure+import Database.DSH.NKL.Lang++-- | Run a translate on an expression without context+applyExpr :: [Ident] -> TransformN Expr b -> Expr -> Either String b+applyExpr nameCtx f e = runRewriteM $ applyT f (initialCtx nameCtx) (inject e)++--------------------------------------------------------------------------------+-- Computation of free and bound variables++freeVarsT :: TransformN Expr [Ident]+freeVarsT = fmap nub $ crushbuT $ do (ctx, Var _ v) <- exposeT+                                     guardM (v `freeIn` ctx)+                                     return [v]++-- | Compute free variables of the given expression+freeVars :: Expr -> [Ident]+freeVars = either error id . applyExpr [] freeVarsT++boundVarsT :: TransformN Expr [Ident]+boundVarsT = fmap nub $ crushbuT $ readerT $ \expr -> case expr of+     Iterator _ _ v _ -> return [v]+     Let _ v _ _      -> return [v]+     _                -> return []++-- | Compute all names that are bound in the given expression. Note+-- that the only binding forms in NKL are comprehensions or 'let'+-- bindings.+boundVars :: Expr -> [Ident]+boundVars = either error id . applyExpr [] boundVarsT++--------------------------------------------------------------------------------+-- Substitution++subst :: [Ident] -> Ident -> Expr -> Expr -> Expr+subst nameCtx x s e = either (const e) id $ applyExpr nameCtx (substR x s) e++alphaCompR :: [Ident] -> RewriteN Expr+alphaCompR avoidNames = do +    Iterator compTy h x _  <- idR+    x'                     <- freshNameT (x : freeVars h ++ avoidNames)+    let varTy = elemT compTy+    iteratorT (tryR $ substR x (Var varTy x')) +              idR +              (\_ h' _ xs' -> Iterator compTy h' x' xs')++alphaLetR :: [Ident] -> RewriteN Expr+alphaLetR avoidNames = do+    Let letTy x e1 e2 <- idR+    x'                <- freshNameT (x : freeVars e2 ++ avoidNames)+    let varTy = typeOf e1+    letT idR (tryR $ substR x (Var varTy x')) (\_ _ e1' e2' -> Let letTy x' e1' e2')++-- | Replace /all/ references to variable 'v' by expression 's'.+substR :: Ident -> Expr -> RewriteN Expr+substR v s = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    Var _ n | n == v                          -> return s++    -- Some other variable+    Var _ _                                   -> idR++    -- A comprehension which does not shadow v and in which v occurs+    -- free in the head. If the comprehension variable occurs free in+    -- the substitute, we rename the comprehension to avoid name+    -- capturing.+    Iterator _ h x _ | x /= v && v `elem` freeVars h ->+        if x `elem` freeVars s+        then alphaCompR (freeVars s) >>> substR v s+        else anyR $ substR v s++    -- A comprehension whose generator shadows v -> don't descend into the head+    Iterator _ _ x _ | v == x                     -> iteratorR idR (substR v s)++    Let _ x _ e2 | x /= v && v `elem` freeVars e2 ->+        if x `elem` freeVars s+        then alphaLetR (freeVars s) >>> substR v s+        else anyR $ substR v s++    -- A let binding which shadows v -> don't descend into the body+    Let _ x _ _ | v == x                      -> letR (substR v s) idR+    _                                         -> anyR $ substR v s++--------------------------------------------------------------------------------+-- Simple optimizations++-- | This function inlines let-bound expressions. In contrast to+-- general substitution, we do not inline into comprehensions, even if+-- we could. The reason is that expressions should not be evaluated+-- iteratively if they are loop-invariant.+inlineBindingR :: Ident -> Expr -> RewriteN Expr+inlineBindingR v s = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    Var _ n | n == v          -> return $ inject s++    -- If a let-binding shadows the name we substitute, only descend+    -- into the bound expression.+    Let _ n _ _ | n == v      -> promoteR $ letR idR (extractR $ inlineBindingR v s)+    Let _ n _ _ | otherwise   ->+        if n `elem` freeVars s+        -- If the let-bound name occurs free in the substitute,+        -- alpha-convert the binding to avoid capturing the name.+        then $unimplemented >>> anyR (inlineBindingR v s)+        else anyR $ inlineBindingR v s++    -- We don't inline into comprehensions to avoid conflicts with+    -- loop-invariant extraction.+    Iterator _ _ _ _          -> idR+    _                         -> anyR $ inlineBindingR v s++pattern ConcatP t xs <- AppE1 t Concat xs+pattern SingletonP e <- AppE1 _ Singleton e +       +-- concatMap (\x -> [e x]) xs+-- concat [ [ e x ] | x <- xs ]+-- =>+-- [ e x | x <- xs ]+singletonHeadR :: RewriteN Expr+singletonHeadR = do+    ConcatP t (Iterator _ (SingletonP e) x xs) <- idR+    return $ Iterator t e x xs++-- | Count all occurences of an identifier for let-inlining.+countVarRefT :: Ident -> TransformN Expr (Sum Int)+countVarRefT v = readerT $ \expr -> case expr of+    -- Occurence of the variable to be replaced+    Var _ n | n == v         -> return 1+    Var _ _ | otherwise      -> return 0++    Let _ n _ _ | n == v     -> letT (constT $ return 0) +                                     (countVarRefT v)+                                     (\_ _ c1 c2 -> c1 + c2)+    Let _ _ _ _ | otherwise  -> letT (countVarRefT v)+                                     (countVarRefT v)+                                     (\_ _ c1 c2 -> c1 + c2)++    Iterator _ _ x _ | v == x -> iteratorT (constT $ return 0)+                                           (countVarRefT v)+                                           (\_ c1 _ c2 -> c1 + c2)+    Iterator _ _ _ _ | otherwise -> iteratorT (countVarRefT v)+                                              (countVarRefT v)+                                              (\_ c1 _ c2 -> c1 + c2)++    Table{}                  -> return 0+    Const{}                  -> return 0+    _                        -> allT (countVarRefT v)++-- | Remove a let-binding that is not referenced.+unusedBindingR :: RewriteN Expr+unusedBindingR = do+    Let _ x _ e2 <- idR+    0            <- childT LetBody $ countVarRefT x+    return $ e2++-- | Inline a let-binding that is only referenced once.+referencedOnceR :: RewriteN Expr+referencedOnceR = do+    Let _ x e1 _ <- idR+    1            <- childT LetBody $ countVarRefT x++    -- We do not inline into comprehensions, but 'countVarRef' counts+    -- all occurences including those in comprehensions. For this+    -- reason, we check if the occurence was actually eliminated by+    -- inlining and fail otherwise.+    body' <- childT LetBody (inlineBindingR x e1)+    0     <- (constT $ return body') >>> countVarRefT x+    return body'++simpleExpr :: Expr -> Bool+simpleExpr Table{} = True+simpleExpr Var{}   = True+simpleExpr _       = False++-- | Inline a let-binding that binds a simple expression.+simpleBindingR :: RewriteN Expr+simpleBindingR = do+    Let _ x e1 _ <- idR+    guardM $ simpleExpr e1+    childT LetBody $ substR x e1+    +nklOptimizations :: RewriteN Expr+nklOptimizations = anybuR $ singletonHeadR +                            <+ unusedBindingR +                            <+ referencedOnceR+                            <+ simpleBindingR++optimizeNKL :: Expr -> Expr+optimizeNKL expr = debugOpt "NKL" expr optimizedExpr+  where+    optimizedExpr = applyExpr [] nklOptimizations expr+        
+ src/Database/DSH/Optimizer/Common/Auxiliary.hs view
@@ -0,0 +1,11 @@+module Database.DSH.Optimizer.Common.Auxiliary where++import qualified Data.IntMap as M++-- | Perform a map lookup and fail with the given error string if the key+-- is not present+lookupUnsafe :: Show a => M.IntMap a -> String -> Int -> a+lookupUnsafe m s u =+    case M.lookup u m of+        Just p -> p+        Nothing -> error $ s ++ " " ++ (show u) ++ " in " ++ (show m)
+ src/Database/DSH/Optimizer/Common/Rewrite.hs view
@@ -0,0 +1,74 @@+module Database.DSH.Optimizer.Common.Rewrite+  ( module Database.Algebra.Rewrite.Match+  , module Database.Algebra.Rewrite.PatternConstruction+  , module Database.Algebra.Rewrite.Properties+  , module Database.Algebra.Rewrite.Rule+  , module Database.Algebra.Rewrite.Traversal+  , replaceRoot+  , replaceWithNew+  , replace+  , R.Rewrite+  , R.runRewrite+  , R.initRewriteState+  , R.Log+  , R.logGeneral+  , R.logRewrite+  , R.parents+  , R.topsort+  , R.operator+  , R.rootNodes+  , R.exposeDag+  , R.getExtras+  , R.condRewrite+  , R.updateExtras+  , R.insert+  , R.insertNoShare+  , R.replaceChild+  , R.infer+  , R.collect+  )++where++import qualified Database.Algebra.Dag                          as D+import           Database.Algebra.Dag.Common+import qualified Database.Algebra.Rewrite.DagRewrite           as R+import           Database.Algebra.Rewrite.Match+import           Database.Algebra.Rewrite.PatternConstruction  (dagPatMatch, v)+import           Database.Algebra.Rewrite.Properties+import           Database.Algebra.Rewrite.Rule+import           Database.Algebra.Rewrite.Traversal++import           Database.DSH.Common.QueryPlan+import           Database.DSH.VL.Vector++--------------------------------------------------------------+-- Versions of rewrite combinators that maintain the Shape+-- description of the query structure.++-- | Replace a root node while maintaining the query structure+-- information.+replaceRoot :: (DagVector v, D.Operator o) => AlgNode -> AlgNode -> R.Rewrite o (Shape v) ()+replaceRoot oldRoot newRoot = do+  sh <- R.getExtras+  R.updateExtras $ updateShape oldRoot newRoot sh+  R.replaceRoot oldRoot newRoot++-- | Replace a node with a new operator while mainting the query+-- structure information.+replaceWithNew :: (D.Operator o, Show o, DagVector v) +               => AlgNode -> o -> R.Rewrite o (Shape v) AlgNode+replaceWithNew oldNode newOp = do+  sh <- R.getExtras+  newNode <- R.replaceWithNew oldNode newOp+  R.updateExtras $ updateShape oldNode newNode sh+  return newNode++-- | Replace a node with another node while maintaining the query+-- structure information.+replace :: (DagVector v, D.Operator o) +        => AlgNode -> AlgNode -> R.Rewrite o (Shape v) ()+replace oldNode newNode = do+  sh <- R.getExtras+  R.replace oldNode newNode+  R.updateExtras $ updateShape oldNode newNode sh
+ src/Database/DSH/Optimizer/TA/OptimizeTA.hs view
@@ -0,0 +1,52 @@+module Database.DSH.Optimizer.TA.OptimizeTA where++import qualified Data.IntMap as M++import qualified Database.Algebra.Dag                                             as Dag++import           Database.Algebra.Table.Lang++import           Database.DSH.Common.QueryPlan+import           Database.DSH.VL.Vector++import           Database.DSH.Optimizer.Common.Rewrite++import           Database.DSH.Optimizer.TA.Rewrite.Basic++{-++rough plan/first goals:++merge projections: no properties, leads to basic infrastructure++prune unreferenced rownums: icols prop++simplify rownums, e.g. key-based: key prop, maybe fd (not sure if necessary)++merge sorting criteria into rownums:  track sorting criteria++remove rownums if concrete values not required: use prop, key prop, ?++-}++type RewriteClass = Rewrite TableAlgebra (Shape NDVec) Bool++defaultPipeline :: [RewriteClass]+defaultPipeline = [cleanup]++runPipeline :: Dag.AlgebraDag TableAlgebra +            -> (Shape NDVec)+            -> [RewriteClass] +            -> Bool +            -> (Dag.AlgebraDag TableAlgebra, Log, Shape NDVec)+runPipeline d sh pipeline debug = (d', rewriteLog, sh')+  where (d', sh', _, rewriteLog) = runRewrite (sequence_ pipeline) d sh debug++optimizeTA :: QueryPlan TableAlgebra NDVec -> QueryPlan TableAlgebra NDVec+optimizeTA plan =+#ifdef DEBUGGRAPH+  let (d, _rewriteLog, shape) = runPipeline (queryDag plan) (queryShape plan) defaultPipeline True+#else+  let (d, _rewriteLog, shape) = runPipeline (queryDag plan) (queryShape plan) defaultPipeline False+#endif+  in QueryPlan { queryDag = d, queryShape = shape, queryTags = M.empty }
+ src/Database/DSH/Optimizer/TA/Properties/Auxiliary.hs view
@@ -0,0 +1,73 @@+-- | Some auxiliary functions for property inference.+module Database.DSH.Optimizer.TA.Properties.Auxiliary where++import qualified Data.Set.Monad              as S++import           Database.Algebra.Table.Lang++(∪) :: Ord a => S.Set a -> S.Set a -> S.Set a+(∪) = S.union++(∩) :: Ord a => S.Set a -> S.Set a -> S.Set a+(∩) = S.intersection++(∖) :: Ord a => S.Set a -> S.Set a -> S.Set a+(∖) = S.difference++(∈) :: Ord a => a -> S.Set a -> Bool+(∈) = S.member++(⊆) :: Ord a => S.Set a -> S.Set a -> Bool+(⊆) = S.isSubsetOf++-- | Singleton set abbreviation+ss :: Ord a => a -> S.Set a+ss = S.singleton++-- | List set abbreviation+ls :: Ord a => [a] -> S.Set a+ls = S.fromList++unionss :: Ord a => S.Set (S.Set a) -> S.Set a+unionss = S.foldr (∪) S.empty++exprCols :: Expr -> S.Set Attr+exprCols (BinAppE _ e1 e2) = exprCols e1 ∪ exprCols e2+exprCols (IfE c t e)       = exprCols c ∪ exprCols t ∪ exprCols e+exprCols (UnAppE _ e)      = exprCols e+exprCols (ColE c)          = S.singleton c+exprCols (ConstE _)        = S.empty++aggrInput :: AggrType -> S.Set Attr+aggrInput (Avg e)  = exprCols e+aggrInput (Max e)  = exprCols e+aggrInput (Min e)  = exprCols e+aggrInput (Sum e)  = exprCols e+aggrInput (All e)  = exprCols e+aggrInput (Any e)  = exprCols e+aggrInput Count    = S.empty++winFunInput :: WinFun -> S.Set Attr+winFunInput (WinAvg e)        = exprCols e+winFunInput (WinMax e)        = exprCols e+winFunInput (WinMin e)        = exprCols e+winFunInput (WinSum e)        = exprCols e+winFunInput (WinAll e)        = exprCols e+winFunInput (WinAny e)        = exprCols e+winFunInput (WinFirstValue e) = exprCols e+winFunInput (WinLastValue e)  = exprCols e+winFunInput WinCount          = S.empty++mapCol :: Proj -> Maybe (Attr, Attr)+mapCol (a, ColE b)                   = Just (a, b)+mapCol (a, UnAppE (Cast _) (ColE b)) = Just (a, b)+mapCol _                             = Nothing++mColE :: Expr -> Maybe Attr+mColE (ColE c) = Just c+mColE _        = Nothing++posCol :: SerializeOrder -> S.Set Attr+posCol (AbsPos c)  = S.singleton c+posCol (RelPos cs) = S.fromList cs+posCol NoPos       = S.empty
+ src/Database/DSH/Optimizer/TA/Properties/BottomUp.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.TA.Properties.BottomUp where++import qualified Data.Set.Monad                             as S++import           Database.Algebra.Dag+import           Database.Algebra.Dag.Common+import           Database.Algebra.Table.Lang++import           Database.DSH.Impossible++import           Database.DSH.Optimizer.Common.Auxiliary+import           Database.DSH.Optimizer.Common.Rewrite++import           Database.DSH.Optimizer.TA.Properties.Card1+import           Database.DSH.Optimizer.TA.Properties.Cols+import           Database.DSH.Optimizer.TA.Properties.Empty+import           Database.DSH.Optimizer.TA.Properties.Keys+import           Database.DSH.Optimizer.TA.Properties.Order+import           Database.DSH.Optimizer.TA.Properties.Const+import           Database.DSH.Optimizer.TA.Properties.Types++-- FIXME this is (almost) identical to its X100 counterpart -> merge+inferWorker :: NodeMap TableAlgebra -> TableAlgebra -> AlgNode -> NodeMap BottomUpProps -> BottomUpProps+inferWorker _ op n pm =+    let res =+           case op of+                TerOp _ _ _ _ -> $impossible+                BinOp vl c1 c2 ->+                  let c1Props = lookupUnsafe pm "no children properties" c1+                      c2Props = lookupUnsafe pm "no children properties" c2+                  in inferBinOp vl c1Props c2Props+                UnOp vl c ->+                  let cProps = lookupUnsafe pm "no children properties" c+                  in inferUnOp vl cProps+                NullaryOp vl -> inferNullOp vl+    in case res of+            Left msg -> error $ "Inference failed at node " ++ (show n) ++ ": " ++ msg+            Right props -> props++inferNullOp :: NullOp -> Either String BottomUpProps+inferNullOp op = do+  let opCols  = inferColsNullOp op+      opKeys  = inferKeysNullOp op+      opEmpty = inferEmptyNullOp op+      opCard1 = inferCard1NullOp op+      -- We only care for rownum-generated columns. Therefore, For+      -- nullary operators order is empty.+      opOrder = []+      opConst = inferConstNullOp op+  return $ BUProps { pCols = opCols+                   , pKeys = opKeys+                   , pEmpty = opEmpty+                   , pCard1 = opCard1+                   , pOrder = opOrder+                   , pConst = opConst+                   }++inferUnOp :: UnOp -> BottomUpProps -> Either String BottomUpProps+inferUnOp op cProps = do+  let opCols  = inferColsUnOp (pCols cProps) op+      opKeys  = inferKeysUnOp (pKeys cProps) (pCard1 cProps) (S.map fst $ pCols cProps) op+      opEmpty = inferEmptyUnOp (pEmpty cProps) op+      opCard1 = inferCard1UnOp (pCard1 cProps) (pEmpty cProps) op+      opOrder = inferOrderUnOp (pOrder cProps) op+      opConst = inferConstUnOp (pConst cProps) op+  return $ BUProps { pCols = opCols+                   , pKeys = opKeys+                   , pEmpty = opEmpty+                   , pCard1 = opCard1+                   , pOrder = opOrder+                   , pConst = opConst+                   }++inferBinOp :: BinOp -> BottomUpProps -> BottomUpProps -> Either String BottomUpProps+inferBinOp op c1Props c2Props = do+  let opCols  = inferColsBinOp (pCols c1Props) (pCols c2Props) op+      opKeys  = inferKeysBinOp (pKeys c1Props) (pKeys c2Props) (pCard1 c1Props) (pCard1 c2Props) op+      opEmpty = inferEmptyBinOp (pEmpty c1Props) (pEmpty c2Props) op+      opCard1 = inferCard1BinOp (pCard1 c1Props) (pCard1 c2Props) op+      opOrder = inferOrderBinOp (pOrder c1Props) (pOrder c2Props) op+      opConst = inferConstBinOp (pConst c1Props) (pConst c2Props) op+  return $ BUProps { pCols = opCols+                   , pKeys = opKeys+                   , pEmpty = opEmpty+                   , pCard1 = opCard1+                   , pOrder = opOrder+                   , pConst = opConst+                   }++inferBottomUpProperties :: AlgebraDag TableAlgebra -> NodeMap BottomUpProps+inferBottomUpProperties dag = inferBottomUpGeneral inferWorker dag
+ src/Database/DSH/Optimizer/TA/Properties/Card1.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.TA.Properties.Card1 where++import           Database.Algebra.Table.Lang++import           Database.DSH.Optimizer.TA.Properties.Types++inferCard1NullOp :: NullOp -> Card1+inferCard1NullOp op =+    case op of+        LitTable (vals, _) -> length vals == 1+        TableRef (_, _, _) -> False++inferCard1UnOp :: Card1 -> Empty -> UnOp -> Card1+inferCard1UnOp childCard1 childEmpty op =+    case op of+        WinFun _          -> childCard1+        RowNum (_, _, _)  -> childCard1+        RowRank (_, _)    -> childCard1+        Rank (_, _)       -> childCard1+        Project _         -> childCard1+        Select _          -> False+        Distinct _        -> childCard1+        Aggr (_, _ : _)   -> childCard1+        Aggr (_, [])      -> not childEmpty+        Serialize    _    -> childCard1++inferCard1BinOp :: Card1 -> Card1 -> BinOp -> Card1+inferCard1BinOp leftCard1 rightCard1 op =+    case op of+        Cross _      -> leftCard1 && rightCard1+        EqJoin _     -> False+        ThetaJoin _  -> False+        SemiJoin _   -> False+        AntiJoin _   -> False+        DisjUnion _  -> False+        Difference _ -> False+
+ src/Database/DSH/Optimizer/TA/Properties/Cols.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Infer the output schema of TableAlgebra operators.+module Database.DSH.Optimizer.TA.Properties.Cols where++import qualified Data.Set.Monad                             as S+++import           Database.Algebra.Table.Lang++import           Database.DSH.Impossible+import           Database.DSH.Optimizer.TA.Properties.Auxiliary+import           Database.DSH.Optimizer.TA.Properties.Types++----------------------------------------------------------------------------+-- Type inference for tablealgebra expressions++isNumeric :: BinFun -> Bool+isNumeric f = f `elem` [Plus, Minus, Times, Div]++isComp :: BinFun -> Bool+isComp f = f `elem` [Gt, Lt, LtE, GtE, Eq, Contains, SimilarTo, Like]++isBool :: BinFun -> Bool+isBool f = f `elem` [And, Or]++binAppTy :: BinFun -> ATy -> ATy -> ATy+binAppTy f t1 _t2 =+    case f of+        Gt        -> ABool+        Lt        -> ABool+        LtE       -> ABool+        GtE       -> ABool+        Eq        -> ABool+        NEq       -> ABool+        Contains  -> ABool+        SimilarTo -> ABool+        Like      -> ABool+        And       -> ABool+        Or        -> ABool+        Plus      -> t1+        Minus     -> t1+        Times     -> t1+        Div       -> t1+        Modulo    -> AInt+        Concat    -> AStr++unAppTy :: UnFun -> ATy+unAppTy Not         = ABool+unAppTy (Cast t)    = t+unAppTy Sin         = ADouble+unAppTy Cos         = ADouble+unAppTy Tan         = ADouble+unAppTy ASin        = ADouble+unAppTy ACos        = ADouble+unAppTy ATan        = ADouble+unAppTy Log         = ADouble+unAppTy Sqrt        = ADouble+unAppTy Exp         = ADouble+unAppTy SubString{} = AStr++valType :: AVal -> ATy+valType (VInt _)    = AInt+valType (VStr _)    = AStr+valType (VBool _)   = ABool+valType (VDouble _) = ADouble+valType (VDec _)    = ADec+valType (VNat _)    = ANat++exprTy :: S.Set TypedAttr -> Expr -> ATy+exprTy childCols expr =+    case expr of+        ColE c          -> typeOf c childCols+        ConstE v        -> valType v+        BinAppE f e1 e2 -> binAppTy f (exprTy childCols e1) (exprTy childCols e2)+        UnAppE f _      -> unAppTy f+        IfE _ t _       -> exprTy childCols t++----------------------------------------------------------------------------+-- Type inference for aggregate functions++numAggr :: ATy -> ATy+numAggr AInt    = AInt+numAggr ADec    = ADec+numAggr ANat    = ANat+numAggr ADouble = ADouble+numAggr _       = $impossible+++aggrTy :: S.Set TypedAttr -> (AggrType, Attr) -> TypedAttr+aggrTy childCols (aggr, resCol) = (resCol, resType)+  where+    resType = case aggr of+        All _  -> ABool+        Any _  -> ABool+        Count  -> AInt+        Avg e  -> numAggr $ exprTy childCols e+        Max e  -> numAggr $ exprTy childCols e+        Min e  -> numAggr $ exprTy childCols e+        Sum e  -> numAggr $ exprTy childCols e++winFunTy :: S.Set TypedAttr -> (WinFun, Attr) -> TypedAttr+winFunTy childCols (aggr, resCol) = (resCol, resType)+  where+    resType = case aggr of+        WinAll _        -> ABool+        WinAny _        -> ABool+        WinCount        -> AInt+        WinAvg e        -> numAggr $ exprTy childCols e+        WinMax e        -> numAggr $ exprTy childCols e+        WinMin e        -> numAggr $ exprTy childCols e+        WinSum e        -> numAggr $ exprTy childCols e+        WinFirstValue e -> exprTy childCols e+        WinLastValue e  -> exprTy childCols e++----------------------------------------------------------------------------+-- Schema inference for tablealgebra operators++inferColsNullOp :: NullOp -> S.Set TypedAttr+inferColsNullOp op =+    case op of+        LitTable (_, schema)   -> S.fromList schema+        TableRef (_, attrs, _) -> S.fromList attrs++inferColsUnOp :: S.Set TypedAttr -> UnOp -> S.Set TypedAttr+inferColsUnOp childCols op =+    case op of+        WinFun ((resCol, fun), _, _, _) -> S.insert (winFunTy childCols (fun, resCol)) childCols+        RowNum (resCol, _, _) -> S.insert (resCol, AInt) childCols+        RowRank (resCol, _)   -> S.insert (resCol, AInt) childCols+        Rank (resCol, _)      -> S.insert (resCol, AInt) childCols+        Project projs         -> S.fromList $ map (\(c, e) -> (c, exprTy childCols e)) projs+        Select _              -> childCols+        Distinct _            -> childCols+        Aggr (afuns, pexprs)  -> (S.fromList $ map (aggrTy childCols) afuns)+                                 ∪+                                 [ (c, exprTy childCols e) | (c, e) <- S.fromList pexprs ]+        Serialize (md, mp, cs) ->+            let cols = (S.fromList $ map (\(PayloadCol c) -> c) cs)+                       ∪ (maybe S.empty (\(DescrCol c) -> S.singleton c) md)+                       ∪ posCol mp+            in S.map (\c -> (c, typeOf c childCols)) cols++inferColsBinOp :: S.Set TypedAttr -> S.Set TypedAttr -> BinOp -> S.Set TypedAttr+inferColsBinOp leftCols rightCols op =+    case op of+        Cross _      -> S.union leftCols rightCols+        EqJoin _     -> S.union leftCols rightCols+        ThetaJoin _  -> S.union leftCols rightCols+        SemiJoin _   -> S.union leftCols rightCols+        AntiJoin _   -> S.union leftCols rightCols+        DisjUnion _  -> leftCols+        Difference _ -> leftCols+++
+ src/Database/DSH/Optimizer/TA/Properties/Const.hs view
@@ -0,0 +1,71 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++module Database.DSH.Optimizer.TA.Properties.Const+    ( inferConstNullOp+    , inferConstUnOp+    , inferConstBinOp+    , constExpr+    ) where++import           Data.Maybe+import           Data.List++import           Database.Algebra.Table.Lang++import           Database.DSH.Optimizer.TA.Properties.Types++constExpr :: [ConstCol] -> Expr -> Maybe AVal+constExpr _         (BinAppE _ _ _) = Nothing+constExpr _         (UnAppE _ _)    = Nothing+constExpr constCols (ColE c)        = lookup c constCols+constExpr _         (ConstE v)      = Just v+constExpr _         (IfE _ _ _)     = Nothing++constProj :: [ConstCol] -> (Attr, Expr) -> Maybe ConstCol+constProj constCols (c, e) = constExpr constCols e >>= \v -> return (c, v)++inferConstNullOp :: NullOp -> [ConstCol]+inferConstNullOp op =+    case op of+        LitTable (tuples, schema) -> concat $ zipWith constCol (transpose tuples) (map fst schema)+          where+            constCol (v:vs) c | all (== v) vs = [(c, v)]+            constCol _      _                 = []+        TableRef _             -> []++inferConstSelect :: Expr -> [ConstCol]+inferConstSelect (BinAppE Eq (ColE c) (ConstE v)) = [(c, v)]+inferConstSelect (BinAppE Eq (ConstE v) (ColE c)) = [(c, v)]+inferConstSelect (BinAppE And e1 e2)              = inferConstSelect e1 ++ inferConstSelect e2+inferConstSelect _                                = []++inferConstUnOp :: [ConstCol] -> UnOp -> [ConstCol]+inferConstUnOp childConst op = +    case op of+        WinFun _          -> childConst+        RowNum (_, _, _)  -> childConst+        RowRank (_, _)    -> childConst+        Rank (_, _)       -> childConst+        Select p          -> inferConstSelect p ++ childConst+        Distinct _        -> childConst+        Aggr _            -> []+        Project projs     -> mapMaybe (constProj childConst) projs+        Serialize _       -> childConst++inferConstBinOp :: [ConstCol] -> [ConstCol] -> BinOp -> [ConstCol]+inferConstBinOp leftChildConst rightChildConst op =+    case op of+        Cross _      -> leftChildConst ++ rightChildConst+        EqJoin _     -> leftChildConst ++ rightChildConst+        ThetaJoin _  -> leftChildConst ++ rightChildConst+        SemiJoin _   -> leftChildConst+        AntiJoin _   -> leftChildConst+        DisjUnion _  -> [ (c1, v1)+                        | (c1, v1) <- leftChildConst+                        , (c2, v2) <- rightChildConst+                        , c1 == c2+                        , v1 == v2+                        ]+        Difference _ -> leftChildConst+
+ src/Database/DSH/Optimizer/TA/Properties/Empty.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.TA.Properties.Empty where++import           Database.Algebra.Table.Lang++import           Database.DSH.Optimizer.TA.Properties.Types++inferEmptyNullOp :: NullOp -> Empty+inferEmptyNullOp op =+    case op of+        LitTable (vs, _)   -> null vs+        TableRef (_, _, _) -> False++inferEmptyUnOp :: Empty -> UnOp -> Empty+inferEmptyUnOp childEmpty op =+    case op of+        WinFun _         -> childEmpty+        RowNum (_, _, _) -> childEmpty+        RowRank (_, _)   -> childEmpty+        Rank (_, _)      -> childEmpty+        Project _        -> childEmpty+        Select _         -> childEmpty+        Distinct _       -> childEmpty+        Aggr (_, _)      -> childEmpty+        Serialize    _   -> childEmpty++inferEmptyBinOp :: Empty -> Empty -> BinOp -> Empty+inferEmptyBinOp leftEmpty rightEmpty op =+    case op of+        Cross _      -> leftEmpty || rightEmpty+        EqJoin _     -> leftEmpty || rightEmpty+        ThetaJoin _  -> leftEmpty || rightEmpty+        SemiJoin _   -> leftEmpty+        AntiJoin _   -> False+        DisjUnion _  -> False+        Difference _ -> False
+ src/Database/DSH/Optimizer/TA/Properties/ICols.hs view
@@ -0,0 +1,107 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Infer the input columns required in TableAlgebra plans.+module Database.DSH.Optimizer.TA.Properties.ICols where++import qualified Data.Set.Monad                           as S++import           Database.Algebra.Table.Lang++import           Database.DSH.Optimizer.TA.Properties.Auxiliary++inferIColsBinOp :: S.Set Attr  -- ^ columns that are required from us+                -> S.Set Attr  -- ^ Columns required from the left child+                -> S.Set Attr  -- ^ Output of the left child+                -> S.Set Attr  -- ^ Columns required from the right child+                -> S.Set Attr  -- ^ Output of the left child+                -> BinOp       -- ^ The operator+                -> (S.Set Attr, S.Set Attr)+inferIColsBinOp ownICols leftICols leftCols rightICols rightCols op =+    case op of+         -- Require columns from the originating side.+         Cross _ -> ( leftICols ∪ (ownICols ∩ leftCols)+                    , rightICols ∪ (ownICols ∩ rightCols) )++         -- Require columns from the originating side, in addition to the join+         -- columns.+         EqJoin (leftJoinCol, rightJoinCol) ->+             ( leftICols ∪ (ownICols ∩ leftCols) ∪ (S.singleton leftJoinCol)+             , rightICols ∪ (ownICols ∩rightCols) ∪ (S.singleton rightJoinCol) )+         ThetaJoin cs ->+             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs+                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs++                 leftICols' = leftICols ∪ (ownICols ∩ leftCols) ∪ leftExprCols+                 rightICols' = rightICols ∪ (ownICols ∩ rightCols) ∪ rightExprCols+             in (leftICols', rightICols')++         -- From the left, we require all columns required by us, in addition to+         -- the left join columns.+         SemiJoin cs ->+             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs+                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs++                 leftICols' = leftICols ∪ ownICols ∪ leftExprCols+                 rightICols' = rightExprCols+             in (leftICols', rightICols')+         AntiJoin cs ->+             let leftExprCols = S.unions $ map (\(l, _, _) -> exprCols l) cs+                 rightExprCols = S.unions $ map (\(_, r, _) -> exprCols r) cs++                 leftICols' = leftICols ∪ ownICols ∪ leftExprCols+                 rightICols' = rightExprCols+             in (leftICols', rightICols')++         -- The schemata of both union inputs must be kept in sync. No+         -- ICols-based (i.e. colummn-pruning) rewrites can be+         -- performed unless there is a guarantee that they happen in+         -- both branches.+         DisjUnion _  -> (leftCols, rightCols)++         Difference _ -> (leftICols ∪ leftCols, rightICols ∪ leftCols)++inferIColsUnOp :: S.Set Attr -> S.Set Attr -> UnOp -> S.Set Attr+inferIColsUnOp ownICols childICols op =+    case op of+        WinFun ((resCol, fun), partExprs, sortInf, _) ->+            (S.delete resCol ownICols)+            ∪ (winFunInput fun)+            ∪ (S.unions $ map (exprCols . fst) sortInf)+            ∪ (S.unions $ map exprCols partExprs)+            ∪ childICols+        -- Require the sorting columns, if the rownum output is required.+        RowNum (resCol, sortInf, groupExprs) ->+            (S.delete resCol ownICols)+            ∪ (S.unions $ map (exprCols . fst) sortInf)+            ∪ (S.unions $ map exprCols groupExprs)+            ∪ childICols++        RowRank (resCol, sortInf)   ->+            (S.delete resCol ownICols)+            ∪ (S.unions $ map (exprCols . fst) sortInf)+            ∪ childICols+        Rank (resCol, sortInf)      ->+            (S.delete resCol ownICols)+            ∪ (S.unions $ map (exprCols . fst) sortInf)+            ∪ childICols++        -- For projections we require input columns of expressions, but only for+        -- those output columns which are actually required from downstream.+        Project projs         -> S.foldr (∪) childICols $ S.fromList $ map (exprCols . snd) projs++        -- Require all columns for the select columns, in addition to columns+        -- required downstream+        Select e              -> childICols ∪ ownICols ∪ exprCols e+        Distinct _            -> childICols ∪ ownICols++        Aggr (acols, pexprs)  -> (S.foldr (∪) childICols $ S.fromList $ map (aggrInput . fst) acols)+                                 ∪+                                 (S.foldr (∪) S.empty $ S.fromList $ map (exprCols . snd) pexprs)++        Serialize cs          ->+            let (mDescr, mPos, cols) = cs+            in childICols+               ∪ (S.fromList $ map (\(PayloadCol c) -> c) cols)+               ∪ (maybe S.empty (\(DescrCol c) -> S.singleton c) mDescr)+               ∪ posCol mPos
+ src/Database/DSH/Optimizer/TA/Properties/Keys.hs view
@@ -0,0 +1,170 @@+-- FIXME once 7.8 is out, use overloaded list notation for sets+-- instead of S.fromList!+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++module Database.DSH.Optimizer.TA.Properties.Keys where++import           Data.Maybe+import           Data.List+import qualified Data.Set.Monad as S++import           Database.Algebra.Table.Lang++import           Database.DSH.Impossible+import           Database.DSH.Optimizer.TA.Properties.Auxiliary+import           Database.DSH.Optimizer.TA.Properties.Types+                 +subsetsOfSize :: Ord a => Int -> S.Set a -> S.Set (S.Set a)+subsetsOfSize n s+    | n == 0                    = S.singleton S.empty+    | S.size s < n || n < 0     = error "onlyLists: out of range n"+    | S.size s == n             = S.singleton s+    | otherwise                 = S.fromDistinctAscList . map S.fromDistinctAscList $+                                                         go n (S.size s) (S.toList s)+      where+        go 1 _ xs = map return xs+        go k l (x:xs)+            | k == l = [x:xs]+            | otherwise = map (x:) (go (k-1) (l-1) xs) ++ go k (l-1) xs+        go _ _ [] = $impossible++-- | Enumerate all subsets of size n++-- | Compute keys for rank and rowrank operators+rowRankKeys :: Attr -> S.Set Attr -> Card1 -> S.Set PKey -> S.Set PKey+rowRankKeys resCol sortCols childCard1 childKeys =+    -- All old keys stay intact+    childKeys+    ∪+    -- Trivial case: singleton input+    [ ss resCol | childCard1 ]+    ∪+    -- If sorting columns form a part of a key, the output column+    -- combined with the key columns that are not sorting columns also+    -- is a key.+    [ (ss resCol) ∪ (k ∖ sortCols)+    | k <- childKeys+    , k ∩ sortCols /= S.empty+    ]++inferKeysNullOp :: NullOp -> S.Set PKey+inferKeysNullOp op =+    case op of+        -- FIXME check all combinations of columns for uniqueness+        LitTable (vals, schema)  -> S.fromList+                                    $ map (ss . snd) +                                    $ filter (isUnique . fst)+                                    $ zip (transpose vals) (map fst schema)+          where+            isUnique :: [AVal] -> Bool+            isUnique vs = (length $ nub vs) == (length vs)++        TableRef (_, _, keys) -> S.fromList $ map (\(Key k) -> ls k) keys++inferKeysUnOp :: S.Set PKey -> Card1 -> S.Set Attr -> UnOp -> S.Set PKey+inferKeysUnOp childKeys childCard1 childCols op =+    case op of+        WinFun _                       -> childKeys+        RowNum (resCol, _, [])         -> S.insert (ss resCol) childKeys+        -- FIXME can we infer a key here if partitioning includes+        -- general expressions?+        RowNum (resCol, _, pexprs)     -> {- (S.singleton $ ls [resCol, pattr])+                                          ∪ -}+                                          [ ss resCol | childCard1 ]+                                          ∪+                                          childKeys+        -- FIXME infer complete rank keys+        RowRank (resCol, sortInfo)     -> childKeys -- rowRankKeys resCol (ls $ map fst sortInfo) childCard1 childKeys+        Rank (resCol, sortInfo)        -> childKeys -- rowRankKeys resCol (ls $ map fst sortInfo) childCard1 childKeys++        -- This is just the standard Pathfinder way: we take all keys+        -- whose columns survive the projection and update to the new+        -- attr names. We could consider all expressions, but need to+        -- be careful here as not all operators might be injective.+        Project projs           -> -- all sets A of a's s.t. |A| = |k| and +                                   -- associated bs = k+                                   S.foldr S.union S.empty+                                   [ [ as+                                     | as <- subsetsOfSize (S.size k) pa+                                     , let bs = [ b | (a, b) <- attrPairs, a ∈ as ]+                                     , bs == k+                                     ]+                                   | k <- childKeys+                                   -- check that the key survives at all+                                   , let attrPairs = S.fromList $ mapMaybe mapCol projs+                                   , k ⊆ [ snd x | x <- attrPairs ]+                                   -- generate the set pa of a's s.t. (a, b) ∈ attrPairs and b ∈ k+                                   -- i.e. consider only those a's for which the original b is+                                   -- actually part of the current key.+                                   , let pa = [ a | (a, b) <- attrPairs, b ∈ k ]+                                   ]++        Select _                 -> childKeys+        Distinct _               -> S.insert childCols childKeys +        Aggr (_, [])             -> S.empty+        Aggr (_, pexprs@(_ : _)) -> S.singleton $ S.fromList $ map fst pexprs+        Serialize _              -> S.empty ++inferKeysBinOp :: S.Set PKey -> S.Set PKey -> Card1 -> Card1 -> BinOp -> S.Set PKey+inferKeysBinOp leftKeys rightKeys leftCard1 rightCard1 op =+    case op of+        Cross _      -> [ k | k <- leftKeys, rightCard1 ]+                        ∪+                        [ k | k <- rightKeys, leftCard1 ]+                        ∪+                        [ k1 ∪ k2 | k1 <- leftKeys, k2 <- rightKeys ]+        EqJoin (a, b) -> [ k | k <- leftKeys, rightCard1 ]+                         ∪+                         [ k | k <- rightKeys, leftCard1 ]+                         ∪+                         [ k | k <- leftKeys, (ss b) ∈ rightKeys ]+                         ∪+                         [ k | k <- rightKeys, (ss a) ∈ leftKeys ]+                         ∪+                         [ ( k1 ∖ (ss a)) ∪ k2+                         | (ss b) ∈ rightKeys+                         , k1 <- leftKeys+                         , k2 <- rightKeys+                         ]+                         ∪+                         [ k1 ∪ (k2 ∖ (ss b))+                         | (ss a) ∈ leftKeys+                         , k1 <- leftKeys+                         , k2 <- rightKeys+                         ]+                         ∪+                         [ k1 ∪ k2 | k1 <- leftKeys, k2 <- rightKeys ]+                         +        ThetaJoin preds -> [ k | k <- leftKeys, rightCard1 ]+                           ∪+                           [ k | k <- rightKeys, leftCard1 ]+                           ∪+                           [ k +                           | k <- leftKeys+                           , (_, be, p) <- S.fromList preds+                           , p == EqJ+                           , b            <- singleCol be+                           , (ss b) ∈ rightKeys+                           ]+                           ∪+                           [ k +                           | k <- rightKeys+                           , (ae, _, p) <- S.fromList preds+                           , p == EqJ+                           , a            <- singleCol ae+                           , (ss a) ∈ leftKeys+                           ]+                           ∪+                           [ k1 ∪ k2 | k1 <- leftKeys, k2 <- rightKeys ]+                  +        SemiJoin _    -> leftKeys+        AntiJoin _    -> leftKeys+        DisjUnion _   -> S.empty -- FIXME need domain property.+        Difference _  -> leftKeys++singleCol :: Expr -> S.Set Attr+singleCol (ColE c) = S.singleton c+singleCol _        = S.empty++
+ src/Database/DSH/Optimizer/TA/Properties/Order.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++module Database.DSH.Optimizer.TA.Properties.Order where++import           Data.Maybe+import qualified Data.Set.Monad                             as S+import           Data.Tuple++import           Database.Algebra.Table.Lang++import           Database.DSH.Impossible++import           Database.DSH.Optimizer.TA.Properties.Auxiliary+import           Database.DSH.Optimizer.TA.Properties.Types++-- | Column 'c' has been overwritten by the current operator. Remove+-- all associated sorting information.+invalidate :: Attr -> Orders -> Orders+invalidate c order = [ o | o@(c', _) <- order, c /= c' ]++-- | Overwrite (if present) order information for column 'o' with new+-- information.+-- FIXME Handle case of arbitrary expressions defining order.+overwrite :: (Attr, [Expr]) -> Orders -> Orders+overwrite (resCol, ordExprs) os =+    if all isJust mOrdCols+    -- Check if the result column overwrites some older order column+    then if any ((== resCol) . fst) os+         then [ (resCol, ordCols) | (oc, _) <- os, oc == resCol ]+         else (resCol, ordCols) : os+    -- The order is defined by non-column expressions. We don't handle+    -- that case currently.+    else os++  where+    mOrdCols = map mColE ordExprs+    ordCols  = catMaybes mOrdCols++-- | Produce all new sorting columns from the list of new names per+-- old sorting column:+-- [[a, b, c], [d, e], [f]] => [[a, d, f], [a, e, f], [b, d, f], ...]+-- [[a, b, c], [], [f]]     => []+ordCombinations :: [[Attr]] -> [[Attr]]+ordCombinations []        = $impossible+ordCombinations (s : [])  = map (: []) s+ordCombinations (s : scs) = dist s (ordCombinations scs)++  where+    dist :: [Attr] -> [[Attr]] -> [[Attr]]+    dist as bs = [ a : b | a <- as, b <- bs ]++-- | Find all new names for column 'c'.+newCols :: [(Attr, Attr)] -> Attr -> [Attr]+newCols colMap c = [ cn | (co, cn) <- colMap, co == c ]++-- | Refresh order information with new names for the order column and+-- new names for the sorting columns.+update :: [(Attr, Attr)] -> (Attr, [Attr]) -> Orders+update colMap (ordCol, sortCols) =+    let ordCols'  = newCols colMap ordCol+        sortCols' = map (newCols colMap) sortCols++    in if any null sortCols'+       then []+       else [ (oc, scs) | oc <- ordCols', scs <- ordCombinations sortCols' ]++inferOrderUnOp :: Orders -> UnOp -> Orders+inferOrderUnOp childOrder op =+    case op of+        WinFun _                          -> childOrder+        RowNum (oc, scs, [])+             | not (null scs) +               -- Only consider ascending sorting+               && all ((== Asc) . snd) scs+               -- Avoid circular references+               && (ColE oc) `notElem` (map fst scs)+                                          -> overwrite (oc, map fst scs) childOrder+             | otherwise+                                          -> invalidate oc childOrder+        RowNum (resCol, _, _)             -> invalidate resCol childOrder+        RowRank (resCol, _)               -> invalidate resCol childOrder+        Rank (resCol, _)                  -> invalidate resCol childOrder+        Select _                          -> childOrder+        Distinct _                        -> childOrder+        Aggr _                            -> []+        Project projs                     ->+            let colMap = S.toList $ S.map swap $ S.fromList $ mapMaybe mapCol projs+            in concatMap (update colMap) childOrder+        Serialize _                       -> []++inferOrderBinOp :: Orders -> Orders -> BinOp -> Orders+inferOrderBinOp leftChildOrder rightChildOrder op =+    case op of+        Cross _      -> leftChildOrder ++ rightChildOrder+        EqJoin _     -> leftChildOrder ++ rightChildOrder+        ThetaJoin _  -> leftChildOrder ++ rightChildOrder+        SemiJoin _   -> leftChildOrder+        AntiJoin _   -> leftChildOrder+        DisjUnion _  -> []+        Difference _ -> leftChildOrder+
+ src/Database/DSH/Optimizer/TA/Properties/TopDown.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.TA.Properties.TopDown where++import           Control.Monad.State++import qualified Data.IntMap                                as M+import           Data.List+import qualified Data.Set.Monad                             as S++import           Database.Algebra.Dag+import           Database.Algebra.Dag.Common+import           Database.Algebra.Table.Lang++import           Database.DSH.Impossible+import           Database.DSH.Optimizer.Common.Auxiliary+import           Database.DSH.Optimizer.TA.Properties.ICols+import           Database.DSH.Optimizer.TA.Properties.Types+import           Database.DSH.Optimizer.TA.Properties.Use+++seed :: TopDownProps+seed = TDProps { pICols = S.empty, pUse = S.empty }++type InferenceState = NodeMap TopDownProps++lookupProps :: AlgNode -> State InferenceState TopDownProps+lookupProps n = do+    m <- get+    case M.lookup n m of+        Just props -> return props+        Nothing -> error "TopDown.lookupProps"++replaceProps :: AlgNode -> TopDownProps -> State InferenceState ()+replaceProps n p = modify (M.insert n p)++inferUnOp :: TopDownProps -> TopDownProps -> UnOp -> TopDownProps+inferUnOp ownProps cp op =+    TDProps { pICols = inferIColsUnOp (pICols ownProps) (pICols cp) op+            , pUse   = inferUseUnOp (pUse ownProps) (pUse cp) op }++inferBinOp :: BottomUpProps+           -> BottomUpProps+           -> TopDownProps+           -> TopDownProps+           -> TopDownProps+           -> BinOp+           -> (TopDownProps, TopDownProps)+inferBinOp childBUProps1 childBUProps2 ownProps cp1 cp2 op =+  let (crc1', crc2') = inferIColsBinOp (pICols ownProps)+                                       (pICols cp1)+                                       (S.map fst $ pCols childBUProps1)+                                       (pICols cp2)+                                       (S.map fst $ pCols childBUProps2)+                                       op+      (urc1', urc2') = inferUseBinOp (pUse ownProps)+                                     (pUse cp1)+                                     (pUse cp2)+                                     (S.map fst $ pCols childBUProps1)+                                     (S.map fst $ pCols childBUProps2)+                                     op+      cp1' = TDProps { pICols = crc1', pUse = urc1' }+      cp2' = TDProps { pICols = crc2', pUse = urc2' }+  in (cp1', cp2')++inferChildProperties :: NodeMap BottomUpProps -> AlgebraDag TableAlgebra -> AlgNode -> State InferenceState ()+inferChildProperties buPropMap d n = do+    ownProps <- lookupProps n+    case operator n d of+        NullaryOp _ -> return ()+        UnOp op c -> do+            cp <- lookupProps c+            let cp' = inferUnOp ownProps cp op+            replaceProps c cp'+        BinOp op c1 c2 -> do+            cp1 <- lookupProps c1+            cp2 <- lookupProps c2+            let buProps1 = lookupUnsafe buPropMap "TopDown.inferChildProperties" c1+                buProps2 = lookupUnsafe buPropMap "TopDown.inferChildProperties" c2+            let (cp1', cp2') = inferBinOp buProps1 buProps2 ownProps cp1 cp2 op+            replaceProps c1 cp1'+            replaceProps c2 cp2'+        TerOp _ _ _ _ -> $impossible++-- | Infer properties during a top-down traversal.+inferAllProperties :: NodeMap BottomUpProps -> [AlgNode] -> AlgebraDag TableAlgebra -> NodeMap AllProps+inferAllProperties buPropMap topOrderedNodes d =+    case mergeProps buPropMap tdPropMap of+        Just ps -> ps+        Nothing -> $impossible+  where+    tdPropMap = execState action initialMap+    action = mapM_ (inferChildProperties buPropMap d) topOrderedNodes++    initialMap = M.map (const seed) $ nodeMap d++    mergeProps :: NodeMap BottomUpProps -> NodeMap TopDownProps -> Maybe (NodeMap AllProps)+    mergeProps bum tdm = do+        let keys1 = M.keys bum+            keys2 = M.keys tdm+            keys  = keys1 `intersect` keys2+        guard $ length keys == length keys1 && length keys == length keys2++        let merge :: AlgNode -> Maybe (AlgNode, AllProps)+            merge n = do+                bup <- M.lookup n bum+                tdp <- M.lookup n tdm+                return (n, AllProps { td = tdp, bu = bup })++        merged <- mapM merge keys+        return $ M.fromList merged++
+ src/Database/DSH/Optimizer/TA/Properties/Types.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++module Database.DSH.Optimizer.TA.Properties.Types where++import qualified Data.Set.Monad              as S+import           Database.Algebra.Table.Lang+import           Database.DSH.Impossible++----------------------------------------------------------------------------+-- Property types++data TopDownProps = TDProps { pICols :: S.Set Attr+                            , pUse   :: S.Set Attr+                            }++instance Show TopDownProps where+    show ps = show $ S.toList (pICols ps)++-- FIXME: unite with Database.Algebra.Pathfinder....Data.Algebra.Key+type PKey = S.Set Attr++-- | Signal if an operator produces exactly one or zero tuples, respectively.+type Card1 = Bool+type Empty = Bool++type Orders = [(Attr, [Attr])]++type ConstCol = (Attr, AVal)++data BottomUpProps = BUProps { pCols  :: S.Set TypedAttr+     		     	     , pKeys  :: S.Set PKey+                             , pCard1 :: Card1+                             , pEmpty :: Empty+                             , pOrder :: Orders+                             , pConst :: [ConstCol]+     		     	     } deriving (Show)++data AllProps = AllProps { bu :: BottomUpProps, td :: TopDownProps } deriving (Show)++----------------------------------------------------------------------------+-- Utility functions on properties++typeOf :: Attr -> S.Set TypedAttr -> ATy+typeOf k s =+    case S.toList $ [ b | (a, b) <- s, k == a ] of+        [b] -> b+        _   -> $impossible
+ src/Database/DSH/Optimizer/TA/Properties/Use.hs view
@@ -0,0 +1,96 @@+{-# LANGUAGE MonadComprehensions #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Infer columns whose exact values are required to compute the+-- correct result.+module Database.DSH.Optimizer.TA.Properties.Use where++import qualified Data.Set.Monad                           as S++import           Database.Algebra.Table.Lang++import           Database.DSH.Optimizer.TA.Properties.Auxiliary++flatten :: S.Set (S.Set Attr) -> S.Set Attr+flatten = S.foldl' (∪) S.empty+++inferUseBinOp :: S.Set Attr+              -> S.Set Attr+              -> S.Set Attr+              -> S.Set Attr+              -> S.Set Attr+              -> BinOp+              -> (S.Set Attr, S.Set Attr)+inferUseBinOp ownUse leftUse rightUse leftCols rightCols op =+    case op of+         Cross _      -> ( leftUse ∪ [ c | c <- leftCols, c ∈ ownUse ]+                         , rightUse ∪ [ c | c <- rightCols, c ∈ ownUse ] )++         EqJoin (jc1, jc2) -> ( leftUse ∪ (ss jc1) ∪ [ c | c <- leftCols, c ∈ ownUse ]+                              , rightUse ∪ (ss jc2) ∪ [ c | c <- rightCols, c ∈ ownUse ] )+         ThetaJoin ps -> ( leftUse+                           ∪+                           flatten [ exprCols a | (a, _, _) <- S.fromList ps ]+                           ∪+                           [ c | c <- leftCols, c ∈ ownUse ]+                         , rightUse+                           ∪+                           flatten [ exprCols b | (_, b, _) <- S.fromList ps ]+                           ∪+                           [ c | c <- rightCols, c ∈ ownUse ]+                         )+         SemiJoin ps  -> ( leftUse+                           ∪+                           flatten [ exprCols a | (a, _, _) <- S.fromList ps ]+                           ∪+                           [ c | c <- leftCols, c ∈ ownUse ]+                         , rightUse+                           ∪+                           flatten [ exprCols b | (_, b, _) <- S.fromList ps ]+                         )+         AntiJoin ps  -> ( leftUse+                           ∪+                           flatten [ exprCols a | (a, _, _) <- S.fromList ps ]+                           ∪+                           [ c | c <- leftCols, c ∈ ownUse ]+                         , rightUse+                           ∪+                           flatten [ exprCols b | (_, b, _) <- S.fromList ps ])++         DisjUnion _  -> ( leftUse ∪ leftCols, rightUse ∪ rightCols )+         Difference _ -> ( leftUse ∪ leftCols, rightUse ∪ rightCols )++absPos :: SerializeOrder -> S.Set Attr+absPos (AbsPos c) = S.singleton c+absPos (RelPos _) = S.empty+absPos NoPos      = S.empty++inferUseUnOp :: S.Set Attr -> S.Set Attr -> UnOp -> S.Set Attr+inferUseUnOp ownUse childUse op =+    case op of+        WinFun ((resCol, winFun), partExprs, sortCols, _) ->+            childUse+            ∪ (S.delete resCol ownUse)+            ∪ (S.unions $ map exprCols partExprs)+            ∪ (S.unions $ map (exprCols . fst) sortCols)+            ∪ (winFunInput winFun)+        RowNum (resCol, _, _)     -> childUse ∪ (S.delete resCol ownUse)+        RowRank (resCol, _)       -> childUse ∪ (S.delete resCol ownUse)+        Rank (resCol, _)          -> childUse ∪ (S.delete resCol ownUse)+        Project projs             -> childUse+                                     ∪ (unionss [ exprCols e | (a, e) <- S.fromList projs, a ∈ ownUse ])+        Select e                  -> childUse ∪ ownUse ∪ (exprCols e)+        Distinct _                -> childUse ∪ ownUse++        -- FIXME unconditionally declaring pcols as used might be a bit too defensive.+        Aggr (acols, pexprs)      -> (S.unions $ map (exprCols . snd) pexprs)+                                     ∪+                                     (S.unions $ map (aggrInput . fst) acols)++        Serialize (md, mp, cs)    -> childUse+                                     ∪ (S.fromList $ map (\(PayloadCol c) -> c) cs)+                                     ∪ (maybe S.empty (\(DescrCol c) -> S.singleton c) md)+                                     -- FIXME once order and -- surrogates are decoupled, absolute pos+                                     -- values are no longer required.+                                     ∪ absPos mp
+ src/Database/DSH/Optimizer/TA/Rewrite/Basic.hs view
@@ -0,0 +1,562 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections   #-}++module Database.DSH.Optimizer.TA.Rewrite.Basic where++import           Debug.Trace+import           Text.Printf++import           Control.Applicative+import           Control.Monad+import           Data.Either.Combinators+import           Data.List                                  hiding (insert)+import           Data.Maybe+import qualified Data.Set.Monad                             as S++import           Database.Algebra.Dag.Common+import           Database.Algebra.Table.Lang                hiding (replace)++import           Database.DSH.Impossible+import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.TA.Properties.Auxiliary+import           Database.DSH.Optimizer.TA.Properties.Types+import           Database.DSH.Optimizer.TA.Properties.Const+import           Database.DSH.Optimizer.TA.Rewrite.Common++cleanup :: TARewrite Bool+cleanup = iteratively $ sequenceRewrites [ applyToAll noProps cleanupRules+                                         , applyToAll inferAll cleanupRulesTopDown+                                         ]++cleanupRules :: TARuleSet ()+cleanupRules = [ stackedProject+               , serializeProject+               , pullProjectWinFun+               , pullProjectSelect+               , duplicateSortingCriteriaWin+               , duplicateSortingCriteriaRownum+               , duplicateSortingCriteriaSerialize+               ]++cleanupRulesTopDown :: TARuleSet AllProps+cleanupRulesTopDown = [ unreferencedRownum+                      , unreferencedRank+                      , unreferencedProjectCols+                      , unreferencedAggrCols+                      , unreferencedLiteralCols+                      , postFilterRownum+                      , inlineSortColsRownum+                      , inlineSortColsSerialize+                      , inlineSortColsWinFun+                      , keyPrefixOrdering+                      , constAggrKey+                      , constRownumCol+                      , constRowRankCol+                      , constSerializeCol+                      , constWinOrderCol+                      ]++----------------------------------------------------------------------------------+-- Rewrite rules++-- | Eliminate rownums which re-generate positions based on one+-- sorting column. These rownums typically occur after filtering+-- operators, i.e. select, antijoin, semijoin. If the absolute values+-- generated by the rownum are not required and only the encoded order+-- is relevant, we can safely remove the rownum and use the sorting+-- column. In that case, positions might not be dense anymore.+postFilterRownum :: TARule AllProps+postFilterRownum q =+  $(dagPatMatch 'q "RowNum args (q1)"+    [| do+        (res, [(ColE sortCol, Asc)], []) <- return $(v "args")+        useCols <- pUse <$> td <$> properties q+        keys    <- pKeys <$> bu <$> properties $(v "q1")+        cols    <- pCols <$> bu <$> properties $(v "q1")++        -- To get rid of the rownum, the absolute values generated by+        -- it must not be required.+        predicate $ not $ res `S.member` useCols++        -- Rownum produces a key. If we remove the rownum because its+        -- absolute values are not needed and replace it with the+        -- original sorting column, it should still be a key.+        predicate $ (S.singleton sortCol) `S.member` keys++        -- If we reuse a sorting column, it's type should be int.+        predicate $ AInt == typeOf sortCol cols++        return $ do+          logRewrite "Basic.Rownum.Unused" q+          let projs = (res, ColE sortCol) : map (\c -> (c, ColE c)) (map fst $ S.toList cols)+          void $ replaceWithNew q $ UnOp (Project projs) $(v "q1") |])+++---------------------------------------------------------------------------+-- ICols rewrites++-- | Prune a rownumber operator if its output is not required+unreferencedRownum :: TARule AllProps+unreferencedRownum q =+  $(dagPatMatch 'q "RowNum args (q1)"+    [| do+         (res, _, _) <- return $(v "args")+         neededCols  <- pICols <$> td <$> properties q+         predicate $ not (res `S.member` neededCols)++         return $ do+           logRewrite "Basic.ICols.Rownum" q+           replace q $(v "q1") |])++-- | Prune a rownumber operator if its output is not required+unreferencedRank :: TARule AllProps+unreferencedRank q =+  $(dagPatMatch 'q "[Rank | RowRank] args (q1)"+    [| do+         (res, _) <- return $(v "args")+         neededCols  <- pICols <$> td <$> properties q+         predicate $ not (res `S.member` neededCols)++         return $ do+           logRewrite "Basic.ICols.Rank" q+           replace q $(v "q1") |])++-- | Prune projections from a project operator if the result columns+-- are not required.+unreferencedProjectCols :: TARule AllProps+unreferencedProjectCols q =+  $(dagPatMatch 'q "Project projs (q1)"+    [| do+        neededCols <- pICols <$> td <$> properties q+        let neededProjs = filter (flip S.member neededCols . fst) $(v "projs")++        -- Only modify the project if we could actually get rid of some columns.+        predicate $ length neededProjs < length $(v "projs")++        return $ do+          logRewrite "Basic.ICols.Project" q+          void $ replaceWithNew q $ UnOp (Project neededProjs) $(v "q1") |])++-- | Remove aggregate functions whose output is not referenced.+unreferencedAggrCols :: TARule AllProps+unreferencedAggrCols q =+  $(dagPatMatch 'q "Aggr args (q1)"+    [| do+        neededCols <- pICols <$> td <$> properties q+        (aggrs, partCols) <- return $(v "args")++        let neededAggrs = filter (flip S.member neededCols . snd) aggrs++        predicate $ length neededAggrs < length aggrs++        return $ do+          case neededAggrs of+              -- If the output of all aggregate functions is not+              -- required, we can replace it with a distinct operator+              -- on the grouping columns.+              [] -> do+                  logRewrite "Basic.ICols.Aggr.Prune" q+                  projectNode <- insert $ UnOp (Project partCols) $(v "q1")+                  void $ replaceWithNew q $ UnOp (Distinct ()) projectNode++              -- Otherwise, we just prune the unreferenced aggregate functions+              _ : _ -> do+                  logRewrite "Basic.ICols.Aggr.Narrow" q+                  void $ replaceWithNew q $ UnOp (Aggr (neededAggrs, partCols)) $(v "q1") |])+++unreferencedLiteralCols :: TARule AllProps+unreferencedLiteralCols q =+  $(dagPatMatch 'q "LitTable tab "+    [| do+         neededCols <- pICols <$> td <$> properties q++         predicate (not $ S.null neededCols)++         let (tuples, schema)  = $(v "tab")++         predicate (not $ null tuples)++         predicate $ S.size neededCols < length schema+    +         return $ do++             let columns = transpose tuples+             let (reqCols, reqSchema) = +                  unzip +                  $ filter (\(_, (colName, _)) -> colName `S.member` neededCols) +                  $ zip columns schema+             let reqTuples = transpose reqCols++             void $ replaceWithNew q $ NullaryOp $ LitTable (reqTuples, reqSchema) |])++----------------------------------------------------------------------------------+-- Basic Const rewrites++isConstExpr :: [ConstCol] -> Expr -> Bool+isConstExpr constCols e = isJust $ constExpr constCols e++-- | Prune const columns from aggregation keys+constAggrKey :: TARule AllProps+constAggrKey q =+  $(dagPatMatch 'q "Aggr args (q1)"+    [| do+         constCols   <- pConst <$> bu <$> properties $(v "q1")+         neededCols  <- S.toList <$> pICols <$> td <$> properties q+         (aggrFuns, keyCols@(_:_)) <- return $(v "args")++         let keyCols'   = filter (\(_, e) -> not $ isConstExpr constCols e) keyCols+             prunedKeys = (map fst keyCols) \\ (map fst keyCols')++         predicate $ not $ null prunedKeys++         return $ do+             logRewrite "Basic.Const.Aggr" q+             let necessaryKeys = prunedKeys `intersect` neededCols++                 constProj c   = lookup c constCols >>= \val -> return (c, ConstE val)++                 constProjs    = mapMaybe constProj necessaryKeys++                 proj          = map (\(_, c) -> (c, ColE c)) aggrFuns+                                 +++                                 map (\(c, _) -> (c, ColE c)) keyCols'+                                 +++                                 constProjs+                                 ++             aggrNode <- insert $ UnOp (Aggr ($(v "aggrFuns"), keyCols')) $(v "q1")+             void $ replaceWithNew q $ UnOp (Project proj) aggrNode |])++constRownumCol :: TARule AllProps+constRownumCol q =+  $(dagPatMatch 'q "RowNum args (q1)"+    [| do+         constCols <- pConst <$> bu <$> properties $(v "q1")++         (resCol, sortCols, partExprs) <- return $(v "args")+         let sortCols' = filter (\(e, _) -> not $ isConstExpr constCols e) sortCols+         predicate $ length sortCols' < length sortCols+         +         return $ do+             logRewrite "Basic.Const.RowNum" q+             void $ replaceWithNew q $ UnOp (RowNum (resCol, sortCols', partExprs)) $(v "q1") |])++constRowRankCol :: TARule AllProps+constRowRankCol q =+  $(dagPatMatch 'q "RowRank args (q1)"+    [| do+         constCols          <- pConst <$> bu <$> properties $(v "q1")+         (resCol, sortCols) <- return $(v "args")+         let sortCols' = filter (\(e, _) -> not $ isConstExpr constCols e) sortCols+         predicate $ length sortCols' < length sortCols+         +         return $ do+             logRewrite "Basic.Const.RowRank" q+             void $ replaceWithNew q $ UnOp (RowRank (resCol, sortCols')) $(v "q1") |])++constSerializeCol :: TARule AllProps+constSerializeCol q =+  $(dagPatMatch 'q "Serialize args (q1)"+    [| do+         (mDescr, RelPos sortCols, payload) <- return $(v "args")+         constCols                          <- map fst <$> pConst <$> bu <$> properties $(v "q1")++         let sortCols' = filter (\c -> c `notElem` constCols) sortCols+         predicate $ length sortCols' < length sortCols+         +         return $ do+             logRewrite "Basic.Const.Serialize" q+             void $ replaceWithNew q $ UnOp (Serialize (mDescr, RelPos sortCols', payload)) $(v "q1") |])++constWinOrderCol :: TARule AllProps+constWinOrderCol q =+  $(dagPatMatch 'q "WinFun args (q1)"+    [| do+         constCols <- pConst <$> bu <$> properties $(v "q1")+         let (f, part, sortCols, frameSpec) = $(v "args")+         let sortCols' = filter (\(e, _) -> not $ isConstExpr constCols e) sortCols+         predicate $ length sortCols' < length sortCols++         return $ do+             logRewrite "Basic.Const.WinFun" q+             void $ replaceWithNew q $ UnOp (WinFun (f, part, sortCols', frameSpec)) $(v "q1") |])+++----------------------------------------------------------------------------------+-- Basic Order rewrites++-- | @lookupSortCol@ returns @Left@ if there is no mapping from the+-- original sort column and @Right@ if there is a mapping from the+-- original sort column to a list of columns that define the same+-- order.+lookupSortCol :: SortSpec -> Orders -> TAMatch AllProps (Either [SortSpec] [SortSpec])+lookupSortCol (ColE oldSortCol, Asc) os =+    case lookup oldSortCol os of+        Nothing          -> return $ Left [(ColE oldSortCol, Asc)]+        Just newSortCols -> return $ Right $ map (\c -> (ColE c, Asc)) newSortCols+lookupSortCol (_, Asc)               _  = fail "only consider column expressions for now"+lookupSortCol (_, Desc)              _  = fail "only consider ascending orders"++inlineSortColsRownum :: TARule AllProps+inlineSortColsRownum q =+  $(dagPatMatch 'q "RowNum o (q1)"+    [| do+        (resCol, sortCols@(_:_), []) <- return $(v "o")++        predicate $ all ((== Asc) . snd) sortCols++        orders@(_:_) <- pOrder <$> bu <$> properties $(v "q1")++        -- For each sorting column, try to find the original+        -- order-defining sorting columns.+        mSortCols <- mapM (flip lookupSortCol orders) sortCols++        -- The rewrite should only fire if something actually changes+        predicate $ any isRight mSortCols++        let sortCols' = nub $ concatMap (either id id) mSortCols++        return $ do+          logRewrite "Basic.InlineOrder.RowNum" q+          void $ replaceWithNew q $ UnOp (RowNum (resCol, sortCols', [])) $(v "q1") |])++inlineSortColsSerialize :: TARule AllProps+inlineSortColsSerialize q =+  $(dagPatMatch 'q "Serialize scols (q1)"+    [| do+        (d, RelPos cs, reqCols) <- return $(v "scols")+        orders@(_:_) <- pOrder <$> bu <$> properties $(v "q1")++        let cs' = nub $ concatMap (\c -> maybe [c] id $ lookup c orders) cs+        predicate $ cs /= cs'++        return $ do+            logRewrite "Basic.InlineOrder.Serialize" q+            void $ replaceWithNew q $ UnOp (Serialize (d, RelPos cs', reqCols)) $(v "q1") |])++inlineSortColsWinFun :: TARule AllProps+inlineSortColsWinFun q =+  $(dagPatMatch 'q "WinFun args (q1)"+    [| do+        let (f, part, sortCols, frameSpec) = $(v "args")++        orders@(_:_) <- pOrder <$> bu <$> properties $(v "q1")++        -- For each sorting column, try to find the original+        -- order-defining sorting columns.+        mSortCols <- mapM (flip lookupSortCol orders) sortCols++        -- The rewrite should only fire if something actually changes+        predicate $ any isRight mSortCols++        let sortCols' = nub $ concatMap (either id id) mSortCols+            args'     = (f, part, sortCols', frameSpec)++        return $ do+            logRewrite "Basic.InlineOrder.WinFun" q+            void $ replaceWithNew q $ UnOp (WinFun args') $(v "q1") |])++isKeyPrefix :: S.Set PKey -> [SortSpec] -> Bool+isKeyPrefix keys orderCols =+    case mapM mColE $ map fst orderCols of+        Just cols -> S.fromList cols `S.member` keys+        Nothing   -> False++-- | If a prefix of the ordering columns in a rownum operator forms a+-- key, the suffix can be removed.+keyPrefixOrdering :: TARule AllProps+keyPrefixOrdering q =+  $(dagPatMatch 'q "RowNum args (q1)"+    [| do+        (resCol, sortCols, []) <- return $(v "args")+        keys                   <- pKeys <$> bu <$> properties $(v "q1")++        predicate $ not $ null sortCols+       +        -- All non-empty and incomplete prefixes of the ordering+        -- columns+        let ordPrefixes = init $ drop 1 (inits sortCols)+        Just prefix <- return $ find (isKeyPrefix keys) ordPrefixes++        return $ do+            logRewrite "Basic.SimplifyOrder.KeyPrefix" q+            let sortCols' = take (length prefix) sortCols+            void $ replaceWithNew q $ UnOp (RowNum (resCol, sortCols', [])) $(v "q1") |])++duplicateSortingCriteriaRownum :: TARule ()+duplicateSortingCriteriaRownum q =+  $(dagPatMatch 'q "RowNum args (q1)"+    [| do+        (resCol, sortCols, []) <- return $(v "args")++        let sortCols' = nub sortCols++        predicate $ length sortCols' < length sortCols++        return $ do+            logRewrite "Basic.SimplifyOrder.Duplicates.Rownum" q+            let args' = (resCol, sortCols', [])+            void $ replaceWithNew q $ UnOp (RowNum args') $(v "q1") |])++duplicateSortingCriteriaWin :: TARule ()+duplicateSortingCriteriaWin q =+  $(dagPatMatch 'q "WinFun args (q1)"+    [| do+        let (winFuns, part, sortCols, mFrameBounds) = $(v "args")+        +        let sortCols' = nub sortCols++        predicate $ length sortCols' < length sortCols++        return $ do+            logRewrite "Basic.SimplifyOrder.Duplicates.WinFun" q+            let args' = (winFuns, part, sortCols', mFrameBounds)+            void $ replaceWithNew q $ UnOp (WinFun args') $(v "q1") |])++duplicateSortingCriteriaSerialize :: TARule ()+duplicateSortingCriteriaSerialize q =+  $(dagPatMatch 'q "Serialize args (q1)"+    [| do+        (mDescr, RelPos sortCols, payload) <- return $(v "args")+        let sortCols' = nub sortCols++        predicate $ length sortCols' < length sortCols++        return $ do+            logRewrite "Basic.SimplifyOrder.Duplicates.Serialize" q+            let args' = (mDescr, RelPos sortCols', payload)+            void $ replaceWithNew q $ UnOp (Serialize args') $(v "q1") |])+        ++----------------------------------------------------------------------------------+-- Serialize rewrites++-- | Merge a projection which only maps columns into a Serialize operator.+serializeProject :: TARule ()+serializeProject q =+    $(dagPatMatch 'q "Serialize scols (Project projs (q1))"+      [| do+          (d, p, reqCols) <- return $(v "scols")++          let projCol (c', ColE c) = return (c', c)+              projCol _            = fail "no match"++              lookupFail x xys = case lookup x xys of+                  Just y  -> return y+                  Nothing -> fail "no match"++          colMap <- mapM projCol $(v "projs")++          -- find new names for all required columns+          reqCols' <- mapM (\(PayloadCol c) -> PayloadCol <$> lookupFail c colMap) reqCols++          -- find new name for the descriptor column (if required)+          d' <- case d of+              Just (DescrCol c)  -> Just <$> DescrCol <$> lookupFail c colMap+              Nothing            -> return Nothing++          -- find new name for the pos column (if required)+          p' <- case p of+              AbsPos c  -> AbsPos <$> lookupFail c colMap+              RelPos cs -> RelPos <$> mapM (flip lookupFail colMap) cs+              NoPos     -> return NoPos++          return $ do+              logRewrite "Basic.Serialize.Project" q+              void $ replaceWithNew q $ UnOp (Serialize (d', p', reqCols')) $(v "q1") |])++--------------------------------------------------------------------------------+-- Pulling projections through other operators and merging them into+-- other operators++inlineExpr :: [Proj] -> Expr -> Expr+inlineExpr proj expr =+    case expr of+        BinAppE op e1 e2 -> BinAppE op (inlineExpr proj e1) (inlineExpr proj e2)+        UnAppE op e      -> UnAppE op (inlineExpr proj e)+        ColE c           -> fromMaybe (failedLookup c) (lookup c proj)+        ConstE val       -> ConstE val+        IfE c t e        -> IfE (inlineExpr proj c) (inlineExpr proj t) (inlineExpr proj e)++  where+    failedLookup :: Attr -> a+    failedLookup c = trace (printf "mergeProjections: column lookup %s failed\n%s\n%s"+                                   c (show expr) (show proj))+                           $impossible++mergeProjections :: [Proj] -> [Proj] -> [Proj]+mergeProjections proj1 proj2 = map (\(c, e) -> (c, inlineExpr proj2 e)) proj1++stackedProject :: TARule ()+stackedProject q =+  $(dagPatMatch 'q "Project ps1 (Project ps2 (qi))"+    [| do+         return $ do+           let ps = mergeProjections $(v "ps1") $(v "ps2")+           logRewrite "Basic.Project.Merge" q+           void $ replaceWithNew q $ UnOp (Project ps) $(v "qi") |])++++mapWinFun :: (Expr -> Expr) -> WinFun -> WinFun+mapWinFun f (WinMax e)        = WinMax $ f e+mapWinFun f (WinMin e)        = WinMin $ f e+mapWinFun f (WinSum e)        = WinSum $ f e+mapWinFun f (WinAvg e)        = WinAvg $ f e+mapWinFun f (WinAll e)        = WinAll $ f e+mapWinFun f (WinAny e)        = WinAny $ f e+mapWinFun f (WinFirstValue e) = WinFirstValue $ f e+mapWinFun f (WinLastValue e)  = WinLastValue $ f e+mapWinFun _ WinCount          = WinCount++mapAggrFun :: (Expr -> Expr) -> AggrType -> AggrType+mapAggrFun f (Max e) = Max $ f e+mapAggrFun f (Min e) = Min $ f e+mapAggrFun f (Sum e) = Sum $ f e+mapAggrFun f (Avg e) = Avg $ f e+mapAggrFun f (All e) = All $ f e+mapAggrFun f (Any e) = Any $ f e+mapAggrFun _ Count   = Count++pullProjectWinFun :: TARule ()+pullProjectWinFun q =+    $(dagPatMatch 'q "WinFun args (Project proj (q1))"+      [| do+          -- Only consider window functions without partitioning for+          -- now. Partitioning requires proper values and inlining+          -- would be problematic.+          ((resCol, f), [], sortSpec, frameSpec) <- return $(v "args")++          -- If the window function result overwrites one of the+          -- projection columns, we can't pull.+          predicate $ resCol `notElem` (map fst $(v "proj"))++          return $ do+              logRewrite "Basic.PullProject.WinFun" q++              -- Merge the projection expressions into window function+              -- arguments and ordering expressions.+              let f'        = mapWinFun (inlineExpr $(v "proj")) f++                  sortSpec' = map (\(e, d) -> (inlineExpr $(v "proj") e, d)) sortSpec++                  proj'     = $(v "proj") ++ [(resCol, ColE resCol)]++              winNode <- insert $ UnOp (WinFun ((resCol, f'), [], sortSpec', frameSpec)) $(v "q1")+              void $ replaceWithNew q $ UnOp (Project proj') winNode |])++pullProjectSelect :: TARule ()+pullProjectSelect q =+    $(dagPatMatch 'q "Select p (Project proj (q1))"+      [| do+          return $ do+              logRewrite "Basic.PullProject.Select" q+              let p' = inlineExpr $(v "proj") $(v "p")+              selectNode <- insert $ UnOp (Select p') $(v "q1")+              void $ replaceWithNew q $ UnOp (Project $(v "proj")) selectNode |])++inlineJoinPredRight :: [Proj] -> [(Expr, Expr, JoinRel)] -> [(Expr, Expr, JoinRel)]+inlineJoinPredRight proj p = map inlineConjunct p+  where+    inlineConjunct (le, re, rel) = (le, inlineExpr proj re, rel)
+ src/Database/DSH/Optimizer/TA/Rewrite/Common.hs view
@@ -0,0 +1,38 @@+module Database.DSH.Optimizer.TA.Rewrite.Common where++import qualified Data.IntMap                                   as M++import           Database.Algebra.Dag.Common++import           Database.DSH.Common.QueryPlan++import           Database.DSH.Optimizer.Common.Rewrite++import           Database.Algebra.Table.Lang++import           Database.DSH.VL.Vector++import           Database.DSH.Optimizer.TA.Properties.BottomUp+import           Database.DSH.Optimizer.TA.Properties.TopDown+import           Database.DSH.Optimizer.TA.Properties.Types++  -- Type abbreviations for convenience+type TARewrite p = Rewrite TableAlgebra (Shape NDVec) p+type TARule p = Rule TableAlgebra p (Shape NDVec)+type TARuleSet p = RuleSet TableAlgebra  p (Shape NDVec)+type TAMatch p = Match TableAlgebra p (Shape NDVec)++inferBottomUp :: TARewrite (NodeMap BottomUpProps)+inferBottomUp = do+  props <- infer inferBottomUpProperties+  return props++inferAll :: TARewrite (NodeMap AllProps)+inferAll = do+  to <- topsort+  buPropMap <- infer inferBottomUpProperties+  props <- infer (inferAllProperties buPropMap to)+  return props++noProps :: Monad m => m (M.IntMap a)+noProps = return M.empty
+ src/Database/DSH/Optimizer/VL/OptimizeVL.hs view
@@ -0,0 +1,56 @@+module Database.DSH.Optimizer.VL.OptimizeVL where++import qualified Data.IntMap                                                      as M++import qualified Database.Algebra.Dag                                             as Dag++import           Database.DSH.Common.QueryPlan++import           Database.DSH.VL.Lang+import           Database.DSH.VL.Vector++import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Rewrite.Expressions+import           Database.DSH.Optimizer.VL.Rewrite.PruneEmpty+import           Database.DSH.Optimizer.VL.Rewrite.Redundant++type RewriteClass = Rewrite VL (Shape VLDVec) Bool++rewriteClasses :: [(Char, RewriteClass)]+rewriteClasses = [ ('E', pruneEmpty)+                 , ('R', removeRedundancy)+                 , ('C', optExpressions)+                 ]++defaultPipeline :: [RewriteClass]+defaultPipeline = case assemblePipeline "ER" of+  Just p -> p+  Nothing -> error "invalid default pipeline"++runPipeline +  :: Dag.AlgebraDag VL +  -> (Shape VLDVec) +  -> [RewriteClass] +  -> Bool -> (Dag.AlgebraDag VL, Log, Shape VLDVec)+runPipeline d sh pipeline debug = (d', rewriteLog, sh')+  where (d', sh', _, rewriteLog) = runRewrite (sequence_ pipeline) d sh debug++assemblePipeline :: String -> Maybe [RewriteClass]+assemblePipeline s = mapM (flip lookup rewriteClasses) s++optimizeVL :: [RewriteClass] -> QueryPlan VL VLDVec -> QueryPlan VL VLDVec+optimizeVL pipeline plan =+#ifdef DEBUGGRAPH+  let (d, _, shape) = runPipeline (queryDag plan) (queryShape plan) pipeline True+#else+  let (d, _, shape) = runPipeline (queryDag plan) (queryShape plan) pipeline False+#endif+  in QueryPlan { queryDag = d, queryShape = shape, queryTags = M.empty }++optimizeVL' :: [RewriteClass] -> QueryPlan VL VLDVec -> (QueryPlan VL VLDVec, Log)+optimizeVL' pipeline plan =+  let (d, l, shape) = runPipeline (queryDag plan) (queryShape plan) pipeline False+  in (QueryPlan { queryDag = d, queryShape = shape, queryTags = M.empty }, l)++optimizeVLDefault :: QueryPlan VL VLDVec -> QueryPlan VL VLDVec+optimizeVLDefault = optimizeVL defaultPipeline
+ src/Database/DSH/Optimizer/VL/Properties/BottomUp.hs view
@@ -0,0 +1,100 @@+module Database.DSH.Optimizer.VL.Properties.BottomUp where++import Text.Printf++import Database.Algebra.Dag+import Database.Algebra.Dag.Common++import Database.DSH.VL.Lang+import Database.DSH.Optimizer.Common.Auxiliary+import Database.DSH.Optimizer.Common.Rewrite+import Database.DSH.Optimizer.VL.Properties.Card+import Database.DSH.Optimizer.VL.Properties.Const+import Database.DSH.Optimizer.VL.Properties.Empty+import Database.DSH.Optimizer.VL.Properties.NonEmpty+import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Properties.VectorType++-- FIXME this is (almost) identical to its X100 counterpart -> merge+inferWorker :: NodeMap VL -> VL -> AlgNode -> NodeMap BottomUpProps -> BottomUpProps+inferWorker d op node pm =+    case op of+         TerOp vl c1 c2 c3 ->+           let c1Props = lookupUnsafe pm "no children properties" c1+               c2Props = lookupUnsafe pm "no children properties" c2+               c3Props = lookupUnsafe pm "no children properties" c3+           in checkError d node [c1Props, c2Props, c3Props] pm $ inferTerOp vl c1Props c2Props c3Props+         BinOp vl c1 c2 ->+           let c1Props = lookupUnsafe pm "no children properties" c1+               c2Props = lookupUnsafe pm "no children properties" c2+           in checkError d node [c1Props, c2Props] pm $ inferBinOp vl c1Props c2Props+         UnOp vl c ->+           let cProps = lookupUnsafe pm "no children properties" c+           in checkError d node [cProps] pm $ inferUnOp vl cProps+         NullaryOp vl -> checkError d node [] pm $ inferNullOp vl++checkError :: NodeMap VL -> AlgNode -> [BottomUpProps] -> NodeMap BottomUpProps -> Either String BottomUpProps -> BottomUpProps+checkError d n childProps propMap (Left msg) = +    let childPropsMsg = concatMap ((++) "\n" . show) childProps+        completeMsg   = printf "Inference failed at node %d\n%s\n%s\n%s\n%s" n msg childPropsMsg (show propMap) (show d)+    in error completeMsg+checkError _ _ _ _ (Right props) = props++inferNullOp :: NullOp -> Either String BottomUpProps+inferNullOp op = do+  opEmpty    <- inferEmptyNullOp op+  opNonEmpty <- inferNonEmptyNullOp op+  opConst    <- inferConstVecNullOp op+  opType     <- inferVectorTypeNullOp op+  opCard     <- inferCardOneNullOp op+  return $ BUProps { emptyProp = opEmpty+                   , nonEmptyProp = opNonEmpty+                   , constProp = opConst+                   , card1Prop = opCard+                   , vectorTypeProp = opType }++inferUnOp :: UnOp -> BottomUpProps -> Either String BottomUpProps+inferUnOp op cProps = do+  opEmpty    <- inferEmptyUnOp (emptyProp cProps) op+  opNonEmpty <- inferNonEmptyUnOp (nonEmptyProp cProps) op+  opType     <- inferVectorTypeUnOp (vectorTypeProp cProps) op+  opConst    <- inferConstVecUnOp (constProp cProps) op+  opCard     <- inferCardOneUnOp (card1Prop cProps) op+  return $ BUProps { emptyProp = opEmpty+                   , nonEmptyProp = opNonEmpty+                   , constProp = opConst+                   , card1Prop = opCard+                   , vectorTypeProp = opType }++inferBinOp :: BinOp -> BottomUpProps -> BottomUpProps -> Either String BottomUpProps+inferBinOp op c1Props c2Props = do+  opEmpty    <- inferEmptyBinOp (emptyProp c1Props) (emptyProp c2Props) op+  opNonEmpty <- inferNonEmptyBinOp (nonEmptyProp c1Props) (nonEmptyProp c2Props) op+  opType     <- inferVectorTypeBinOp (vectorTypeProp c1Props) (vectorTypeProp c2Props) op+  opConst    <- inferConstVecBinOp (constProp c1Props) (constProp c2Props) op+  opCard     <- inferCardOneBinOp (card1Prop c1Props) (card1Prop c2Props) op+  return $ BUProps { emptyProp = opEmpty+                   , nonEmptyProp = opNonEmpty+                   , constProp = opConst+                   , card1Prop = opCard+                   , vectorTypeProp = opType }++inferTerOp :: TerOp+           -> BottomUpProps+           -> BottomUpProps+           -> BottomUpProps+           -> Either String BottomUpProps+inferTerOp op c1Props c2Props c3Props = do+  opEmpty    <- inferEmptyTerOp (emptyProp c1Props) (emptyProp c2Props) (emptyProp c3Props) op+  opNonEmpty <- inferNonEmptyTerOp (nonEmptyProp c1Props) (nonEmptyProp c2Props) (nonEmptyProp c3Props) op+  opType     <- inferVectorTypeTerOp (vectorTypeProp c1Props) (vectorTypeProp c2Props) (vectorTypeProp c3Props) op+  opConst    <- inferConstVecTerOp (constProp c1Props) (constProp c2Props) (constProp c3Props) op+  opCard     <- inferCardOneTerOp (card1Prop c1Props) (card1Prop c2Props) (card1Prop c3Props) op+  return $ BUProps { emptyProp = opEmpty+                   , nonEmptyProp = opNonEmpty+                   , constProp = opConst+                   , card1Prop = opCard+                   , vectorTypeProp = opType }++inferBottomUpProperties :: AlgebraDag VL -> NodeMap BottomUpProps+inferBottomUpProperties dag = inferBottomUpGeneral inferWorker dag
+ src/Database/DSH/Optimizer/VL/Properties/Card.hs view
@@ -0,0 +1,102 @@+-- FIXME complete rules+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Properties.Card where++import Control.Applicative++import Database.DSH.VL.Lang++import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Properties.Common++unp :: Show a => VectorProp a -> Either String a+unp = unpack "Properties.Card"++inferCardOneNullOp :: NullOp -> Either String (VectorProp Bool)+inferCardOneNullOp op =+  case op of+    SingletonDescr   -> Right $ VProp True+    Lit (_, _, rows) -> Right $ VProp $ length rows == 1+    TableRef _       -> Right $ VProp False++inferCardOneUnOp :: VectorProp Bool -> UnOp -> Either String (VectorProp Bool)+inferCardOneUnOp c op = +  case op of+    UniqueS -> Right c+    Aggr _ -> Right $ VProp True+    AggrNonEmpty _ -> Right $ VProp True+    WinFun _ -> Right c+    UnboxRename -> Right c+    Segment -> Right c+    Unsegment -> Right c+    Project _  -> Right c+    Reverse -> unp c >>= (\uc -> return $ VPropPair uc uc)+    ReverseS -> unp c >>= (\uc -> return $ VPropPair uc uc)+    SelectPos1{}  -> Right $ VPropTriple False False False+    SelectPos1S{} -> Right $ VPropTriple False False False+    Select _ -> Right $ VPropPair False False+    SortS _ -> unp c >>= (\uc -> return $ VPropPair uc uc)+    GroupS _ -> unp c >>= (\uc -> return $ VPropTriple uc uc uc)+    R1 -> +      case c of+        VProp _           -> Left "Properties.Card: not a pair/triple"+        VPropPair b _     -> Right $ VProp b+        VPropTriple b _ _ -> Right $ VProp b+    R2 ->+      case c of+        VProp _           -> Left "Properties.Card: not a pair/triple"+        VPropPair _ b     -> Right $ VProp b+        VPropTriple _ b _ -> Right $ VProp b+    R3 ->+      case c of+        VPropTriple _ _ b -> Right $ VProp b+        _                 -> Left "Properties.Card: not a triple"+    GroupAggr ([], _) -> Right $ VProp True+    GroupAggr (_, _)  -> Right c+    Number -> Right c+    NumberS -> Right c+    Reshape _ -> unp c >>= (\uc -> return $ VPropPair uc uc)+    ReshapeS _ -> unp c >>= (\uc -> return $ VPropPair uc uc)+    Transpose -> unp c >>= (\uc -> return $ VPropPair uc uc)+    AggrNonEmptyS _ -> return $ VProp False+    ++inferCardOneBinOp :: VectorProp Bool -> VectorProp Bool -> BinOp -> Either String (VectorProp Bool)+inferCardOneBinOp c1 c2 op =+  case op of+    AggrS _ -> return $ VProp False+    NestProduct -> return $ VPropTriple False False False+    DistLift -> return $ VPropPair False False+    PropRename -> return $ VProp False+    PropFilter -> return $ VPropPair False False+    PropReorder -> return $ VPropPair False False+    UnboxNested -> return $ VPropPair False False+    UnboxScalar -> return $ VProp False+    -- FIXME more precisely: empty(left) and card1(right) or card1(left) and empty(right)+    Append -> Right $ VPropTriple False False False+    AppendS -> Right $ VPropTriple False False False+    SelectPos _ -> return $ VPropTriple False False False+    SelectPosS _ -> return $ VPropTriple False False False+    Zip -> VProp <$> ((||) <$> unp c1 <*> unp c2)+    Align -> VProp <$> ((||) <$> unp c1 <*> unp c2)+    CartProduct -> return $ VPropTriple False False False+    CartProductS -> return $ VPropTriple False False False+    NestProductS -> return $ VPropTriple False False False+    ThetaJoin _ -> return $ VPropTriple False False False+    NestJoin _ -> return $ VPropTriple False False False+    ThetaJoinS _ -> return $ VPropTriple False False False+    NestJoinS _ -> return $ VPropTriple False False False+    SemiJoin _ -> return $ VPropPair False False+    SemiJoinS _ -> return $ VPropPair False False+    AntiJoin _ -> return $ VPropPair False False+    AntiJoinS _ -> return $ VPropPair False False+    TransposeS -> return $ VPropPair False False+    ZipS -> do+      c <- (||) <$> unp c1 <*> unp c2+      return $ VPropTriple c c c+      +inferCardOneTerOp :: VectorProp Bool -> VectorProp Bool -> VectorProp Bool -> TerOp -> Either String (VectorProp Bool)+inferCardOneTerOp _ _ _ op =+  case op of+    Combine -> return $ VPropTriple False False False
+ src/Database/DSH/Optimizer/VL/Properties/Common.hs view
@@ -0,0 +1,19 @@+module Database.DSH.Optimizer.VL.Properties.Common where++import Control.Monad++import Database.DSH.Optimizer.VL.Properties.Types++unpack :: Show a => String -> VectorProp a -> Either String a+unpack _ (VProp b)  = Right b+unpack moduleName p = Left $ "no single vector in " ++ moduleName ++ " " ++ (show p)++mapUnpack :: Show a => String +             -> VectorProp a+             -> VectorProp a+             -> (a -> a -> VectorProp a) +             -> Either String (VectorProp a)+mapUnpack moduleName e1 e2 f = let ue1 = unpack moduleName e1+                                   ue2 = unpack moduleName e2+                               in liftM2 f ue1 ue2+                                  
+ src/Database/DSH/Optimizer/VL/Properties/Const.hs view
@@ -0,0 +1,492 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Properties.Const+    ( inferConstVecNullOp+    , inferConstVecUnOp+    , inferConstVecBinOp+    , inferConstVecTerOp+    ) where++import           Control.Monad+import           Data.List+import qualified Data.List.NonEmpty                          as N+import           Data.Maybe++import Database.DSH.Impossible+import           Database.DSH.Optimizer.VL.Properties.Common+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.VL.Lang+import           Database.DSH.Common.Lang++unp :: Show a => VectorProp a -> Either String a+unp = unpack "Properties.Const"++fromDBV :: ConstVec -> Either String (ConstDescr, [ConstPayload])+fromDBV (DBVConst d ps)   = Right (d, ps)+fromDBV x                 = Left $ "Properties.Const fromDBV " ++ (show x)++fromRVec :: ConstVec -> Either String (SourceConstDescr, TargetConstDescr)+fromRVec (RenameVecConst s t) = Right (s, t)+fromRVec x                    = Left ("Properties.Const fromRVec " ++ (show x))++fromPVec :: ConstVec -> Either String (SourceConstDescr, TargetConstDescr)+fromPVec (PropVecConst s t)  = Right (s, t)+fromPVec _                   = Left "Properties.Const fromPVec"++--------------------------------------------------------------------------------+-- Evaluation of constant expressions++-- FIXME finish remaining cases, only integer numeric operations so+-- far.++mkEnv :: [ConstPayload] -> [(DBCol, VLVal)]+mkEnv constCols = mapMaybe envEntry $ zip [1..] constCols+  where+    envEntry :: (DBCol, ConstPayload) -> Maybe (DBCol, VLVal)+    envEntry (_, NonConstPL) = mzero+    envEntry (c, ConstPL v)  = return (c, v)++evalNumOp :: BinNumOp -> Int -> Int -> Int+evalNumOp op v1 v2 =+    case op of+        Add -> v1 + v2+        Sub -> v1 - v2+        Div -> v1 `div` v2+        Mul -> v1 * v2+        Mod -> v1 `mod` v2++evalBinOp :: ScalarBinOp -> VLVal -> VLVal -> Maybe VLVal+evalBinOp op v1 v2 =+    case (v1, v2) of+        (VLInt i1, VLInt i2)       ->+            case op of+                SBNumOp nop  -> return $ VLInt $ evalNumOp nop i1 i2+                SBRelOp _    -> mzero+                SBBoolOp _   -> $impossible+                SBStringOp _ -> $impossible+                +        (VLBool _, VLBool _)     ->+            case op of+                SBBoolOp _   -> mzero+                SBRelOp _    -> mzero+                SBNumOp _    -> $impossible+                SBStringOp _ -> $impossible+        (VLString _, VLString _) ->+            case op of+                SBRelOp _    -> mzero+                SBStringOp _ -> mzero+                SBBoolOp _   -> $impossible+                SBNumOp _    -> $impossible+        (VLDouble _, VLDouble _) ->+            case op of+                SBRelOp _    -> mzero+                SBNumOp _    -> mzero+                SBBoolOp _   -> $impossible+                SBStringOp _ -> $impossible+        (VLUnit, VLUnit)           -> mzero+        _                          -> $impossible++evalUnOp :: ScalarUnOp -> VLVal -> Maybe VLVal+evalUnOp _ _ = mzero++constExpr :: [ConstPayload] -> Expr -> Either String ConstPayload+constExpr constCols expr =+    case eval expr of+        Just v  -> return $ ConstPL v+        Nothing -> return NonConstPL++  where+    env :: [(DBCol, VLVal)]+    env = mkEnv constCols++    eval :: Expr -> Maybe VLVal+    eval (Constant v)      = return v+    eval (Column i)        = lookup i env+    eval (BinApp op e1 e2) = do+        v1 <- eval e1+        v2 <- eval e2+        evalBinOp op v1 v2+    eval (UnApp op e1)     = do+        v <- eval e1+        evalUnOp op v+    eval (If c t e)        = do+        cv <- eval c+        case cv of+            VLBool True  -> eval t+            VLBool False -> eval e+            _            -> mzero++--------------------------------------------------------------------------------+-- Stuff++nonConstPVec :: ConstVec+nonConstPVec = PropVecConst (SC NonConstDescr) (TC NonConstDescr)++nonConstRVec :: ConstVec+nonConstRVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++inferConstVecNullOp :: NullOp -> Either String (VectorProp ConstVec)+inferConstVecNullOp op =+  case op of+    SingletonDescr                    -> return $ VProp $ DBVConst (ConstDescr 1) []+    -- do not include the first two columns in the payload columns because they represent descr and pos.+    Lit (_, colTypes, rows)      ->+      if null rows+      then return $ VProp $ DBVConst NonConstDescr $ map (const NonConstPL) colTypes+      else return $ VProp $ DBVConst (ConstDescr 1) constCols+        where constCols       = map toConstPayload $ drop 2 $ transpose rows++              toConstPayload col@(c : _) = if all (c ==) col+                                           then ConstPL c+                                           else NonConstPL+              toConstPayload []          = NonConstPL++    TableRef              (_, cols, _)    -> return $ VProp $ DBVConst (ConstDescr 1) $ map (const NonConstPL) cols++inferConstVecUnOp :: (VectorProp ConstVec) -> UnOp -> Either String (VectorProp ConstVec)+inferConstVecUnOp c op =+  case op of+    WinFun _ -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VProp $ DBVConst d (cols ++ [NonConstPL])++    UniqueS -> return c++    Aggr _ -> do+      return $ VProp $ DBVConst NonConstDescr [NonConstPL]++    AggrNonEmpty _ -> do+      return $ VProp $ DBVConst (ConstDescr 1) [NonConstPL]++    UnboxRename -> do+      (d, _) <- unp c >>= fromDBV+      return $ VProp $ RenameVecConst (SC NonConstDescr) (TC d)++    Segment -> do+      (_, constCols) <- unp c >>= fromDBV+      return $ VProp $ DBVConst NonConstDescr constCols++    Unsegment -> do+      (_, constCols) <- unp c >>= fromDBV+      return $ VProp $ DBVConst NonConstDescr constCols++    SelectPos1{}  -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VPropTriple (DBVConst d cols) +                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))+                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    SelectPos1S{} -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VPropTriple (DBVConst d cols) +                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))+                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    Reverse -> do+      (d, cs) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst d cs) (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    ReverseS -> do+      (d, cs) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst d cs) (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    Project projExprs   -> do+      (constDescr, constCols) <- unp c >>= fromDBV+      constCols'              <- mapM (constExpr constCols) projExprs+      return $ VProp $ DBVConst constDescr constCols'++    Select _       -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst d cols) (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    GroupAggr (g, as) -> do+      (d, _) <- unp c >>= fromDBV+      return $ VProp $ DBVConst d (map (const NonConstPL) [ 1 .. (length g) + (N.length as) ])++    Number -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VProp $ DBVConst d (cols ++ [NonConstPL])++    NumberS -> do+      (d, cols) <- unp c >>= fromDBV+      return $ VProp $ DBVConst d (cols ++ [NonConstPL])++    SortS _ -> do+      (d, cs) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst d cs) (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    GroupS es -> do+      (d, cs) <- unp c >>= fromDBV+      return $ VPropTriple (DBVConst d (map (const NonConstPL) es))+                           (DBVConst NonConstDescr (map (const NonConstPL) cs))+                           (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    Transpose -> do+      (_, cols) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst NonConstDescr []) (DBVConst NonConstDescr cols)+    Reshape _ -> do+      (_, cols) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst NonConstDescr []) (DBVConst NonConstDescr cols)+    ReshapeS _ -> do+      (_, cols) <- unp c >>= fromDBV+      return $ VPropPair (DBVConst NonConstDescr []) (DBVConst NonConstDescr cols)++    AggrNonEmptyS _ -> do+      return $ VProp $ DBVConst NonConstDescr [NonConstPL]++    R1 ->+      case c of+        VProp _           -> Left "Properties.Const: not a pair/triple"+        VPropPair b _     -> Right $ VProp b+        VPropTriple b _ _ -> Right $ VProp b+    R2 ->+      case c of+        VProp _           -> Left "Properties.Const: not a pair/triple"+        VPropPair _ b     -> Right $ VProp b+        VPropTriple _ b _ -> Right $ VProp b+    R3 ->+      case c of+        VPropTriple _ _ b -> Right $ VProp b+        _                 -> Left "Properties.Const: not a triple"++inferConstVecBinOp :: (VectorProp ConstVec) -> (VectorProp ConstVec) -> BinOp -> Either String (VectorProp ConstVec)+inferConstVecBinOp c1 c2 op =+  case op of+    -- FIXME use cardinality property to infer the length if possible+    -- FIXME handle special cases: empty input, cardinality 1 and const input, ...+    AggrS _ -> do+      return $ VProp $ DBVConst NonConstDescr [NonConstPL]++    DistLift -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (d, cols2) <- unp c2 >>= fromDBV+      return $ VPropPair (DBVConst d (cols1 ++ cols2)) (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    PropRename -> do+      (_, cols) <- unp c2 >>= fromDBV+      (SC _, TC target) <- unp c1 >>= fromRVec++      return $ VProp $ DBVConst target cols++    PropFilter -> do+      (_, cols) <- unp c2 >>= fromDBV+      (SC _, TC target) <- unp c1 >>= fromRVec++      return $ VPropPair (DBVConst target cols) (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    PropReorder -> do+      (_, cols) <- unp c2 >>= fromDBV+      (SC _, TC target) <- unp c1 >>= fromPVec++      return $ VPropPair (DBVConst target cols) (PropVecConst (SC NonConstDescr) (TC NonConstDescr))++    UnboxNested -> do+      (_, TC descr) <- unp c1 >>= fromRVec+      (_, cols)     <- unp c2 >>= fromDBV++      return $ VPropPair (DBVConst descr cols) (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    UnboxScalar -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (_, cols2)  <- unp c2 >>= fromDBV+      return $ VProp $ DBVConst d1 (cols1 ++ cols2)++    Append -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (d2, cols2) <- unp c2 >>= fromDBV++      let constCols = map sameConst $ zip cols1 cols2++          sameConst ((ConstPL v1), (ConstPL v2)) | v1 == v2 = ConstPL v1+          sameConst (_, _)                                  = NonConstPL++          d = case (d1, d2) of+            (ConstDescr n1, ConstDescr n2) | n1 == n2 -> ConstDescr n1+            _                                         -> NonConstDescr++      return $ VPropTriple (DBVConst d constCols) nonConstRVec nonConstRVec++    AppendS -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (d2, cols2) <- unp c2 >>= fromDBV++      let constCols = map sameConst $ zip cols1 cols2++          sameConst ((ConstPL v1), (ConstPL v2)) | v1 == v2 = ConstPL v1+          sameConst (_, _)                                  = NonConstPL++          d = case (d1, d2) of+            (ConstDescr n1, ConstDescr n2) | n1 == n2 -> ConstDescr n1+            _                                         -> NonConstDescr++      return $ VPropTriple (DBVConst d constCols) nonConstRVec nonConstRVec++    SelectPos _ -> do+      (d1, cols1) <- unp c1 >>= fromDBV++      return $ VPropTriple (DBVConst d1 cols1) +                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))+                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    SelectPosS _ -> do+      (d1, cols1) <- unp c1 >>= fromDBV++      return $ VPropTriple (DBVConst d1 cols1) +                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))+                           (RenameVecConst (SC NonConstDescr) (TC NonConstDescr))++    Align -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (_, cols2)  <- unp c2 >>= fromDBV++      let cols = cols1 ++ cols2++      return $ VProp $ DBVConst d1 cols++    Zip -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (_, cols2)  <- unp c2 >>= fromDBV++      let cols = cols1 ++ cols2++      return $ VProp $ DBVConst d1 cols++    ZipS -> do+      (d1, cols1) <- unp c1 >>= fromDBV+      (_, cols2)  <- unp c2 >>= fromDBV++      let cols = cols1 ++ cols2+          renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      return $ VPropTriple (DBVConst d1 cols) renameVec renameVec++    CartProduct -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      -- FIXME descr = 1 is almost certainly not correct+      return $ VPropTriple (DBVConst (ConstDescr 1) constCols) nonConstPVec nonConstPVec++    CartProductS -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    NestProductS -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    NestJoin _ -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    NestProduct -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    ThetaJoin _ -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst (ConstDescr 1) constCols) nonConstPVec nonConstPVec++    ThetaJoinS _ -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    NestJoinS _ -> do+      (_, cols1) <- unp c1 >>= fromDBV+      (_, cols2) <- unp c2 >>= fromDBV++      let constCols = cols1 ++ cols2++      -- FIXME check propVec components for correctness/precision+      return $ VPropTriple (DBVConst NonConstDescr constCols) nonConstPVec nonConstPVec++    SemiJoin _ -> do+      (_, cols1) <- unp c1 >>= fromDBV++      -- FIXME This is propably too pessimistic for the source descriptor+      let renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      -- FIXME This is propably too pessimistic for the descr+      return $ VPropPair (DBVConst NonConstDescr cols1) renameVec++    SemiJoinS _ -> do+      (_, cols1) <- unp c1 >>= fromDBV++      -- FIXME This is propably too pessimistic for the source descriptor+      let renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      -- FIXME This is propably too pessimistic for the descr+      return $ VPropPair (DBVConst NonConstDescr cols1) renameVec++    AntiJoin _ -> do+      (_, cols1) <- unp c1 >>= fromDBV++      -- FIXME This is propably too pessimistic for the source descriptor+      let renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      -- FIXME This is propably too pessimistic for the descr+      return $ VPropPair (DBVConst NonConstDescr cols1) renameVec++    AntiJoinS _ -> do+      (_, cols1) <- unp c1 >>= fromDBV++      -- FIXME This is propably too pessimistic for the source descriptor+      let renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      -- FIXME This is propably too pessimistic for the descr+      return $ VPropPair (DBVConst NonConstDescr cols1) renameVec++    TransposeS -> do+      (_, cols2) <- unp c2 >>= fromDBV+      return $ VPropPair (DBVConst NonConstDescr []) (DBVConst NonConstDescr cols2)++inferConstVecTerOp :: (VectorProp ConstVec) -> (VectorProp ConstVec) -> (VectorProp ConstVec) -> TerOp -> Either String (VectorProp ConstVec)+inferConstVecTerOp c1 c2 c3 op =+  case op of+    Combine -> do+      (d1, _) <- unp c1 >>= fromDBV+      (_, cols2)  <- unp c2 >>= fromDBV+      (_, cols3)  <- unp c3 >>= fromDBV++      let constCols = map sameConst $ zip cols2 cols3++          sameConst ((ConstPL v1), (ConstPL v2)) | v1 == v2 = ConstPL v1+          sameConst (_, _)                                  = NonConstPL++          renameVec = RenameVecConst (SC NonConstDescr) (TC NonConstDescr)++      return $ VPropTriple (DBVConst d1 constCols) renameVec renameVec+
+ src/Database/DSH/Optimizer/VL/Properties/Empty.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Properties.Empty where++import Control.Monad+  +import Database.DSH.VL.Lang++import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Properties.Common+  +unp :: Show a => VectorProp a -> Either String a+unp = unpack "Properties.Empty"+                   +mapUnp :: Show a => VectorProp a+          -> VectorProp a +          -> (a -> a -> VectorProp a) +          -> Either String (VectorProp a)+mapUnp = mapUnpack "Properties.Empty"  ++inferEmptyNullOp :: NullOp -> Either String (VectorProp Bool)+inferEmptyNullOp op =+  case op of+    SingletonDescr     -> Right $ VProp False+    Lit (_, _, [])     -> Right $ VProp True+    Lit (_, _, _)      -> Right $ VProp False+    TableRef (_, _, _) -> Right $ VProp False+    +inferEmptyUnOp :: VectorProp Bool -> UnOp -> Either String (VectorProp Bool)+inferEmptyUnOp e op =+  case op of+    WinFun _       -> Right e+    UniqueS         -> Right e+    Aggr _          -> Right $ VProp False+    AggrNonEmpty _  -> Right $ VProp False+    UnboxRename     -> Right e+    Segment         -> Right e+    Unsegment       -> Right e+    Reverse         -> let ue = unp e in liftM2 VPropPair ue ue+    ReverseS        -> let ue = unp e in liftM2 VPropPair ue ue+    Project _       -> Right e+    Select _        -> let ue = unp e in liftM2 VPropPair ue ue+    SortS _         -> let ue = unp e in liftM2 VPropPair ue ue+    GroupS _        -> let ue = unp e in liftM3 VPropTriple ue ue ue++    -- FIXME this documents the current implementation behaviour, not+    -- what _should_ happen!+    ReshapeS _ -> let ue = unp e in liftM2 VPropPair ue ue+    Reshape _ -> let ue = unp e in liftM2 VPropPair ue ue+    Transpose -> let ue = unp e in liftM2 VPropPair ue ue++    SelectPos1{} -> let ue = unp e in liftM3 VPropTriple ue ue ue+    SelectPos1S{} -> let ue = unp e in liftM3 VPropTriple ue ue ue+    -- FIXME think about it: what happens if we feed an empty vector into the aggr operator?+    GroupAggr (_, _) -> Right $ VProp False+    Number -> Right e+    NumberS -> Right e+    AggrNonEmptyS _ -> return $ VProp False+  +    R1 -> +      case e of+        VProp _           -> Left "Properties.Empty: not a pair/triple"+        VPropPair b _     -> Right $ VProp b+        VPropTriple b _ _ -> Right $ VProp b+    R2 ->+      case e of+        VProp _           -> Left "Properties.Empty: not a pair/triple"+        VPropPair _ b     -> Right $ VProp b+        VPropTriple _ b _ -> Right $ VProp b+    R3 ->+      case e of+        VPropTriple _ _ b -> Right $ VProp b+        p                 -> Left ("Properties.Empty: not a triple" ++ show p)++    +inferEmptyBinOp :: VectorProp Bool -> VectorProp Bool -> BinOp -> Either String (VectorProp Bool)+inferEmptyBinOp e1 e2 op =+  case op of+    DistLift -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 || ue2) (ue1 || ue2))+    PropRename -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 || ue2))+    PropFilter -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 || ue2) (ue1 || ue2))+    PropReorder -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 || ue2) (ue1 || ue2))+    UnboxNested -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 || ue2) (ue1 || ue2))+    UnboxScalar -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 || ue2))+    Append -> mapUnp e1 e2 (\ue1 ue2 -> VPropTriple (ue1 && ue2) ue1 ue2)+    AppendS -> mapUnp e1 e2 (\ue1 ue2 -> VPropTriple (ue1 && ue2) ue1 ue2)+    AggrS _ -> return $ VProp False+    SelectPos _ -> mapUnp e1 e2 (\ue1 ue2 -> let b = ue1 || ue2 in VPropTriple b b b)+    SelectPosS _ -> mapUnp e1 e2 (\ue1 ue2 -> let b = ue1 || ue2 in VPropTriple b b b)+    Zip -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 || ue2))+    Align -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 || ue2))+    ZipS -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    CartProduct -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    CartProductS -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    NestProductS -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    ThetaJoin _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    NestJoin _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    NestProduct -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    ThetaJoinS _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    NestJoinS _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 || ue2))+    SemiJoin _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropPair p p) (ue1 || ue2))+    SemiJoinS _ -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropPair p p) (ue1 || ue2))+    AntiJoin _ -> mapUnp e1 e2 (\ue1 _ -> (\p -> VPropPair p p) ue1)+    AntiJoinS _ -> mapUnp e1 e2 (\ue1 _ -> (\p -> VPropPair p p) ue1)+    -- FIXME This documents the current behaviour of the algebraic+    -- implementations, not what _should_ happen!+    TransposeS -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropPair p p) (ue1 || ue2))+    +inferEmptyTerOp :: VectorProp Bool -> VectorProp Bool -> VectorProp Bool -> TerOp -> Either String (VectorProp Bool)+inferEmptyTerOp _ e2 e3 op =+  case op of+    Combine -> let ue2 = unp e2+                   ue3 = unp e3+               in liftM3 VPropTriple (liftM2 (&&) ue2 ue3) ue2 ue3+    
+ src/Database/DSH/Optimizer/VL/Properties/NonEmpty.hs view
@@ -0,0 +1,139 @@+{-# LANGUAGE TemplateHaskell #-}++{-++FIXME semantics need to be clarified.++For an inner vector (one with multiple segments), True means that all+segments contained in the outer vector will be present. This is+particularly true for the output of a grouping operator.++For a non-segmented vector, it is true if we can (derived from a base+tables non-empty property) statically assert that a vector will not be+empty.++This is all rather unclear. Currently, the main purpose of this+property is to avoid the special treatment of empty segments in+segmented aggregates.++-}++module Database.DSH.Optimizer.VL.Properties.NonEmpty where++import Control.Monad+  +import Database.DSH.Common.Lang(nonEmptyHint, Emptiness(..))+import Database.DSH.VL.Lang++import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Properties.Common+  +unp :: Show a => VectorProp a -> Either String a+unp = unpack "Properties.NonEmpty"+                   +mapUnp :: Show a => VectorProp a+          -> VectorProp a +          -> (a -> a -> VectorProp a) +          -> Either String (VectorProp a)+mapUnp = mapUnpack "Properties.NonEmpty"  ++inferNonEmptyNullOp :: NullOp -> Either String (VectorProp Bool)+inferNonEmptyNullOp op =+  case op of+    SingletonDescr            -> Right $ VProp False+    Lit (NonEmpty, _, _)      -> Right $ VProp True+    Lit (PossiblyEmpty, _, _) -> Right $ VProp False+    TableRef (_, _, hs)       -> return $ VProp $ (nonEmptyHint hs) == NonEmpty+    +inferNonEmptyUnOp :: VectorProp Bool -> UnOp -> Either String (VectorProp Bool)+inferNonEmptyUnOp e op =+  case op of+    WinFun _       -> Right e+    UniqueS         -> Right e+    Aggr _          -> Right $ VProp True+    AggrNonEmpty _  -> Right $ VProp True+    UnboxRename     -> Right e+    Segment         -> Right e+    Unsegment       -> Right e+    Reverse         -> let ue = unp e in liftM2 VPropPair ue ue+    ReverseS        -> let ue = unp e in liftM2 VPropPair ue ue+    Project _       -> Right e+    Select _        -> Right $ VPropPair False False+    SortS _         -> let ue = unp e in liftM2 VPropPair ue ue+    -- If the input is not completely empty (that is, segments exist),+    -- grouping leads to a nested vector in which every inner segment+    -- is not empty.+    GroupS _        -> let ue = unp e in liftM3 VPropTriple ue (return True) ue++    -- FIXME this documents the current implementation behaviour, not+    -- what _should_ happen!+    ReshapeS _ -> let ue = unp e in liftM2 VPropPair ue ue+    Reshape _ -> let ue = unp e in liftM2 VPropPair ue ue+    Transpose -> let ue = unp e in liftM2 VPropPair ue ue++    SelectPos1{} -> return $ VPropTriple False False False+    SelectPos1S{} -> return $ VPropTriple False False False+    -- FIXME think about it: what happens if we feed an empty vector into the aggr operator?+    GroupAggr (_, _) -> Right e+    Number -> Right e+    NumberS -> Right e+    AggrNonEmptyS _ -> return $ VProp True+  +    R1 -> +      case e of+        VProp _           -> Left "Properties.NonEmpty: not a pair/triple"+        VPropPair b _     -> Right $ VProp b+        VPropTriple b _ _ -> Right $ VProp b+    R2 ->+      case e of+        VProp _           -> Left "Properties.NonEmpty: not a pair/triple"+        VPropPair _ b     -> Right $ VProp b+        VPropTriple _ b _ -> Right $ VProp b+    R3 ->+      case e of+        VPropTriple _ _ b -> Right $ VProp b+        _                 -> Left "Properties.NonEmpty: not a triple"++    +inferNonEmptyBinOp :: VectorProp Bool -> VectorProp Bool -> BinOp -> Either String (VectorProp Bool)+inferNonEmptyBinOp e1 e2 op =+  case op of+    DistLift        -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 && ue2) (ue1 && ue2))+    PropRename      -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 && ue2))+    PropFilter      -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 && ue2) (ue1 && ue2))+    PropReorder     -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 && ue2) (ue1 && ue2))+    UnboxNested     -> mapUnp e1 e2 (\ue1 ue2 -> VPropPair (ue1 && ue2) (ue1 && ue2))+    UnboxScalar     -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 && ue2))+    Append          -> mapUnp e1 e2 (\ue1 ue2 -> VPropTriple (ue1 || ue2) ue1 ue2)+    AppendS         -> mapUnp e1 e2 (\ue1 ue2 -> VPropTriple (ue1 || ue2) ue1 ue2)+    AggrS _         -> return $ VProp True+    SelectPos _     -> mapUnp e1 e2 (\ue1 ue2 -> let b = ue1 && ue2 in VPropTriple b b b)+    SelectPosS _    -> mapUnp e1 e2 (\ue1 ue2 -> let b = ue1 && ue2 in VPropTriple b b b)+    Zip             -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 && ue2))+    Align           -> mapUnp e1 e2 (\ue1 ue2 -> VProp (ue1 && ue2))+    ZipS            -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 && ue2))+    CartProduct     -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 && ue2))+    CartProductS    -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 && ue2))+    NestProductS    -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropTriple p p p) (ue1 && ue2))+    ThetaJoin _     -> return $ VPropTriple False False False+    NestJoin _      -> return $ VPropTriple False False False+    NestProduct     -> return $ VPropTriple False False False+    ThetaJoinS _    -> return $ VPropTriple False False False+    NestJoinS _     -> return $ VPropTriple False False False+    SemiJoin _      -> return $ VPropPair False False+    SemiJoinS _     -> return $ VPropPair False False+    AntiJoin _      -> return $ VPropPair False False+    AntiJoinS _     -> return $ VPropPair False False+    -- FIXME This documents the current behaviour of the algebraic+    -- implementations, not what _should_ happen!+    TransposeS      -> mapUnp e1 e2 (\ue1 ue2 -> (\p -> VPropPair p p) (ue1 || ue2))+    +inferNonEmptyTerOp :: VectorProp Bool -> VectorProp Bool -> VectorProp Bool -> TerOp -> Either String (VectorProp Bool)+inferNonEmptyTerOp e1 e2 e3 op =+  case op of+    Combine -> do+        ue1 <- unp e1+        ue2 <- unp e2+        ue3 <- unp e3+        return $ VPropTriple ue1 ue2 ue3+    
+ src/Database/DSH/Optimizer/VL/Properties/ReqColumns.hs view
@@ -0,0 +1,418 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Properties.ReqColumns where++import           Control.Applicative+import qualified Data.List                                  as L+import qualified Data.List.NonEmpty                         as N++import           Database.DSH.Common.Lang+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.VL.Lang+++(∪) :: VectorProp ReqCols -> VectorProp ReqCols -> Either String (VectorProp ReqCols)+(∪) (VProp (Just cols1)) (VProp (Just cols2)) = return $ VProp $ Just $ cols1 `L.union` cols2+(∪) (VProp (Just cols1)) (VProp Nothing)      = return $ VProp $ Just $ cols1+(∪) (VProp Nothing)      (VProp (Just cols2)) = return $ VProp $ Just $ cols2+(∪) (VProp Nothing)      (VProp Nothing)      = return $ VProp $ Nothing+(∪) p1                   p2                   = Left $ "ReqColumns.union"+                                                       ++ " "+                                                       ++ (show p1)+                                                       ++ " "+                                                       ++ (show p2)++none :: VectorProp ReqCols+none = VProp $ Just []++one :: VectorProp ReqCols+one = VProp $ Just [1]++na :: VectorProp ReqCols+na = VProp Nothing++reqExprCols :: Expr -> [DBCol]+reqExprCols (BinApp _ e1 e2) = reqExprCols e1 `L.union` reqExprCols e2+reqExprCols (UnApp _ e)      = reqExprCols e+reqExprCols (Column col)     = [col]+reqExprCols (Constant _)     = []+reqExprCols (If c t e)       = reqExprCols c `L.union` reqExprCols t `L.union` reqExprCols e++reqLeftPredCols :: JoinPredicate Expr -> [DBCol]+reqLeftPredCols (JoinPred cs) = L.nub +                                $ concatMap (\(JoinConjunct le _ _) -> reqExprCols le) +                                $ N.toList cs++reqRightPredCols :: JoinPredicate Expr -> [DBCol]+reqRightPredCols (JoinPred cs) = L.nub +                                $ concatMap (\(JoinConjunct _ _ re) -> reqExprCols re) +                                $ N.toList cs++aggrReqCols :: AggrFun -> [DBCol]+aggrReqCols (AggrSum _ e) = reqExprCols e+aggrReqCols (AggrMin e)   = reqExprCols e+aggrReqCols (AggrMax e)   = reqExprCols e+aggrReqCols (AggrAvg e)   = reqExprCols e+aggrReqCols (AggrAll e)   = reqExprCols e+aggrReqCols (AggrAny e)   = reqExprCols e+aggrReqCols AggrCount     = []++winReqCols :: WinFun -> [DBCol]+winReqCols (WinSum e)        = reqExprCols e+winReqCols (WinMin e)        = reqExprCols e+winReqCols (WinMax e)        = reqExprCols e+winReqCols (WinAvg e)        = reqExprCols e+winReqCols (WinAll e)        = reqExprCols e+winReqCols (WinAny e)        = reqExprCols e+winReqCols (WinFirstValue e) = reqExprCols e+winReqCols WinCount          = []++fromProp :: Show a => VectorProp a -> Either String a+fromProp (VProp p) = return p+fromProp x         = fail $ "ReqColumns.fromProp " ++ (show x)++fromPropPair :: VectorProp a -> Either String (a, a)+fromPropPair (VPropPair x y) = return (x, y)+fromPropPair _               = fail "not a property pair"++fromPropTriple :: VectorProp a -> Either String (a, a, a)+fromPropTriple (VPropTriple x y z) = return (x, y, z)+fromPropTriple _                   = fail "not a property triple"++allCols :: BottomUpProps -> Either String (VectorProp ReqCols)+allCols props = do+    VProp (ValueVector w) <- return $ vectorTypeProp props+    return $ VProp $ Just [1 .. w]++-- | For operators that combine two value vectors in a product-like+-- manner (products, joins, zips, ...), map the columns that are+-- required from above to the respective input columns.+partitionCols :: BottomUpProps   -- ^ Available columns in the left input+              -> BottomUpProps   -- ^ Available columns in the right input+              -> ReqCols         -- ^ Columns required from above+              -> Either String (VectorProp ReqCols, VectorProp ReqCols)+partitionCols childBUProps1 childBUProps2 ownReqCols = do+    ValueVector w1 <- fromProp $ vectorTypeProp childBUProps1+    ValueVector w2 <- fromProp $ vectorTypeProp childBUProps2++    let cols = maybe [] id ownReqCols++    -- If both inputs are ValueVectors, map the required columns to+    -- the respective inputs+    let leftReqCols  = cols `L.intersect` [1 .. w1]+        rightReqCols = map (\c -> c - w1) $ cols `L.intersect` [(w1 + 1) .. (w1 + w2)]+    return (VProp $ Just leftReqCols, VProp $ Just rightReqCols)++-- | Infer required columns for unary operators+inferReqColumnsUnOp :: BottomUpProps          -- ^ Input properties+                    -> VectorProp ReqCols     -- ^ Columns required from the current node+                    -> VectorProp ReqCols     -- ^ Columns required from the input node+                    -> UnOp                   -- ^ Current operator+                    -> Either String (VectorProp ReqCols)+inferReqColumnsUnOp childBUProps ownReqColumns childReqColumns op =+    case op of+        WinFun (wfun, _) -> do+            cs <- (VProp $ Just $ winReqCols wfun)+                  ∪+                  childReqColumns+            cs ∪ ownReqColumns+        Transpose  -> do+            cols <- snd <$> fromPropPair ownReqColumns+            childReqColumns ∪ VProp cols++        Reshape _  -> do+            cols <- snd <$> fromPropPair ownReqColumns+            VProp cols ∪ childReqColumns++        ReshapeS _ -> do+            cols <- snd <$> fromPropPair ownReqColumns+            VProp cols ∪ childReqColumns++        UniqueS    -> ownReqColumns ∪ childReqColumns++        Aggr aggrFun -> (VProp $ Just $ aggrReqCols aggrFun)+                        ∪+                        childReqColumns++        AggrNonEmpty aggrFuns -> (VProp $ Just $ concatMap aggrReqCols (N.toList aggrFuns))+                                 ∪+                                 childReqColumns++        UnboxRename -> none ∪ childReqColumns++        Segment    -> ownReqColumns ∪ childReqColumns+        Unsegment  -> ownReqColumns ∪ childReqColumns++        -- Numbering operators add one column at the end. We have to+        -- determine the column index of the new column and remove it+        -- from the set of required columns+        Number     -> do+            ValueVector w <- fromProp $ vectorTypeProp childBUProps+            Just cols     <- fromProp ownReqColumns+            let cols'     = filter (/= w) cols+            VProp (Just cols') ∪ childReqColumns+        NumberS    -> do+            ValueVector w <- fromProp $ vectorTypeProp childBUProps+            (Just cols)   <- fromProp ownReqColumns+            let cols'     = filter (/= w) cols+            VProp (Just cols') ∪ childReqColumns++        Reverse    -> do+            cols <- fst <$> fromPropPair ownReqColumns+            VProp cols ∪ childReqColumns+        ReverseS   -> do+            cols <- fst <$> fromPropPair ownReqColumns+            VProp cols ∪ childReqColumns++        Project ps -> childReqColumns ∪ (VProp $ Just $ L.nub $ concatMap reqExprCols ps)++        Select e   -> do+            cols           <- fst <$> fromPropPair ownReqColumns+            ownReqColumns' <- (VProp cols) ∪ (VProp $ Just $ reqExprCols e)+            ownReqColumns' ∪ childReqColumns++        SelectPos1{}   -> do+            (cols, _, _) <- fromPropTriple ownReqColumns+            childReqColumns ∪ (VProp cols)++        SelectPos1S{}   -> do+            (cols, _, _) <- fromPropTriple ownReqColumns+            childReqColumns ∪ (VProp cols)++        -- We don't need to look at the columns required from above,+        -- because they can only be a subset of (gs ++ as).+        GroupAggr (gs, as) -> childReqColumns+                           ∪+                           (VProp $ Just $ L.nub $ concatMap reqExprCols gs+                                                   +++                                                   concatMap aggrReqCols (N.toList as))++        SortS exprs -> do+            cols <- fst <$> fromPropPair ownReqColumns+            ownReqColumns' <- VProp cols+                              ∪+                              (VProp $ Just $ L.nub $ concatMap reqExprCols exprs)+            childReqColumns ∪ ownReqColumns'++        GroupS exprs -> do+            (_, colsi, _) <- fromPropTriple ownReqColumns+            ownReqColumns' <- VProp colsi+                              ∪+                              (VProp $ Just $ L.nub $ concatMap reqExprCols exprs)+            childReqColumns ∪ ownReqColumns'++        AggrNonEmptyS aggrFuns -> do+          reqCols <- (VProp $ Just $ concatMap aggrReqCols (N.toList aggrFuns))+                      ∪+                      childReqColumns+          return reqCols++        R1               ->+            case childReqColumns of+                VProp _                       -> Left $ "ReqColumns.R1 " ++ (show childReqColumns)+                VPropPair cols1 cols2         -> do+                    cols1' <- fromProp =<< VProp cols1 ∪ ownReqColumns+                    return $ VPropPair cols1' cols2+                VPropTriple cols1 cols2 cols3 -> do+                    cols1' <- fromProp =<< VProp cols1 ∪ ownReqColumns+                    return $ VPropTriple cols1' cols2 cols3++        R2               ->+            case childReqColumns of+                VProp _                       -> fail "ReqColumns.R2"+                VPropPair cols1 cols2         -> do+                    cols2' <- fromProp =<< VProp cols2 ∪ ownReqColumns+                    return $ VPropPair cols1 cols2'+                VPropTriple cols1 cols2 cols3 -> do+                    cols2' <- fromProp =<< VProp cols2 ∪ ownReqColumns+                    return $ VPropTriple cols1 cols2' cols3++        R3               ->+            case childReqColumns of+                VProp _                       -> fail "ReqColumns.R3/1"+                VPropPair _ _                 -> fail "ReqColumns.R3/2"+                VPropTriple cols1 cols2 cols3 -> do+                    cols3' <- fromProp =<< VProp cols3 ∪ ownReqColumns+                    return $ VPropTriple cols1 cols2 cols3'+++-- | Infer required columns for binary operators+inferReqColumnsBinOp :: BottomUpProps+                     -> BottomUpProps+                     -> VectorProp ReqCols+                     -> VectorProp ReqCols+                     -> VectorProp ReqCols+                     -> BinOp+                     -> Either String (VectorProp ReqCols, VectorProp ReqCols)+inferReqColumnsBinOp childBUProps1 childBUProps2 ownReqColumns childReqColumns1 childReqColumns2 op =+  case op of+      AggrS aggrFun   -> do+          fromLeft  <- childReqColumns1 ∪ none+          fromRight <- (VProp $ Just $ aggrReqCols aggrFun)+                       ∪+                       childReqColumns2+          return (fromLeft, fromRight)++      DistLift -> do+          cols <- fst <$> fromPropPair ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      PropRename -> do+          fromRight <- childReqColumns2 ∪ ownReqColumns+          return (na, fromRight)++      PropFilter      -> do+          cols      <- fst <$> fromPropPair ownReqColumns+          fromRight <- childReqColumns2 ∪ VProp cols+          return (na, fromRight)++      PropReorder -> do+          cols      <- fst <$> fromPropPair ownReqColumns+          fromRight <- childReqColumns2 ∪ VProp cols+          return (na, fromRight)++      UnboxNested -> do+          cols      <- fst <$> fromPropPair ownReqColumns+          fromRight <- childReqColumns2 ∪ VProp cols+          return (na, fromRight)++      Append -> do+          (cols, _, _) <- fromPropTriple ownReqColumns+          fromLeft     <- (VProp cols) ∪ childReqColumns1+          fromRight    <- (VProp cols) ∪ childReqColumns2+          return (fromLeft, fromRight)++      AppendS -> do+          (cols, _, _) <- fromPropTriple ownReqColumns+          fromLeft     <- (VProp cols) ∪ childReqColumns1+          fromRight    <- (VProp cols) ∪ childReqColumns2+          return (fromLeft, fromRight)++      SelectPos _ -> do+          (cols, _, _) <- fromPropTriple ownReqColumns+          fromLeft     <- VProp cols ∪ childReqColumns1+          return (fromLeft, one)++      SelectPosS _ -> do+          (cols, _, _) <- fromPropTriple ownReqColumns+          fromLeft     <- VProp cols ∪ childReqColumns1+          return (fromLeft, one)++      Align -> do+          cols <- fromProp ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      Zip -> do+          cols <- fromProp ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      CartProduct -> do+          (cols1, _, _)       <- fromPropTriple ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols1+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      CartProductS -> do+          (cols1, _, _)       <- fromPropTriple ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols1+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      NestProductS -> do+          cols1 <- fst <$> fromPropPair ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols1+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      ThetaJoin p -> do+          (cols1, _, _)               <- fromPropTriple ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          leftReqCols'                <- (VProp $ Just $ reqLeftPredCols p) ∪ leftReqCols+          rightReqCols'               <- (VProp $ Just $ reqRightPredCols p) ∪ rightReqCols+          (,) <$> (childReqColumns1 ∪ leftReqCols') <*> (childReqColumns2 ∪ rightReqCols')++      UnboxScalar -> do+          cols1                       <- fromProp ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          (,) <$> (childReqColumns1 ∪ leftReqCols) <*> (childReqColumns2 ∪ rightReqCols)++      NestJoin p -> do+          (cols1, _, _)               <- fromPropTriple ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          leftReqCols'                <- (VProp $ Just $ reqLeftPredCols p) ∪ leftReqCols+          rightReqCols'               <- (VProp $ Just $ reqRightPredCols p) ∪ rightReqCols+          (,) <$> (childReqColumns1 ∪ leftReqCols') <*> (childReqColumns2 ∪ rightReqCols')+      NestProduct -> do+          (cols1, _, _)               <- fromPropTriple ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          (,) <$> (childReqColumns1 ∪ leftReqCols) <*> (childReqColumns2 ∪ rightReqCols)++      ThetaJoinS p -> do+          (cols1, _, _)               <- fromPropTriple ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          leftReqCols'                <- (VProp $ Just $ reqLeftPredCols p) ∪ leftReqCols+          rightReqCols'               <- (VProp $ Just $ reqRightPredCols p) ∪ rightReqCols+          (,) <$> (childReqColumns1 ∪ leftReqCols') <*> (childReqColumns2 ∪ rightReqCols')++      NestJoinS p -> do+          cols1                       <- fst <$> fromPropPair ownReqColumns+          (leftReqCols, rightReqCols) <- partitionCols childBUProps1 childBUProps2 cols1+          leftReqCols'                <- (VProp $ Just $ reqLeftPredCols p) ∪ leftReqCols+          rightReqCols'               <- (VProp $ Just $ reqRightPredCols p) ∪ rightReqCols+          (,) <$> (childReqColumns1 ∪ leftReqCols') <*> (childReqColumns2 ∪ rightReqCols')++      ZipS -> do+          (cols, _, _) <- fromPropTriple ownReqColumns+          (ownLeft, ownRight) <- partitionCols childBUProps1 childBUProps2 cols+          (,) <$> (childReqColumns1 ∪ ownLeft) <*> (childReqColumns2 ∪ ownRight)++      -- For a semijoin, we only require the columns used in the join argument+      -- from the right input.+      SemiJoin p -> do+          cols1     <- fst <$> fromPropPair ownReqColumns+          fromLeft  <- ((VProp $ Just $ reqLeftPredCols p) ∪ VProp cols1) >>= (∪ childReqColumns1)+          fromRight <- (VProp $ Just $ reqRightPredCols p) ∪ childReqColumns2+          return (fromLeft, fromRight)++      -- For a semijoin, we only require the columns used in the join argument+      -- from the right input.+      SemiJoinS p -> do+          cols1     <- fst <$> fromPropPair ownReqColumns+          fromLeft  <- ((VProp $ Just $ reqLeftPredCols p) ∪ VProp cols1) >>= (∪ childReqColumns1)+          fromRight <- (VProp $ Just $ reqRightPredCols p) ∪ childReqColumns2+          return (fromLeft, fromRight)++      -- For a antijoin, we only require the columns used in the join argument+      -- from the right input.+      AntiJoin p -> do+          cols1     <- fst <$> fromPropPair ownReqColumns+          fromLeft  <- ((VProp $ Just $ reqLeftPredCols p) ∪ VProp cols1) >>= (∪ childReqColumns1)+          fromRight <- (VProp $ Just $ reqRightPredCols p) ∪ childReqColumns2+          return (fromLeft, fromRight)++      -- For a antijoin, we only require the columns used in the join argument+      -- from the right input.+      AntiJoinS p -> do+          cols1     <- fst <$> fromPropPair ownReqColumns+          fromLeft  <- ((VProp $ Just $ reqLeftPredCols p) ∪ VProp cols1) >>= (∪ childReqColumns1)+          fromRight <- (VProp $ Just $ reqRightPredCols p) ∪ childReqColumns2+          return (fromLeft, fromRight)++      TransposeS -> do+          cols      <- snd <$> fromPropPair ownReqColumns+          fromRight <- childReqColumns2 ∪ VProp cols+          return (none, fromRight)++inferReqColumnsTerOp :: VectorProp ReqCols+                     -> VectorProp ReqCols+                     -> VectorProp ReqCols+                     -> VectorProp ReqCols+                     -> TerOp+                     -> Either String (VectorProp ReqCols, VectorProp ReqCols, VectorProp ReqCols)+inferReqColumnsTerOp ownReqColumns _ childReqColumns2 childReqColumns3 op =+    case op of+        Combine -> do+            (cols, _, _) <- fromPropTriple ownReqColumns+            fromLeft     <- VProp cols ∪ childReqColumns2+            fromRight    <- VProp cols ∪ childReqColumns3+            return (one, fromLeft, fromRight)
+ src/Database/DSH/Optimizer/VL/Properties/TopDown.hs view
@@ -0,0 +1,185 @@+module Database.DSH.Optimizer.VL.Properties.TopDown(inferTopDownProperties) where++import Control.Monad.State+import Text.Printf+  +import qualified Data.IntMap as M++import Database.Algebra.Dag.Common+import Database.Algebra.Dag++import Database.DSH.VL.Lang+import Database.DSH.Optimizer.Common.Auxiliary+import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Properties.ReqColumns+  +reqColumnsSeed :: ReqCols+reqColumnsSeed = Nothing++vPropSeed :: TopDownProps+vPropSeed = TDProps { reqColumnsProp = VProp reqColumnsSeed }++vPropPairSeed :: TopDownProps+vPropPairSeed = TDProps { reqColumnsProp = VPropPair reqColumnsSeed reqColumnsSeed }++vPropTripleSeed :: TopDownProps+vPropTripleSeed = TDProps { reqColumnsProp = VPropTriple reqColumnsSeed reqColumnsSeed reqColumnsSeed }+                  +seed :: VL -> TopDownProps+seed (NullaryOp _) = vPropSeed+seed (UnOp op _)   =+  case op of+    WinFun _           -> vPropSeed+    SelectPos1{}       -> vPropTripleSeed+    SelectPos1S{}      -> vPropTripleSeed +    Reverse            -> vPropPairSeed+    ReverseS           -> vPropPairSeed+    UniqueS            -> vPropSeed+    UnboxRename        -> vPropSeed+    Segment            -> vPropSeed+    Unsegment          -> vPropSeed+    Select     _       -> vPropPairSeed+    SortS _            -> vPropPairSeed+    GroupS _           -> vPropTripleSeed+    Project      _     -> vPropSeed+    Aggr _             -> vPropSeed+    AggrNonEmpty _     -> vPropSeed+    AggrNonEmptyS _    -> vPropSeed+    GroupAggr (_, _)   -> vPropSeed+    R1                 -> vPropSeed+    R2                 -> vPropSeed+    R3                 -> vPropSeed+    Number             -> vPropSeed+    NumberS            -> vPropSeed+    Transpose          -> vPropPairSeed+    Reshape _          -> vPropPairSeed+    ReshapeS _         -> vPropPairSeed++seed (BinOp op _ _) = +  case op of+    Append             -> vPropTripleSeed+    AppendS            -> vPropTripleSeed+    ZipS               -> vPropTripleSeed+    DistLift           -> vPropPairSeed+    PropFilter         -> vPropPairSeed+    PropReorder        -> vPropPairSeed+    UnboxNested        -> vPropPairSeed+    UnboxScalar        -> vPropSeed+    SelectPos _        -> vPropTripleSeed+    SelectPosS _       -> vPropTripleSeed+    PropRename         -> vPropSeed+    AggrS _            -> vPropSeed+    Zip                -> vPropSeed+    Align              -> vPropSeed+    CartProduct        -> vPropTripleSeed+    CartProductS       -> vPropTripleSeed+    ThetaJoin _        -> vPropTripleSeed+    NestJoin _         -> vPropTripleSeed+    NestProduct        -> vPropTripleSeed+    ThetaJoinS _       -> vPropTripleSeed+    SemiJoin _         -> vPropPairSeed+    SemiJoinS _        -> vPropPairSeed+    AntiJoin _         -> vPropPairSeed+    AntiJoinS _        -> vPropPairSeed+    NestJoinS _        -> vPropPairSeed+    NestProductS       -> vPropPairSeed+    TransposeS         -> vPropPairSeed+    +    +seed (TerOp op _ _ _) =+  case op of+    Combine -> vPropTripleSeed+    ++type InferenceState = NodeMap TopDownProps++lookupProps :: AlgNode -> State InferenceState TopDownProps+lookupProps n = do+    m <- get+    case M.lookup n m of+        Just props -> return props+        Nothing -> error "TopDown.lookupProps"++replaceProps :: AlgNode -> TopDownProps -> State InferenceState ()+replaceProps n p = modify (M.insert n p)++inferUnOp :: BottomUpProps -> TopDownProps -> TopDownProps -> UnOp -> Either String TopDownProps+inferUnOp childBUProps ownProps cp op = do+    cols <- inferReqColumnsUnOp childBUProps+                                (reqColumnsProp ownProps) +                                (reqColumnsProp cp) +                                op+    return $ TDProps { reqColumnsProp = cols }++inferBinOp :: BottomUpProps +           -> BottomUpProps+           -> TopDownProps +           -> TopDownProps +           -> TopDownProps +           -> BinOp +           -> Either String (TopDownProps, TopDownProps)+inferBinOp childBUProps1 childBUProps2 ownProps cp1 cp2 op = do+    (crc1', crc2') <- inferReqColumnsBinOp childBUProps1 +                                           childBUProps2 +                                           (reqColumnsProp ownProps) +                                           (reqColumnsProp cp1) +                                           (reqColumnsProp cp2) op+    let cp1' = TDProps { reqColumnsProp = crc1' }+        cp2' = TDProps { reqColumnsProp = crc2' }+    return (cp1', cp2')++inferTerOp :: TopDownProps +           -> TopDownProps +           -> TopDownProps +           -> TopDownProps +           -> TerOp +           -> Either String (TopDownProps, TopDownProps, TopDownProps)+inferTerOp ownProps cp1 cp2 cp3 op = do+    (crc1', crc2', crc3') <- inferReqColumnsTerOp (reqColumnsProp ownProps) +                                                  (reqColumnsProp cp1) +                                                  (reqColumnsProp cp2) +                                                  (reqColumnsProp cp3) op+    let cp1' = TDProps { reqColumnsProp = crc1' }+        cp2' = TDProps { reqColumnsProp = crc2' }+        cp3' = TDProps { reqColumnsProp = crc3' }+    return (cp1', cp2', cp3')++inferChildProperties :: NodeMap BottomUpProps -> AlgebraDag VL -> AlgNode -> State InferenceState ()+inferChildProperties buPropMap d n = do+    ownProps <- lookupProps n+    case operator n d of+        NullaryOp _ -> return ()+        UnOp op c -> do+            cp <- lookupProps c+            let buProps = lookupUnsafe buPropMap "TopDown.infer" c+            let cp' = checkError n [cp] d $ inferUnOp buProps ownProps cp op+            replaceProps c cp'+        BinOp op c1 c2 -> do+            cp1 <- lookupProps c1+            cp2 <- lookupProps c2+            let buProps1 = lookupUnsafe buPropMap "TopDown.inferChildProperties" c1+                buProps2 = lookupUnsafe buPropMap "TopDown.inferChildProperties" c2+            let (cp1', cp2') = checkError n [cp1, cp2] d $ inferBinOp buProps1 buProps2 ownProps cp1 cp2 op+            replaceProps c1 cp1'+            replaceProps c2 cp2'+        TerOp op c1 c2 c3 -> do+          cp1 <- lookupProps c1+          cp2 <- lookupProps c2+          cp3 <- lookupProps c3+          let (cp1', cp2', cp3') = checkError n [cp1, cp2, cp3] d $ inferTerOp ownProps cp1 cp2 cp3 op+          replaceProps c1 cp1'+          replaceProps c2 cp2'+          replaceProps c3 cp3'++checkError :: AlgNode -> [TopDownProps] -> AlgebraDag VL -> Either String p -> p+checkError n childProps d (Left msg) = +    let completeMsg   = printf "Inference failed at node %d\n%s\n%s\n%s" n msg (show childProps) (show $ nodeMap d)+    in error completeMsg+checkError _ _ _ (Right props) = props+    +-- | Infer properties during a top-down traversal.+inferTopDownProperties :: NodeMap BottomUpProps -> [AlgNode] -> AlgebraDag VL -> NodeMap TopDownProps+inferTopDownProperties buPropMap topOrderedNodes d = execState action initialMap +    where action = mapM_ (inferChildProperties buPropMap d) topOrderedNodes+          initialMap = M.map seed $ nodeMap d+          
+ src/Database/DSH/Optimizer/VL/Properties/Types.hs view
@@ -0,0 +1,127 @@+module Database.DSH.Optimizer.VL.Properties.Types where++import           Text.PrettyPrint++import           Database.DSH.VL.Lang+import           Database.DSH.VL.Render.Dot++data VectorProp a = VProp a+                  | VPropPair a a+                  | VPropTriple a a a++instance Show a => Show (VectorProp a) where+  show (VProp a) = show a+  show (VPropPair a1 a2) = show (a1, a2)+  show (VPropTriple a1 a2 a3) = show (a1, a2, a3)++data VectorType = ValueVector Int+                | RenameVector+                | PropVector+                deriving Show++data Const = Const VLVal+           | NoConst+            deriving Show++data ConstDescr = ConstDescr Int+                | NonConstDescr++data ConstPayload = ConstPL VLVal+                  | NonConstPL+                  deriving Show++data ConstVec = DBVConst ConstDescr [ConstPayload]+              | RenameVecConst SourceConstDescr TargetConstDescr+              | PropVecConst SourceConstDescr TargetConstDescr+              deriving Show++newtype SourceConstDescr = SC ConstDescr deriving Show+newtype TargetConstDescr = TC ConstDescr deriving Show++data BottomUpProps = BUProps { emptyProp            :: VectorProp Bool+                             -- Documents wether a vector is+                             -- statically known to be not empty. For+                             -- a flat vector (i.e. a vector with only+                             -- one segment) t his property is true if+                             -- we can statically decide that the+                             -- vector is not empty. For an inner+                             -- vector, i.e. a vector with multiple+                             -- segments, it is true if *every*+                             -- segment is non-empty.+                             , nonEmptyProp         :: VectorProp Bool+                             , constProp            :: VectorProp ConstVec+                             , card1Prop            :: VectorProp Bool+                             , vectorTypeProp       :: VectorProp VectorType+                             } deriving (Show)+++type ReqCols = Maybe [DBCol]++data TopDownProps = TDProps { reqColumnsProp :: VectorProp ReqCols } deriving (Show)++data Properties = Properties { bu :: BottomUpProps+                             , td :: TopDownProps+                             }++class Renderable a where+  renderProp :: a -> Doc++insertComma :: Doc -> Doc -> Doc+insertComma d1 d2 = d1 <> comma <+> d2++instance Renderable a => Renderable (VectorProp a) where+  renderProp (VProp p)              = renderProp p+  renderProp (VPropPair p1 p2)      = parens $ (renderProp p1) `insertComma` (renderProp p2)+  renderProp (VPropTriple p1 p2 p3) = parens $ (renderProp p1) `insertComma` (renderProp p2) `insertComma` (renderProp p3)++instance Renderable a => Renderable (Maybe a) where+  renderProp (Just x) = renderProp x+  renderProp Nothing  = text "na"++instance Renderable Bool where+  renderProp = text . show++bracketList :: (a -> Doc) -> [a] -> Doc+bracketList f = brackets . hsep . punctuate comma . map f++instance Renderable Int where+  renderProp = text . show++instance Renderable a => Renderable [a] where+  renderProp = bracketList renderProp++instance Show ConstDescr where+  show (ConstDescr v) = render $ int v+  show NonConstDescr  = "NC"++instance Renderable ConstVec where+  renderProp (DBVConst d ps) = (text $ show d) <+> payload+    where payload = bracketList id $ map renderPL $ foldr isConst [] $ zip [1..] ps+          isConst (_, NonConstPL) vals   = vals+          isConst (i, (ConstPL v)) vals  = (i, v) : vals++          renderPL (i, v)  = int i <> colon <> renderTblVal v++  renderProp (RenameVecConst (SC ds) (TC ts)) = (text $ show ds) <> text " -> " <> (text $ show ts)+  renderProp (PropVecConst (SC ds) (TC ts)) = (text $ show ds) <> text " -> " <> (text $ show ts)++instance Renderable VectorType where+  renderProp = text . show++instance Renderable BottomUpProps where+  renderProp p = text "empty:" <+> (renderProp $ emptyProp p)+                 $$ text "const:" <+> (renderProp $ constProp p)+                 $$ text "schema:" <+> (renderProp $ vectorTypeProp p)++instance Renderable TopDownProps where+  renderProp p = text "reqCols:" <+> (text $ show $ reqColumnsProp p)++-- | Rendering function for the bottom-up properties container.+renderBottomUpProps :: BottomUpProps -> [String]+renderBottomUpProps ps = [render $ renderProp ps]++renderTopDownProps :: TopDownProps -> [String]+renderTopDownProps ps = [render $ renderProp ps]++renderProperties  :: Properties -> [String]+renderProperties ps = (renderBottomUpProps $ bu ps) ++ (renderTopDownProps $ td ps)
+ src/Database/DSH/Optimizer/VL/Properties/VectorType.hs view
@@ -0,0 +1,164 @@+{-# LANGUAGE TemplateHaskell #-}++-- FIXME introduce consistency checks for schema inference+module Database.DSH.Optimizer.VL.Properties.VectorType where++import           Control.Monad+import           Control.Applicative+import qualified Data.List.NonEmpty as N+       +import           Database.DSH.Optimizer.VL.Properties.Types+  +import           Database.DSH.VL.Lang+  +{- Implement more checks: check the input types for correctness -}++vectorWidth :: VectorProp VectorType -> Int+vectorWidth (VProp (ValueVector w))  = w+vectorWidth _                        = error "vectorWidth: non-ValueVector input"++inferVectorTypeNullOp :: NullOp -> Either String (VectorProp VectorType)+inferVectorTypeNullOp op =+  case op of+    SingletonDescr      -> Right $ VProp $ ValueVector 0+    Lit (_, t, _)       -> Right $ VProp $ ValueVector $ length t+    TableRef (_, cs, _) -> Right $ VProp $ ValueVector $ length cs+  +unpack :: VectorProp VectorType -> Either String VectorType+unpack (VProp s) = Right s+unpack _         = Left "Input is not a single vector property" ++inferVectorTypeUnOp :: VectorProp VectorType -> UnOp -> Either String (VectorProp VectorType)+inferVectorTypeUnOp s op = +  case op of+    WinFun _ -> do+        ValueVector w <- unpack s+        return $ VProp $ ValueVector $ w + 1+    UniqueS -> VProp <$> unpack s+    Aggr _ -> Right $ VProp $ ValueVector 1+    AggrNonEmpty as -> Right $ VProp $ ValueVector $ N.length as+    UnboxRename -> Right $ VProp $ RenameVector+    Segment -> VProp <$> unpack s+    Unsegment -> VProp <$> unpack s+    Reverse -> liftM2 VPropPair (unpack s) (Right PropVector)+    ReverseS -> liftM2 VPropPair (unpack s) (Right PropVector)+    SelectPos1{} -> liftM3 VPropTriple (unpack s) (Right RenameVector) (Right RenameVector)+    SelectPos1S{} -> liftM3 VPropTriple (unpack s) (Right RenameVector) (Right RenameVector)+    R1 -> +      case s of+        VPropPair s1 _ -> Right $ VProp s1+        VPropTriple s1 _ _ -> Right $ VProp s1+        _ -> Left "Input of R1 is not a tuple"+    R2 -> +      case s of+        VPropPair _ s2 -> Right $ VProp s2+        VPropTriple _ s2 _ -> Right $ VProp s2+        _ -> Left "Input of R2 is not a tuple"+    R3 -> +      case s of+        VPropTriple s3 _ _ -> Right $ VProp s3+        _ -> Left "Input of R3 is not a tuple"++    Project valProjs -> Right $ VProp $ ValueVector $ length valProjs++    Select _ -> VPropPair <$> unpack s <*> (Right RenameVector)+    SortS _  -> liftM2 VPropPair (unpack s) (Right PropVector)+    AggrNonEmptyS as -> Right $ VProp $ ValueVector $ N.length as++    GroupS es -> +      case s of+        VProp t@(ValueVector _) -> +          Right $ VPropTriple (ValueVector $ length es) t PropVector+        _                                                    -> +          Left "Input of GroupSimple is not a value vector"+    GroupAggr (g, as) -> Right $ VProp $ ValueVector (length g + N.length as)+    Number -> do+        ValueVector w <- unpack s+        return $ VProp $ ValueVector (w + 1)+    NumberS -> do+        ValueVector w <- unpack s+        return $ VProp $ ValueVector (w + 1)++    Reshape _ -> liftM2 VPropPair (return $ ValueVector 0) (unpack s)+    ReshapeS _ -> liftM2 VPropPair (return $ ValueVector 0) (unpack s)+    Transpose -> liftM2 VPropPair (return $ ValueVector 0) (unpack s)+  +reqValVectors :: VectorProp VectorType +                 -> VectorProp VectorType +                 -> (Int -> Int -> VectorProp VectorType)+                 -> String +                 -> Either String (VectorProp VectorType)+reqValVectors (VProp (ValueVector w1)) (VProp (ValueVector w2)) f _ =+  Right $ f w1 w2+reqValVectors _ _ _ e =+  Left $ "Inputs of " ++ e ++ " are not ValueVectors"+      +inferVectorTypeBinOp :: VectorProp VectorType -> VectorProp VectorType -> BinOp -> Either String (VectorProp VectorType)+inferVectorTypeBinOp s1 s2 op = +  case op of+    AggrS _ -> return $ VProp $ ValueVector 1++    DistLift -> do+        ValueVector w1 <- unpack s1+        ValueVector w2 <- unpack s2+        return $ VPropPair (ValueVector $ w1 + w2) PropVector++    PropRename -> Right s2+    PropFilter -> liftM2 VPropPair (unpack s2) (Right RenameVector)+    PropReorder -> liftM2 VPropPair (unpack s2) (Right PropVector)+    UnboxNested -> liftM2 VPropPair (unpack s2) (Right RenameVector)+    Append -> +      case (s1, s2) of+        (VProp (ValueVector w1), VProp (ValueVector w2)) | w1 == w2 -> +          Right $ VPropTriple (ValueVector w1) RenameVector RenameVector+        (VProp (ValueVector w1), VProp (ValueVector w2)) -> +          Left $ "Inputs of Append do not have the same width " ++ (show w1) ++ " " ++ (show w2)+        v -> +          Left $ "Input of Append is not a ValueVector " ++ (show v)+    AppendS -> +      case (s1, s2) of+        (VProp (ValueVector w1), VProp (ValueVector w2)) | w1 == w2 -> +          Right $ VPropTriple (ValueVector w1) RenameVector RenameVector+        (VProp (ValueVector w1), VProp (ValueVector w2)) -> +          Left $ "Inputs of Append do not have the same width " ++ (show w1) ++ " " ++ (show w2)+        v -> +          Left $ "Input of Append is not a ValueVector " ++ (show v)++    SelectPos _ -> liftM3 VPropTriple (unpack s1) (Right RenameVector) (Right RenameVector)+    SelectPosS _ -> liftM3 VPropTriple (unpack s1) (Right RenameVector) (Right RenameVector)+    Align ->+      case (s1, s2) of+        (VProp (ValueVector w1), VProp (ValueVector w2)) -> Right $ VProp $ ValueVector $ w1 + w2+        _                                                -> Left "Inputs of Align are not ValueVectors"+    Zip ->+      case (s1, s2) of+        (VProp (ValueVector w1), VProp (ValueVector w2)) -> Right $ VProp $ ValueVector $ w1 + w2+        _                                                -> Left "Inputs of PairL are not ValueVectors"+    ZipS -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) RenameVector RenameVector) "ZipL"+    CartProduct -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "CartProduct"+    CartProductS -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "CartProductS"+    NestProductS -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "NestProductS"+    ThetaJoin _ -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "ThetaJoin"+    UnboxScalar -> reqValVectors s1 s2 (\w1 w2 -> VProp $ ValueVector $ w1 + w2) "UnboxScalar"+    NestJoin _ -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "NestJoin"+    NestProduct -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "NestProduct"+    ThetaJoinS _ -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "ThetaJoinS"+    NestJoinS _ -> reqValVectors s1 s2 (\w1 w2 -> VPropTriple (ValueVector $ w1 + w2) PropVector PropVector) "NestJoinS"+    SemiJoin _ -> liftM2 VPropPair (unpack s1) (Right RenameVector)+    SemiJoinS _ -> liftM2 VPropPair (unpack s1) (Right RenameVector)+    AntiJoin _ -> liftM2 VPropPair (unpack s1) (Right RenameVector)+    AntiJoinS _ -> liftM2 VPropPair (unpack s1) (Right RenameVector)++    TransposeS -> liftM2 VPropPair (return $ ValueVector 0) (unpack s2)++inferVectorTypeTerOp :: VectorProp VectorType -> VectorProp VectorType -> VectorProp VectorType -> TerOp -> Either String (VectorProp VectorType)+inferVectorTypeTerOp _ s2 s3 op = +  case op of+    Combine -> +      case (s2, s3) of+        (VProp (ValueVector w1), VProp (ValueVector w2)) | w1 == w2 -> +          Right $ VPropTriple (ValueVector w1) RenameVector RenameVector+        (VProp (ValueVector _), VProp (ValueVector _))              -> +          Left $ "Inputs of CombineVec do not have the same width"+        _                                                           -> +          Left $ "Inputs of CombineVec are not ValueVectors/DescrVectors " ++ (show (s2, s3))
+ src/Database/DSH/Optimizer/VL/Rewrite/Aggregation.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE TemplateHaskell #-}+module Database.DSH.Optimizer.VL.Rewrite.Aggregation(groupingToAggregation) where++import           Control.Applicative+import           Control.Monad+import qualified Data.List.NonEmpty                         as N+import           Data.Semigroup++import           Database.Algebra.Dag.Common++import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.Optimizer.VL.Rewrite.Common+import           Database.DSH.VL.Lang++aggregationRules :: VLRuleSet ()+aggregationRules = [ inlineAggrSProject+                   , inlineAggrProject+                   , inlineAggrNonEmptyProject+                   , inlineAggrSNonEmptyProject+                   , flatGrouping+                   , mergeNonEmptyAggrs+                   , mergeGroupAggr+                   , mergeGroupWithGroupAggrLeft+                   ]++aggregationRulesBottomUp :: VLRuleSet BottomUpProps+aggregationRulesBottomUp = [ nonEmptyAggr+                           , nonEmptyAggrS+                           ]++groupingToAggregation :: VLRewrite Bool+groupingToAggregation = iteratively $ sequenceRewrites [ applyToAll inferBottomUp aggregationRulesBottomUp+                                                       , applyToAll noProps aggregationRules+                                                       ]++-- FIXME this rewrite will no longer work: take the UnboxScalarS+-- operator into account.+mergeNonEmptyAggrs :: VLRule ()+mergeNonEmptyAggrs q =+  $(dagPatMatch 'q "(AggrNonEmptyS afuns1 (qi1)) Zip (AggrNonEmptyS afuns2 (qi2))"+    [| do+        predicate $ $(v "qi1") == $(v "qi2")++        return $ do+            logRewrite "Aggregation.NonEmpty.Merge" q+            let afuns  = $(v "afuns1") <> $(v "afuns2")+            let aggrOp = UnOp (AggrNonEmptyS afuns) $(v "qi1")+            void $ replaceWithNew q aggrOp |])++-- | If we can infer that the vector is not empty, we can employ a+-- simplified version of the aggregate operator that does not add a+-- default value for an empty input.+nonEmptyAggr :: VLRule BottomUpProps+nonEmptyAggr q =+  $(dagPatMatch 'q "Aggr aggrFun (q1)"+    [| do+        VProp True <- nonEmptyProp <$> properties $(v "q1")++        return $ do+            logRewrite "Aggregation.NonEmpty.Aggr" q+            let aggrOp = UnOp (AggrNonEmpty ($(v "aggrFun") N.:| [])) $(v "q1")+            void $ replaceWithNew q aggrOp |])++-- | If we can infer that all segments (if there are any) are not+-- empty, we can employ a simplified version of the aggregate operator+-- that does not add default values for empty segments.+nonEmptyAggrS :: VLRule BottomUpProps+nonEmptyAggrS q =+  $(dagPatMatch 'q "(_) AggrS aggrFun (q2)"+    [| do+        VProp True <- nonEmptyProp <$> properties $(v "q2")++        return $ do+            logRewrite "Aggregation.NonEmpty.AggrS" q+            let aggrOp = UnOp (AggrNonEmptyS ($(v "aggrFun") N.:| [])) $(v "q2")+            void $ replaceWithNew q aggrOp |])++-- | Merge a projection into a segmented aggregate operator.+inlineAggrProject :: VLRule ()+inlineAggrProject q =+  $(dagPatMatch 'q "Aggr afun (Project proj (qi))"+    [| do+        let env = zip [1..] $(v "proj")+        let afun' = mapAggrFun (mergeExpr env) $(v "afun")++        return $ do+            logRewrite "Aggregation.Normalize.Aggr.Project" q+            void $ replaceWithNew q $ UnOp (Aggr afun') $(v "qi") |])++-- | Merge a projection into a segmented aggregate operator.+inlineAggrSProject :: VLRule ()+inlineAggrSProject q =+  $(dagPatMatch 'q "(qo) AggrS afun (Project proj (qi))"+    [| do+        let env = zip [1..] $(v "proj")+        let afun' = mapAggrFun (mergeExpr env) $(v "afun")++        return $ do+            logRewrite "Aggregation.Normalize.AggrS.Project" q+            void $ replaceWithNew q $ BinOp (AggrS afun') $(v "qo") $(v "qi") |])++-- | Merge a projection into a non-empty aggregate operator. We+-- restrict this to only one aggregate function. Therefore, merging of+-- projections must happen before merging of aggregate operators+inlineAggrNonEmptyProject :: VLRule ()+inlineAggrNonEmptyProject q =+  $(dagPatMatch 'q "AggrNonEmpty afuns (Project proj (qi))"+    [| do+        let env = zip [1..] $(v "proj")+        let afuns' = fmap (mapAggrFun (mergeExpr env)) $(v "afuns")++        return $ do+            logRewrite "Aggregation.Normalize.AggrNonEmpty.Project" q+            let aggrOp = UnOp (AggrNonEmpty afuns') $(v "qi")+            void $ replaceWithNew q aggrOp |])++-- | Merge a projection into a non-empty segmented aggregate+-- operator. We restrict this to only one aggregate+-- function. Therefore, merging of projections must happen before+-- merging of aggregate operators+inlineAggrSNonEmptyProject :: VLRule ()+inlineAggrSNonEmptyProject q =+  $(dagPatMatch 'q "AggrNonEmptyS afuns (Project proj (qi))"+    [| do+        let env = zip [1..] $(v "proj")+        let afuns' = fmap (mapAggrFun (mergeExpr env)) $(v "afuns")++        return $ do+            logRewrite "Aggregation.Normalize.AggrNonEmptyS.Project" q+            let aggrOp = UnOp (AggrNonEmptyS afuns') $(v "qi")+            void $ replaceWithNew q aggrOp |])++-- We rewrite a combination of GroupBy and aggregation operators into a single+-- VecAggr operator if the following conditions hold:+--+-- 1. The R2 output of GroupBy is only consumed by aggregation operators (MaxL,+--    MinL, VecSumL, LengthSeg)+-- 2. The grouping criteria is a simple column projection from the input vector+flatGrouping :: VLRule ()+flatGrouping q =+  $(dagPatMatch 'q "(R1 (qg)) UnboxScalar (AggrNonEmptyS afuns (R2 (qg1=GroupS groupExprs (q1))))"+    [| do++        -- Ensure that the aggregate results are unboxed using the+        -- outer vector of the grouping operator.+        predicate $ $(v "qg") == $(v "qg1")++        return $ do+          logRewrite "Aggregation.Grouping.Aggr" q+          void $ replaceWithNew q $ UnOp (GroupAggr ($(v "groupExprs"), $(v "afuns"))) $(v "q1") |])++mergeGroupAggr :: VLRule ()+mergeGroupAggr q =+  $(dagPatMatch 'q "(GroupAggr args1 (q1)) Align (GroupAggr args2 (q2))"+    [| do+        let (ges1, afuns1) = $(v "args1")+        let (ges2, afuns2) = $(v "args2")++        -- The rewrite can be applied if the same input is grouped+        -- according to the same grouping expressions.+        predicate $ ges1 == ges2+        predicate $ $(v "q1") == $(v "q2")+    +        return $ do+          logRewrite "Aggregation.Normalize.MergeGroupAggr" q+          groupNode <- insert $ UnOp (GroupAggr ($(v "ges1"), ($(v "afuns1") <> $(v "afuns2")))) $(v "q1")++          -- Reconstruct the schema produced by Zip. Note that this+          -- duplicates the grouping columns.+          let groupWidth = length $(v "ges1")+              aggrWidth1 = N.length afuns1+              aggrWidth2 = N.length afuns2+              groupCols  = [ Column c | c <- [1 .. groupWidth]]++          let proj = groupCols+                     +++                     [ Column $ c + groupWidth | c <- [1 .. aggrWidth1] ]+                     +++                     groupCols+                     +++                     [ Column $ c + groupWidth + aggrWidth1 | c <- [1 .. aggrWidth2] ]++          void $ replaceWithNew q $ UnOp (Project proj) groupNode |])++-- | This is a cleanup rewrite: It applies in a situation when+-- aggregates have already been merged with GroupScalarS into+-- GroupAggr. If the GroupAggr output is combined with the R1 output+-- of GroupScalarS on the same input and grouping expressions via Zip,+-- the effect is that only the grouping expressions are duplicated.+mergeGroupWithGroupAggrLeft :: VLRule ()+mergeGroupWithGroupAggrLeft q =+  $(dagPatMatch 'q "(R1 (GroupS ges (q1))) Align (GroupAggr args (q2))"+    [| do+        let (ges', afuns) = $(v "args")+    +        -- Input vectors and grouping expressions have to be the same.+        predicate $ $(v "q1") == $(v "q2")+        predicate $ $(v "ges") == ges'++        return $ do+            logRewrite "Aggregation.Normalize.MergeGroupScalars" q+            +            -- To keep the schema, we have to duplicate the grouping+            -- columns.+            let groupWidth = length ges'+                aggrWidth  = N.length afuns+                groupCols  = [ Column c | c <- [1..groupWidth] ]+                proj       = groupCols +                             ++ +                             groupCols+                             +++                             [ Column $ c + groupWidth | c <- [1..aggrWidth] ]++            groupNode <- insert $ UnOp (GroupAggr (ges', afuns)) $(v "q1")+            void $ replaceWithNew q $ UnOp (Project proj) groupNode |])+                     +
+ src/Database/DSH/Optimizer/VL/Rewrite/Common.hs view
@@ -0,0 +1,115 @@+{-# LANGUAGE TemplateHaskell #-}+module Database.DSH.Optimizer.VL.Rewrite.Common where++import qualified Data.IntMap                                   as M++import           Control.Monad++import           Database.Algebra.Dag.Common++import           Database.DSH.Common.QueryPlan+import           Database.DSH.Impossible++import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.VL.Lang+import           Database.DSH.VL.Vector++import           Database.DSH.Optimizer.VL.Properties.BottomUp+import           Database.DSH.Optimizer.VL.Properties.TopDown+import           Database.DSH.Optimizer.VL.Properties.Types++  -- Type abbreviations for convenience+type VLRewrite p = Rewrite VL (Shape VLDVec) p+type VLRule p = Rule VL p (Shape VLDVec)+type VLRuleSet p = RuleSet VL p (Shape VLDVec)+type VLMatch p = Match VL p (Shape VLDVec)++inferBottomUp :: VLRewrite (NodeMap BottomUpProps)+inferBottomUp = do+  props <- infer inferBottomUpProperties+  return props++inferTopDown :: VLRewrite (NodeMap TopDownProps)+inferTopDown = do+  to <- topsort+  buPropMap <- infer inferBottomUpProperties+  props <- infer (inferTopDownProperties buPropMap to)+  return props++inferProperties :: VLRewrite (NodeMap Properties)+inferProperties = do+  buMap <- inferBottomUp+  tdMap <- inferTopDown+  return $ M.intersectionWith Properties buMap tdMap++noProps :: Monad m => m (M.IntMap a)+noProps = return M.empty++---------------------------------------------------------------------------------+-- Rewrite helper functions++lookupR1Parents :: AlgNode -> VLRewrite [AlgNode]+lookupR1Parents q = do+  let isR1 q' = do+        o <- operator q'+        case o of+          UnOp R1 _ -> return True+          _         -> return False++  ps <- parents q+  filterM isR1 ps++lookupR2Parents :: AlgNode -> VLRewrite [AlgNode]+lookupR2Parents q = do+  let isR2 q' = do+        o <- operator q'+        case o of+          UnOp R2 _ -> return True+          _         -> return False++  ps <- parents q+  filterM isR2 ps++mergeExpr :: [(DBCol, Expr)] -> Expr -> Expr+mergeExpr env expr =+    case expr of+        BinApp o e1 e2 -> BinApp o (mergeExpr env e1) (mergeExpr env e2)+        UnApp o e1     -> UnApp o (mergeExpr env e1)+        Column c       -> case lookup c env of+                               Just expr' -> expr'+                               Nothing    -> $impossible+        If c t e       -> If (mergeExpr env c) (mergeExpr env t) (mergeExpr env e)+        Constant _     -> expr++-- | Unwrap a constant value+constVal :: Monad m => (VLVal -> a) -> ConstPayload -> m a+constVal wrap (ConstPL val) = return $ wrap val+constVal _             _    = fail "no match"++mapAggrFun :: (Expr -> Expr) -> AggrFun -> AggrFun+mapAggrFun f (AggrMax e) = AggrMax $ f e+mapAggrFun f (AggrSum t e) = AggrSum t $ f e+mapAggrFun f (AggrMin e) = AggrMin $ f e+mapAggrFun f (AggrAvg e) = AggrAvg $ f e+mapAggrFun f (AggrAny e) = AggrAny $ f e+mapAggrFun f (AggrAll e) = AggrAll $ f e+mapAggrFun _ AggrCount   = AggrCount++mapWinFun :: (Expr -> Expr) -> WinFun -> WinFun+mapWinFun f (WinMax e)        = WinMax $ f e+mapWinFun f (WinSum e)        = WinSum $ f e+mapWinFun f (WinMin e)        = WinMin $ f e+mapWinFun f (WinAvg e)        = WinAvg $ f e+mapWinFun f (WinAny e)        = WinAny $ f e+mapWinFun f (WinAll e)        = WinAll $ f e+mapWinFun f (WinFirstValue e) = WinFirstValue $ f e+mapWinFun _ WinCount          = WinCount++mapExprCols :: (DBCol -> DBCol) -> Expr -> Expr+mapExprCols f (BinApp op e1 e2) = BinApp op (mapExprCols f e1) (mapExprCols f e2)+mapExprCols f (UnApp op e)      = UnApp op (mapExprCols f e)+mapExprCols f (Column c)        = Column $ f c+mapExprCols _ (Constant val)    = Constant val+mapExprCols f (If c t e)        = If (mapExprCols f c) +                                     (mapExprCols f t) +                                     (mapExprCols f e)
+ src/Database/DSH/Optimizer/VL/Rewrite/Expressions.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE TemplateHaskell  #-}+{-# LANGUAGE ParallelListComp #-}++module Database.DSH.Optimizer.VL.Rewrite.Expressions where++-- This module contains rewrites which aim to simplify and merge complex expressions+-- which are expressed through multiple operators.++import Control.Monad+import Control.Applicative+import Data.Maybe++import Database.Algebra.Dag.Common++import Database.DSH.VL.Lang+import Database.DSH.Optimizer.Common.Rewrite+import Database.DSH.Optimizer.VL.Properties.Types+import Database.DSH.Optimizer.VL.Rewrite.Common++optExpressions :: VLRewrite Bool+optExpressions = iteratively $ applyToAll inferBottomUp expressionRules++expressionRules :: VLRuleSet BottomUpProps+expressionRules = [ mergeExpr1+                  , identityProject+                  , mergeSelectProject+                  ]++mergeExpr1 :: VLRule BottomUpProps+mergeExpr1 q =+  $(dagPatMatch 'q "Project es1 (Project es2 (q1))"+    [| do++        return $ do+          logRewrite "Expr.Merge.11" q+          let env  = zip [1..] $(v "es2")+              es1' = map (mergeExpr env) $(v "es1")+          void $ replaceWithNew q $ UnOp (Project es1') $(v "q1") |])++mergeSelectProject :: VLRule BottomUpProps+mergeSelectProject q =+  $(dagPatMatch 'q "R1 (qs=Select p (Project projs (q1)))"+     [| do+        return $ do+          logRewrite "Expr.Merge.Select" q+          let env = zip [1..] $(v "projs")+          let p'  = mergeExpr env $(v "p")+          selectNode <- insert $ UnOp (Select p') $(v "q1")+          r1Node     <- insert $ UnOp R1 selectNode+          void $ replaceWithNew q $ UnOp (Project $(v "projs")) r1Node++          r2Parents <- lookupR2Parents $(v "qs")++          -- If there are any R2 nodes linking to the original+          -- Restrict operator (i.e. there are inner vectors to which+          -- changes must be propagated), they have to be rewired to+          -- the new Select operator.+          when (not $ null r2Parents) $ do+            qr2' <- insert $ UnOp R2 selectNode+            mapM_ (\qr2 -> replace qr2 qr2') r2Parents |])++identityProject :: VLRule BottomUpProps+identityProject q =+  $(dagPatMatch 'q "Project ps (q1)"+    [| do+        VProp (ValueVector w) <- vectorTypeProp <$> properties $(v "q1")+        predicate $ length $(v "ps") == w++        let sameCol :: (Int, Expr) -> Bool+            sameCol (i, Column i') = i == i'+            sameCol _               = False++        predicate $ all sameCol (zip [1..] $(v "ps"))++        rs <- getRootNodes+        predicate $ not $ q `elem` rs++        return $ do+          logRewrite "Project.Identity" q+          replace q $(v "q1") |])++------------------------------------------------------------------------------+-- Constant expression inputs++liftPairRight :: Monad m => (a, m b) -> m (a, b)+liftPairRight (a, mb) = mb >>= \b -> return (a, b)++mapPair :: (a -> c) -> (b -> d) -> (a, b) -> (c, d)+mapPair f g (a, b) = (f a, g b)++insertConstants :: [(DBCol, VLVal)] -> Expr -> Expr+insertConstants env expr =+    case expr of+        BinApp o e1 e2 -> BinApp o (insertConstants env e1) (insertConstants env e2)+        UnApp o e1     -> UnApp o (insertConstants env e1)+        Column c       -> case lookup c env of+                               Just val -> Constant val+                               Nothing  -> Column c+        If c t e       -> If (insertConstants env c) (insertConstants env t) (insertConstants env e)+        Constant _     -> expr++constProject :: VLRule BottomUpProps+constProject q =+  $(dagPatMatch 'q "Project projs (q1)"+    [| do+        VProp (DBVConst _ constCols) <- constProp <$> properties $(v "q1")+        let envEntry = liftPairRight . mapPair id (constVal id)+        let constEnv = mapMaybe envEntry $ zip [1..] constCols++        predicate $ not $ null constEnv++        let projs' = map (insertConstants constEnv) $(v "projs")++        -- To avoid rewriting loops, ensure that a change occured.+        predicate $ projs' /= $(v "projs")++        return $ do+          logRewrite "Expr.Project.Const" q+          void $ replaceWithNew q $ UnOp (Project projs') $(v "q1") |])
+ src/Database/DSH/Optimizer/VL/Rewrite/PruneEmpty.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Rewrite.PruneEmpty(pruneEmpty) where+       +import           Control.Applicative+import           Control.Monad++import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.Optimizer.VL.Rewrite.Common++import           Database.Algebra.Dag.Common+import           Database.DSH.VL.Lang++pruneEmpty :: VLRewrite Bool+pruneEmpty = applyToAll inferBottomUp emptyRules++emptyRules :: VLRuleSet BottomUpProps+emptyRules = [ emptyAppendLeftR1+             -- , emptyAppendLeftR2+             -- , emptyAppendLeftR3+             , emptyAppendRightR1+             -- , emptyAppendRightR2+             -- , emptyAppendRightR3+             ]++isEmpty :: AlgNode -> VLMatch BottomUpProps Bool+isEmpty q = do+  ps <- liftM emptyProp $ properties q+  case ps of+    VProp b -> return b+    x       -> error $ "PruneEmpty.isEmpty: non-vector input " ++ show x++{- If the left input is empty and the other is not, the resulting value vector+is simply the right input. -}+emptyAppendLeftR1 :: VLRule BottomUpProps+emptyAppendLeftR1 q =+  $(dagPatMatch 'q "R1 ((q1) [Append | AppendS] (q2))"+    [| do+        predicate =<< ((&&) <$> (isEmpty $(v "q1")) <*> (not <$> isEmpty $(v "q2")))++        return $ do+          logRewrite "Empty.Append.Left.R1" q+          replace q $(v "q2") |])++-- FIXME re-add rules when +{-+-- If the left input is empty, renaming will make the inner vector+-- empty as well.+emptyAppendLeftR2 :: VLRule BottomUpProps+emptyAppendLeftR2 q =+  $(dagPatMatch 'q "(R2 ((q1) Append (q2))) PropRename (qv)"+    [| do+        predicate =<< ((&&) <$> (isEmpty $(v "q1")) <*> (not <$> isEmpty $(v "q2")))++        VProp (ValueVector w) <- vectorTypeProp <$> properties $(v "qv")++        return $ do+          logRewrite "Empty.Append.Left.R2" q+          void $ replaceWithNew q (NullaryOp $ Empty w) |])++-- If the left input is empty, the rename vector for the right inner+-- vectors is simply identity+emptyAppendLeftR3 :: VLRule BottomUpProps+emptyAppendLeftR3 q = +  $(dagPatMatch 'q "(R3 ((q1) Append (q2))) PropRename (qv)" +    [| do +        predicate =<< ((&&) <$> (isEmpty $(v "q1")) <*> (not <$> isEmpty $(v "q2")))++        return $ do+          logRewrite "Empty.Append.Left.R3" q+          replace q $(v "qv") |])+-}++emptyAppendRightR1 :: VLRule BottomUpProps+emptyAppendRightR1 q =+  $(dagPatMatch 'q "R1 ((q1) [Append | AppendS] (q2))"+    [| do+        predicate =<< ((&&) <$> (isEmpty $(v "q2")) <*> (not <$> isEmpty $(v "q1")))+        return $ do+          logRewrite "Empty.Append.Right.R1" q+          replace q $(v "q1") |])++{-+-- If the right input is empty, renaming will make the inner vector+-- empty as well.+emptyAppendRightR3 :: VLRule BottomUpProps+emptyAppendRightR3 q =+  $(dagPatMatch 'q "(R3 ((q1) Append (q2))) PropRename (qv)"+    [| do+        predicate =<< ((&&) <$> (not <$> isEmpty $(v "q1")) <*> (isEmpty $(v "q2")))+        VProp (ValueVector w) <- vectorTypeProp <$> properties $(v "qv")++        return $ do+          logRewrite "Empty.Append.Right.R3" q+          void $ replaceWithNew q $ NullaryOp $ Empty w |])++-- If the right input is empty, the rename vector for the left inner+-- vectors is simply identity+emptyAppendRightR2 :: VLRule BottomUpProps+emptyAppendRightR2 q =+  $(dagPatMatch 'q "(R2 ((q1) Append (q2))) PropRename (qv)"+    [| do+        predicate =<< ((&&) <$> (isEmpty $(v "q2")) <*> (not <$> isEmpty $(v "q1")))+        return $ do+          logRewrite "Empty.Append.Right.R2" q+          void $ replace q $(v "qv") |])+-}
+ src/Database/DSH/Optimizer/VL/Rewrite/Redundant.hs view
@@ -0,0 +1,965 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Optimizer.VL.Rewrite.Redundant (removeRedundancy) where++import Debug.Trace++import           Control.Applicative+import           Control.Monad++import           Database.Algebra.Dag.Common++import           Database.DSH.Common.Lang+import           Database.DSH.Impossible+import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.Optimizer.VL.Properties.VectorType+import           Database.DSH.Optimizer.VL.Rewrite.Common+import           Database.DSH.Optimizer.VL.Rewrite.Expressions+import           Database.DSH.Optimizer.VL.Rewrite.Aggregation+import           Database.DSH.Optimizer.VL.Rewrite.Window+import           Database.DSH.VL.Lang++removeRedundancy :: VLRewrite Bool+removeRedundancy =+    iteratively $ sequenceRewrites [ cleanup+                                   , applyToAll noProps redundantRules+                                   , applyToAll inferBottomUp redundantRulesBottomUp+                                   , applyToAll inferProperties redundantRulesAllProps+                                   , groupingToAggregation+                                   ]++cleanup :: VLRewrite Bool+cleanup = iteratively $ sequenceRewrites [ optExpressions ]++redundantRules :: VLRuleSet ()+redundantRules = [ pullProjectPropRename+                 , pullProjectPropReorder+                 , pullProjectSelectPos1S+                 , pullProjectPropFilter+                 , pullProjectUnboxRename+                 , pullProjectAggrS+                 , scalarConditional+                 ]++redundantRulesBottomUp :: VLRuleSet BottomUpProps+redundantRulesBottomUp = [ cartProdConstant+                         , sameInputZip+                         , sameInputZipProject+                         , sameInputZipProjectLeft+                         , sameInputZipProjectRight+                         , zipProjectLeft+                         , zipProjectRight+                         , distLiftProjectLeft+                         , distLiftProjectRight+                         , distLiftNestProduct+                         , distLiftNestJoin+                         , distLiftStacked+                         , distLiftSelect+                         , alignedDistLift+                         , selectConstPos+                         , selectConstPosS+                         , zipConstLeft+                         , zipConstRight+                         , alignConstLeft+                         , alignConstRight+                         , zipZipLeft+                         , zipWinLeft+                         , zipWinRight+                         , zipWinRightPush+                         , zipUnboxScalarRight+                         , zipUnboxScalarLeft+                         , alignCartProdRight+                         , propProductCard1Right+                         , runningAggWin+                         , inlineWinAggrProject+                         , pullProjectNumber+                         , constDistLift+                         , nestJoinChain+                         , pullProjectUnboxScalarLeft+                         , pullProjectUnboxScalarRight+                         , pullProjectNestJoinLeft+                         , pullProjectNestJoinRight+                         , selectCartProd+                         ]++redundantRulesAllProps :: VLRuleSet Properties+redundantRulesAllProps = [ unreferencedDistLift+                         , firstValueWin+                         , notReqNumber+                         ]++--------------------------------------------------------------------------------+-- ++-- | Replace a 'CartProduct' operator with a projection if its right+-- input is constant and has cardinality one.+cartProdConstant :: VLRule BottomUpProps+cartProdConstant q =+  $(dagPatMatch 'q "R1 ((q1) CartProduct (q2))"+    [| do+        qvProps <- properties $(v "q2")++        VProp True              <- return $ card1Prop qvProps+        VProp (DBVConst _ cols) <- return $ constProp qvProps+        constProjs              <- mapM (constVal Constant) cols++        -- Preserve columns from the left input+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+        let proj = map Column [1..w1] ++ constProjs++        return $ do+          logRewrite "Redundant.CartProduct.Constant" q+          void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++unwrapConstVal :: ConstPayload -> VLMatch p VLVal+unwrapConstVal (ConstPL val) = return val+unwrapConstVal  NonConstPL   = fail "not a constant"++-- | If the left input of an distLift is constant, a normal projection+-- can be used because the DistLift operator keeps the shape of the right+-- input.+constDistLift :: VLRule BottomUpProps+constDistLift q =+  $(dagPatMatch 'q "R1 ((q1) DistLift (q2))"+    [| do +         VProp (DBVConst _ constCols) <- constProp <$> properties $(v "q1")+         VProp (ValueVector w)        <- vectorTypeProp <$> properties $(v "q2")+         constVals                    <- mapM unwrapConstVal constCols+         +         return $ do +              logRewrite "Redundant.Const.DistLift" q+              let proj = map Constant constVals ++ map Column [1..w]+              void $ replaceWithNew q $ UnOp (Project proj) $(v "q2") |])+       +-- | If a vector is distributed over an inner vector in a segmented+-- way, check if the vector's columns are actually referenced/required+-- downstream. If not, we can remove the DistLift altogether, as the+-- shape of the inner vector is not changed by DistLift.+unreferencedDistLift :: VLRule Properties+unreferencedDistLift q =+  $(dagPatMatch 'q  "R1 ((q1) DistLift (q2))"+    [| do+        VProp (Just reqCols)   <- reqColumnsProp <$> td <$> properties q+        VProp (ValueVector w1) <- vectorTypeProp <$> bu <$> properties $(v "q1")+        VProp (ValueVector w2) <- vectorTypeProp <$> bu <$> properties $(v "q2")++        -- Check that only columns from the right input are required+        predicate $ all (> w1) reqCols++        return $ do+          logRewrite "Redundant.Unreferenced.DistLift" q++          -- FIXME HACKHACKHACK+          let padProj = [ Constant $ VLInt 0xdeadbeef | _ <- [1..w1] ]+                        +++                        [ Column i | i <- [1..w2] ]++          void $ replaceWithNew q $ UnOp (Project padProj) $(v "q2") |])++-- | Remove a DistLift if the outer vector is aligned with a+-- NestProduct that uses the same outer vector.+distLiftNestProduct :: VLRule BottomUpProps+distLiftNestProduct q =+  $(dagPatMatch 'q "R1 ((qo) DistLift (R1 ((qo1) NestProduct (qi))))"+    [| do+        predicate $ $(v "qo") == $(v "qo1")++        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "qo")+        w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "qi")++        return $ do+            logRewrite "Redundant.DistLift.NestProduct" q+            -- Preserve the original schema+            let proj = map Column $ [1..w1] ++ [1..w1] ++ [w1+1..w1+w2]+            prodNode <- insert $ BinOp NestProduct $(v "qo") $(v "qi")+            r1Node   <- insert $ UnOp R1 prodNode+            void $ replaceWithNew q $ UnOp (Project proj) r1Node |])++-- | Remove a DistLift if the outer vector is aligned with a+-- NestJoin that uses the same outer vector.+distLiftNestJoin :: VLRule BottomUpProps+distLiftNestJoin q =+  $(dagPatMatch 'q "R1 ((qo) DistLift (R1 ((qo1) NestJoin p (qi))))"+    [| do+        predicate $ $(v "qo") == $(v "qo1")++        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "qo")+        w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "qi")++        return $ do+            logRewrite "Redundant.DistLift.NestJoin" q+            -- Preserve the original schema+            let proj = map Column $ [1..w1] ++ [1..w1] ++ [w1+1..w1+w2]+            prodNode <- insert $ BinOp (NestJoin $(v "p")) $(v "qo") $(v "qi")+            r1Node   <- insert $ UnOp R1 prodNode+            void $ replaceWithNew q $ UnOp (Project proj) r1Node |])++distLiftProjectLeft :: VLRule BottomUpProps+distLiftProjectLeft q =+  $(dagPatMatch 'q "R1 ((Project ps1 (q1)) DistLift (q2))"+    [| do+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+        w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q2")++        return $ do+          logRewrite "Redundant.DistLift.Project.Left" q+          -- Take the projection expressions from the left and the+          -- shifted columns from the right.+          let proj = $(v "ps1") ++ [ Column $ c + w1 | c <- [1 .. w2]]+          distNode <- insert $ BinOp DistLift $(v "q1") $(v "q2")+          r1Node   <- insert $ UnOp R1 distNode+          void $ replaceWithNew q $ UnOp (Project proj) r1Node |])++distLiftProjectRight :: VLRule BottomUpProps+distLiftProjectRight q =+  $(dagPatMatch 'q "R1 ((q1) DistLift (Project p2 (q2)))"+    [| do+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")++        return $ do+          logRewrite "Redundant.DistLift.Project.Right" q+          -- Take the columns from the left and the expressions from+          -- the right projection. Since expressions are applied after+          -- the zip, their column references have to be shifted.+          let proj = [Column c | c <- [1..w1]] ++ [ mapExprCols (+ w1) e | e <- $(v "p2") ]+          distNode <- insert $ BinOp DistLift $(v "q1") $(v "q2")+          r1Node   <- insert $ UnOp R1 distNode+          void $ replaceWithNew q $ UnOp (Project proj) r1Node |])++-- If the same outer vector is propagated twice to an inner vector,+-- one DistLift can be removed. Reasoning: DistLift does not change+-- the shape of the inner vector.+distLiftStacked :: VLRule BottomUpProps+distLiftStacked q =+  $(dagPatMatch 'q "R1 ((q1) DistLift (r1=R1 ((q11) DistLift (q2))))"+     [| do+         predicate $ $(v "q1") == $(v "q11")+         w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+         w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q2")++         return $ do+             logRewrite "Redundant.DistLift.Stacked" q+             let proj = map Column $ [1..w1] ++ [1..w1] ++ [w1+1..w1+w2]+             void $ replaceWithNew q $ UnOp (Project proj) $(v "r1") |])++-- | Pull a selection through a DistLift. The reasoning for+-- correctness is simple: It does not matter wether an element of an+-- inner segment is removed before or after DistLift (on relational+-- level, DistLift maps to join which commutes with selection). The+-- "use case" for this rewrite is not well thought-through yet: We+-- want to push down DistLift to eliminate it or merge it with other+-- operators (e.g. DistLift.Stacked). The usual wisdom would suggest+-- to push selections down, though.+distLiftSelect :: VLRule BottomUpProps+distLiftSelect q =+  $(dagPatMatch 'q "R1 ((q1) DistLift (R1 (Select p (q2))))"+     [| do+         w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+         return $ do+             logRewrite "Redundant.DistLift.Select" q+             let p' = shiftExprCols w1 $(v "p")+             distNode <- insert $ BinOp DistLift $(v "q1") $(v "q2")+             distR1   <- insert $ UnOp R1 distNode+             selNode  <- insert $ UnOp (Select p') distR1+             void $ replaceWithNew q $ UnOp R1 selNode |])++-- | When a DistLift result is aligned with the right (inner) DistLift+-- input, we can eliminate the Align. Reasoning: DistLift does not+-- change the shape of the vector, only adds columns from its right+-- input.+alignedDistLift :: VLRule BottomUpProps+alignedDistLift q =+  $(dagPatMatch 'q "(q21) Align (qr1=R1 ((q1) DistLift (q22)))"+    [| do+        predicate $ $(v "q21") == $(v "q22")+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+        w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q21")+        +        return $ do+            logRewrite "Redundant.DistLift.Align" q+            let proj = map Column $+                       [w1+1..w1+w2]+                       +++                       [1..w1]+                       +++                       [w1+1..w1+w2]+            void $ replaceWithNew q $ UnOp (Project proj) $(v "qr1") |])++--------------------------------------------------------------------------------+-- Zip and Align rewrites. ++-- Note that the rewrites valid for Zip are a subset of the rewrites+-- valid for Align. In the case of Align, we statically know that both+-- inputs have the same length and can be positionally aligned without+-- discarding elements.++-- | Replace a Zip operator with a projection if both inputs are the+-- same.+sameInputZip :: VLRule BottomUpProps+sameInputZip q =+  $(dagPatMatch 'q "(q1) [Zip | Align] (q2)"+    [| do+        predicate $ $(v "q1") == $(v "q2")+        w <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")++        return $ do+          logRewrite "Redundant.Zip/Align.Self" q+          let ps = map Column [1 .. w]+          void $ replaceWithNew q $ UnOp (Project (ps ++ ps)) $(v "q1") |])++sameInputZipProject :: VLRule BottomUpProps+sameInputZipProject q =+  $(dagPatMatch 'q "(Project ps1 (q1)) [Zip | Align] (Project ps2 (q2))"+    [| do+        predicate $ $(v "q1") == $(v "q2")++        return $ do+          logRewrite "Redundant.Zip/Align.Self.Project" q+          void $ replaceWithNew q $ UnOp (Project ($(v "ps1") ++ $(v "ps2"))) $(v "q1") |])++sameInputZipProjectLeft :: VLRule BottomUpProps+sameInputZipProjectLeft q =+  $(dagPatMatch 'q "(Project ps1 (q1)) [Zip | Align] (q2)"+    [| do+        predicate $ $(v "q1") == $(v "q2")+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")++        return $ do+          logRewrite "Redundant.Zip/Align.Self.Project.Left" q+          let proj = $(v "ps1") ++ (map Column [1..w1])+          void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++sameInputZipProjectRight :: VLRule BottomUpProps+sameInputZipProjectRight q =+  $(dagPatMatch 'q "(q1) [Zip | Align] (Project ps2 (q2))"+    [| do+        predicate $ $(v "q1") == $(v "q2")+        w <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")++        return $ do+          logRewrite "Redundant.Zip/Align.Self.Project.Right" q+          let proj = (map Column [1 .. w]) ++ $(v "ps2")+          void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++zipProjectLeft :: VLRule BottomUpProps+zipProjectLeft q =+  $(dagPatMatch 'q "(Project ps1 (q1)) [Zip | Align]@op (q2)"+    [| do+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")+        w2 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q2")++        return $ do+          logRewrite "Redundant.Zip/Align.Project.Left" q+          -- Take the projection expressions from the left and the+          -- shifted columns from the right.+          let proj = $(v "ps1") ++ [ Column $ c + w1 | c <- [1 .. w2]]+          zipNode <- insert $ BinOp $(v "op") $(v "q1") $(v "q2")+          void $ replaceWithNew q $ UnOp (Project proj) zipNode |])++zipProjectRight :: VLRule BottomUpProps+zipProjectRight q =+  $(dagPatMatch 'q "(q1) [Zip | Align]@op (Project p2 (q2))"+    [| do+        w1 <- liftM (vectorWidth . vectorTypeProp) $ properties $(v "q1")++        return $ do+          logRewrite "Redundant.Zip/Align.Project.Right" q+          -- Take the columns from the left and the expressions from+          -- the right projection. Since expressions are applied after+          -- the zip, their column references have to be shifted.+          let proj = [Column c | c <- [1..w1]] ++ [ mapExprCols (+ w1) e | e <- $(v "p2") ]+          zipNode <- insert $ BinOp $(v "op") $(v "q1") $(v "q2")+          void $ replaceWithNew q $ UnOp (Project proj) zipNode |])++fromConst :: Monad m => ConstPayload -> m VLVal+fromConst (ConstPL val) = return val+fromConst NonConstPL    = fail "not a constant"++-- | This rewrite is valid because we statically know that both+-- vectors have the same length.+alignConstLeft :: VLRule BottomUpProps+alignConstLeft q =+  $(dagPatMatch 'q "(q1) Align (q2)"+    [| do+        VProp (DBVConst _ ps) <- constProp <$> properties $(v "q1")+        w2                    <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++        vals                  <- mapM fromConst ps++        return $ do+            logRewrite "Redundant.Align.Constant.Left" q+            let proj = map Constant vals ++ map Column [1..w2]+            void $ replaceWithNew q $ UnOp (Project proj) $(v "q2") |])++alignConstRight :: VLRule BottomUpProps+alignConstRight q =+  $(dagPatMatch 'q "(q1) Align (q2)"+    [| do+        w1                    <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        VProp (DBVConst _ ps) <- constProp <$> properties $(v "q2")+++        vals                  <- mapM fromConst ps++        return $ do+            logRewrite "Redundant.Align.Constant.Right" q+            let proj = map Column [1..w1] ++ map Constant vals+            void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++-- | In contrast to the 'Align' version ('alignConstLeft') this+-- rewrite is only valid if we can statically determine that both+-- input vectors have the same length. If the constant vector was+-- shorter, overhanging elements from the non-constant vector would+-- need to be discarded. In general, we can only determine equal+-- length for the special case of length one.+zipConstLeft :: VLRule BottomUpProps+zipConstLeft q =+  $(dagPatMatch 'q "(q1) Zip (q2)"+    [| do+        prop1                 <- properties $(v "q1")+        VProp card1           <- return $ card1Prop prop1+        VProp (DBVConst _ ps) <- return $ constProp prop1++        prop2                 <- properties $(v "q2")+        VProp card2           <- return $ card1Prop prop2+        w2                    <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++        vals                  <- mapM fromConst ps+        predicate $ card1 && card2++        return $ do+            logRewrite "Redundant.Zip.Constant.Left" q+            let proj = map Constant vals ++ map Column [1..w2]+            void $ replaceWithNew q $ UnOp (Project proj) $(v "q2") |])++zipConstRight :: VLRule BottomUpProps+zipConstRight q =+  $(dagPatMatch 'q "(q1) Zip (q2)"+    [| do+        prop1                 <- properties $(v "q1")+        VProp card1           <- return $ card1Prop prop1+        w1                    <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        prop2                 <- properties $(v "q2")+        VProp card2           <- return $ card1Prop prop2+        VProp (DBVConst _ ps) <- return $ constProp prop2+++        vals                  <- mapM fromConst ps+        predicate $ card1 && card2++        return $ do+            logRewrite "Redundant.Zip.Constant.Right" q+            let proj = map Column [1..w1] ++ map Constant vals+            void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++zipZipLeft :: VLRule BottomUpProps+zipZipLeft q =+  $(dagPatMatch 'q "(q1) Zip (qz=(q11) [Zip | Align] (_))"+     [| do+         predicate $ $(v "q1") == $(v "q11")++         w1 <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+         wz <- vectorWidth <$> vectorTypeProp <$> properties $(v "qz")+        +         return $ do+             logRewrite "Redundant.Zip/Align.Zip.Left" q+             let proj = map Column $ [1..w1] ++ [1..wz]+             void $ replaceWithNew q $ UnOp (Project proj) $(v "qz") |])++zipWinRight :: VLRule BottomUpProps+zipWinRight q =+  $(dagPatMatch 'q "(q1) [Zip | Align] (qw=WinFun _ (q2))"+     [| do+         predicate $ $(v "q1") == $(v "q2")+         +         w <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+         +         return $ do+             logRewrite "Redundant.Zip.Self.Win.Right" q+             -- We get all columns from the left input. The WinAggr+             -- operator produces the input column followed the window+             -- function result.+             let proj = map Column $ [1 .. w] ++ [1 .. w] ++ [w+1]+             logGeneral ("zipWinRight " ++ show proj)+             void $ replaceWithNew q $ UnOp (Project proj) $(v "qw") |])++zipWinLeft :: VLRule BottomUpProps+zipWinLeft q =+  $(dagPatMatch 'q "(qw=WinFun _ (q1)) [Zip | Align] (q2)"+     [| do+         predicate $ $(v "q1") == $(v "q2")+         +         w <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+         +         return $ do+             logRewrite "Redundant.Zip.Self.Win.Left" q+             -- We get all input columns plus the window function+             -- output from the left. From the right we get all input+             -- columns.+             let proj = map Column $ [1 .. w] ++ [w+1] ++ [1 .. w]+             void $ replaceWithNew q $ UnOp (Project proj) $(v "qw") |])++isPrecedingFrameSpec :: FrameSpec -> Bool+isPrecedingFrameSpec fs =+    case fs of+        FAllPreceding -> True+        FNPreceding _ -> True++zipWinRightPush :: VLRule BottomUpProps+zipWinRightPush q =+  $(dagPatMatch 'q "(q1) Zip (WinFun args (q2))"+    [| do+        let (winFun, frameSpec) = $(v "args")+        predicate $ isPrecedingFrameSpec frameSpec+        w1 <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        return $ do+            logRewrite "Redundant.Zip.Win.Right" q+            zipNode <- insert $ BinOp Zip $(v "q1") $(v "q2")+            let winFun' = mapWinFun (mapExprCols (\c -> c + w1)) winFun+                args'   = (winFun', frameSpec)+            void $ replaceWithNew q $ UnOp (WinFun args') zipNode |])++-- | If singleton scalar elements in an inner vector (with singleton+-- segments) are unboxed using an outer vector and then zipped with+-- the same outer vector, we can eliminate the zip, because the+-- positional alignment is implicitly performed by the UnboxScalar+-- operator. We exploit the fact that UnboxScalar is only a+-- specialized join which nevertheless produces payload columns from+-- both inputs.+zipUnboxScalarRight :: VLRule BottomUpProps+zipUnboxScalarRight q = +  $(dagPatMatch 'q "(q11) Align (qu=(q12) UnboxScalar (q2))"+     [| do+         predicate $ $(v "q11") == $(v "q12")++         leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q11")+         rightWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++         return $ do+             logRewrite "Redundant.Align.UnboxScalar.Right" q+             ++             -- Keep the original schema intact by duplicating columns+             -- from the left input (UnboxScalar produces columns from+             -- its left and right inputs).+             let outputCols = -- Two times the left input columns+                              [1..leftWidth] ++ [1..leftWidth] +                              -- Followed by the right input columns+                              ++ [ leftWidth+1..rightWidth+leftWidth ]+                 proj       = map Column outputCols++             -- Keep only the unboxing operator, together with a+             -- projection that keeps the original output schema+             -- intact.+             void $ replaceWithNew q $ UnOp (Project proj) $(v "qu") |])++-- | See Align.UnboxScalar.Right+zipUnboxScalarLeft :: VLRule BottomUpProps+zipUnboxScalarLeft q = +  $(dagPatMatch 'q "(qu=(q11) UnboxScalar (q2)) Align (q12)"+     [| do+         predicate $ $(v "q11") == $(v "q12")++         leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q11")+         rightWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++         return $ do+             logRewrite "Redundant.Align.UnboxScalar.Left" q+             ++             -- Keep the original schema intact by duplicating columns+             -- from the left input (UnboxScalar produces columns from+             -- its left and right inputs).+             let outputCols = -- The left (outer) columns+                              [1..leftWidth]+                              -- Followed by the right (inner) input columns+                              ++ [ leftWidth+1..rightWidth+leftWidth ]+                              -- Followed by the left (outer columns) again+                              -- (originally produced by Align)+                              ++ [1..leftWidth]+                 proj       = map Column outputCols++             -- Keep only the unboxing operator, together with a+             -- projection that keeps the original output schema+             -- intact.+             void $ replaceWithNew q $ UnOp (Project proj) $(v "qu") |])++-- | A CartProduct output is aligned with some other vector. If one of+-- the CartProduct inputs has cardinality one, the other CartProduct+-- input determines the length of the result vector. From the original+-- structure we can derive that 'q11' and the CartProduct result are+-- aligned. Consequentially, 'q11 and 'q12' (the left CartProduct+-- input) must be aligned as well.+alignCartProdRight :: VLRule BottomUpProps+alignCartProdRight q =+  $(dagPatMatch 'q "(q11) Align (R1 ((q12) CartProduct (q2)))"+    [| do+        VProp True <- card1Prop <$> properties $(v "q2")+        return $ do+            logRewrite "Redundant.Align.CartProduct.Card1.Right" q+            alignNode <- insert $ BinOp Align $(v "q11") $(v "q12")+            prodNode  <- insert $ BinOp CartProduct alignNode $(v "q2")+            void $ replaceWithNew q $ UnOp R1 prodNode |])++--------------------------------------------------------------------------------+-- Scalar conditionals++-- | Under a number of conditions, a combination of Combine and Select+-- (Restrict) operators implements a scalar conditional that can be+-- simply mapped to an 'if' expression evaluated on the input vector.+scalarConditional :: VLRule ()+scalarConditional q =+  $(dagPatMatch 'q "R1 (Combine (Project predProj (q1)) (Project thenProj (R1 (Select pred2 (q2)))) (Project elseProj (R1 (Select negPred (q3)))))"+    [| do+        -- All branches must work on the same input vector+        predicate $ $(v "q1") == $(v "q2") && $(v "q1") == $(v "q3")++        -- The condition projection as well as the projections for+        -- then and else branches must produce single columns.+        [predExpr] <- return $(v "predProj")+        [thenExpr] <- return $(v "thenProj")+        [elseExpr] <- return $(v "elseProj")++        -- The condition for the boolean vector must be the same as+        -- the selection condition for the then-branch.+        predicate $ predExpr == $(v "pred2")++        -- The selection condition must be the negated form of the+        -- then-condition.+        predicate $ (UnApp (SUBoolOp Not) predExpr) == $(v "negPred")++        return $ do+          logRewrite "Redundant.ScalarConditional" q+          void $ replaceWithNew q $ UnOp (Project [If predExpr thenExpr elseExpr]) $(v "q1") |])++------------------------------------------------------------------------------+-- Projection pullup++inlineJoinPredLeft :: [(DBCol, Expr)] -> JoinPredicate Expr -> JoinPredicate Expr+inlineJoinPredLeft env (JoinPred conjs) = JoinPred $ fmap inlineLeft conjs+  where+    inlineLeft :: JoinConjunct Expr -> JoinConjunct Expr+    inlineLeft (JoinConjunct le op re) = JoinConjunct (mergeExpr env le) op re++inlineJoinPredRight :: [(DBCol, Expr)] -> JoinPredicate Expr -> JoinPredicate Expr+inlineJoinPredRight env (JoinPred conjs) = JoinPred $ fmap inlineRight conjs+  where+    inlineRight :: JoinConjunct Expr -> JoinConjunct Expr+    inlineRight (JoinConjunct le op re) = JoinConjunct le op (mergeExpr env re)++pullProjectNestJoinLeft :: VLRule BottomUpProps+pullProjectNestJoinLeft q =+  $(dagPatMatch 'q "R1 ((Project proj (q1)) NestJoin p (q2))"+    [| do+        leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+        rightWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++        return $ do+            logRewrite "Redundant.Project.NestJoin.Left" q+            let proj' = $(v "proj") ++ map Column [leftWidth + 1 .. leftWidth + rightWidth]+                p'    = inlineJoinPredLeft (zip [1..] $(v "proj")) $(v "p")++            joinNode <- insert $ BinOp (NestJoin p') $(v "q1") $(v "q2")+            r1Node   <- insert $ UnOp R1 joinNode+            void $ replaceWithNew q $ UnOp (Project proj') r1Node++            -- FIXME relink R2 and R3 parents +            |])++pullProjectNestJoinRight :: VLRule BottomUpProps+pullProjectNestJoinRight q =+  $(dagPatMatch 'q "R1 ((q1) NestJoin p (Project proj (q2)))"+    [| do+        leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        return $ do+            logRewrite "Redundant.Project.NestJoin.Right" q+            let proj' = map Column [1..leftWidth] ++ map (shiftExprCols leftWidth) $(v "proj")+                p'    = inlineJoinPredRight (zip [1..] $(v "proj")) $(v "p")++            joinNode <- insert $ BinOp (NestJoin p') $(v "q1") $(v "q2")+            r1Node   <- insert $ UnOp R1 joinNode+            void $ replaceWithNew q $ UnOp (Project proj') r1Node++            -- FIXME relink R2 and R3 parents +            |])+        ++pullProjectNumber :: VLRule BottomUpProps+pullProjectNumber q =+  $(dagPatMatch 'q "Number (Project proj (q1))"+    [| do+         w <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++         return $ do+             logRewrite "Redundant.Project.Number" q++             -- We have to preserve the numbering column in the+             -- pulled-up projection.+             let proj' = $(v "proj") ++ [Column $ w + 1]+             numberNode <- insert $ UnOp Number $(v "q1")+             void $ replaceWithNew q $ UnOp (Project proj') numberNode |])++-- Motivation: In order to eliminate or pull up sorting operations in+-- VL rewrites or subsequent stages, payload columns which might+-- induce sort order should be available as long as possible. We+-- assume that the cost of having unrequired columns around is+-- negligible (best case: column store).++pullProjectPropRename :: VLRule ()+pullProjectPropRename q =+  $(dagPatMatch 'q "(qp) PropRename (Project proj (qv))"+    [| do+         return $ do+           logRewrite "Redundant.Project.PropRename" q+           renameNode <- insert $ BinOp PropRename $(v "qp") $(v "qv")+           void $ replaceWithNew q $ UnOp (Project $(v "proj")) renameNode |])++pullProjectUnboxScalarLeft :: VLRule BottomUpProps+pullProjectUnboxScalarLeft q =+  $(dagPatMatch 'q "(Project proj (q1)) UnboxScalar (q2)"+    [| do +         leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+         rightWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "q2")++         return $ do+           logRewrite "Redundant.Project.UnboxScalar" q++           -- Employ projection expressions on top of the unboxing+           -- operator, add right input columns.+           let proj' = $(v "proj") ++ map Column [ leftWidth + 1 .. leftWidth + rightWidth ]+           unboxNode <- insert $ BinOp UnboxScalar $(v "q1") $(v "q2")++           void $ replaceWithNew q $ UnOp (Project proj') unboxNode |])++pullProjectUnboxScalarRight :: VLRule BottomUpProps+pullProjectUnboxScalarRight q =+  $(dagPatMatch 'q "(q1) UnboxScalar (Project proj (q2))"+    [| do +         leftWidth  <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++         return $ do+           logRewrite "Redundant.Project.UnboxScalar" q++           -- Preserve left input columns on top of the unboxing+           -- operator and add right input expressions with shifted+           -- columns.+           let proj' = map Column [1..leftWidth]+                       +++                       [ mapExprCols (+ leftWidth) e | e <- $(v "proj") ]++           unboxNode <- insert $ BinOp UnboxScalar $(v "q1") $(v "q2")++           void $ replaceWithNew q $ UnOp (Project proj') unboxNode |])+    +pullProjectPropReorder :: VLRule ()+pullProjectPropReorder q =+  $(dagPatMatch 'q "R1 ((qp) PropReorder (Project proj (qv)))"+    [| do+         return $ do+           logRewrite "Redundant.Project.Reorder" q+           reorderNode <- insert $ BinOp PropReorder $(v "qp") $(v "qv")+           r1Node      <- insert $ UnOp R1 reorderNode+           void $ replaceWithNew q $ UnOp (Project $(v "proj")) r1Node |])++pullProjectSelectPos1S :: VLRule ()+pullProjectSelectPos1S q =+  $(dagPatMatch 'q "R1 (qs=SelectPos1S args (Project proj (q1)))"+    [| do+         return $ do+           logRewrite "Redundant.Project.SelectPos1S" q+           selectNode  <- insert $ UnOp (SelectPos1S $(v "args")) $(v "q1")+           r1Node      <- insert $ UnOp R1 selectNode+           void $ replaceWithNew q $ UnOp (Project $(v "proj")) r1Node |])++pullProjectPropFilter :: VLRule ()+pullProjectPropFilter q =+  $(dagPatMatch 'q "R1 ((q1) PropFilter (Project proj (q2)))"+    [| do+         return $ do+           logRewrite "Redundant.Project.PropFilter" q+           filterNode <- insert $ BinOp PropFilter $(v "q1") $(v "q2")+           r1Node     <- insert $ UnOp R1 filterNode+           void $ replaceWithNew q $ UnOp (Project $(v "proj")) r1Node |])++pullProjectUnboxRename :: VLRule ()+pullProjectUnboxRename q =+  $(dagPatMatch 'q "UnboxRename (Project _ (q1))"+    [| do+         return $ do+           logRewrite "Redundant.Project.UnboxRename" q+           void $ replaceWithNew q $ UnOp UnboxRename $(v "q1") |])++-- | Any projections on the left input of AggrS are irrelevant, as+-- only the segment information are required from the vector.+pullProjectAggrS :: VLRule ()+pullProjectAggrS q =+  $(dagPatMatch 'q "(Project _ (q1)) AggrS args (q2)"+    [| do+        return $ do+            logRewrite "Redundant.Project.AggrS" q+            void $ replaceWithNew q $ BinOp (AggrS $(v "args")) $(v "q1") $(v "q2") |])++--------------------------------------------------------------------------------+-- Positional selection on constants++selectConstPos :: VLRule BottomUpProps+selectConstPos q =+  $(dagPatMatch 'q "(q1) SelectPos op (qp)"+    [| do+         VProp (DBVConst _ constCols) <- constProp <$> properties $(v "qp")+         pos <- case constCols of+                    [ConstPL (VLInt p)] -> return p+                    [NonConstPL]        -> fail "no match"+                    _                   -> $impossible++         return $ do+           logRewrite "Redundant.SelectPos.Constant" q+           void $ replaceWithNew q $ UnOp (SelectPos1 ($(v "op"), pos)) $(v "q1") |])++selectConstPosS :: VLRule BottomUpProps+selectConstPosS q =+  $(dagPatMatch 'q "(q1) SelectPosS op (qp)"+    [| do+         VProp (DBVConst _ constCols) <- constProp <$> properties $(v "qp")+         pos <- case constCols of+                    [ConstPL (VLInt p)] -> return p+                    [NonConstPL]        -> fail "no match"+                    _                   -> $impossible++         return $ do+           logRewrite "Redundant.SelectPosS.Constant" q+           void $ replaceWithNew q $ UnOp (SelectPos1S ($(v "op"), pos)) $(v "q1") |])++--------------------------------------------------------------------------------+-- Rewrites that deal with nested structures and propagation vectors.++-- | When the right input of a cartesian product has cardinality one,+-- the cardinality of the right input does not change and the+-- propagation vector for the left input is a NOOP.+propProductCard1Right :: VLRule BottomUpProps+propProductCard1Right q =+  $(dagPatMatch 'q "R1 ((R2 ((_) CartProduct (q2))) PropReorder (qi))"+    [| do+        VProp True <- card1Prop <$> properties $(v "q2")+        +        return $ do+          logRewrite "Redundant.Prop.CartProduct.Card1.Right" q+          void $ replace q $(v "qi") |])++-- | Turn a right-deep nestjoin tree into a left-deep one.+-- +-- A comprehension of the form+-- @+-- [ [ [ e x y z | z <- zs, p2 y z ]+--   | y <- ys+--   , p1 x y+--   ]+-- | x <- xs+-- ]+-- @+-- +-- is first rewritten into a right-deep chain of nestjoins: 'xs △ (ys △ zs)'. +-- Bottom-up compilation of this expression to VL (vectorization) results in +-- a rather awkward plan, though: The inner nestjoin is computed independent+-- of values of 'x'. The join result is then re-shaped using the propagation+-- vector from the nestjoin of the outer relations 'xs' and 'ys'. This pattern+-- is problematic for multiple reasons: PropReorder is an expensive operation as +-- it involves re-ordering semantically, leading to a hard-to-eliminate rownum.+-- On the plan level, we do not get a left- or right-deep join tree of thetajoins,+-- but two independent joins between the two pairs of input relations whose results+-- are connected using an additional join (PropReorder). This means that the two+-- base joins will be executed on the full base tables, without being able to profit+-- from a reduced cardinality in one of the join results.+-- +-- NestJoin does not exhibit useful algebraic properties, most notably it is neither+-- associate nor commutative. It turns out however that we can turn the pattern+-- described above into a proper left-deep sequence of nestjoins if we consider+-- the flat (vectorized) representation. The output of 'xs △ ys' is nestjoined+-- with the innermost input 'zs'. This gives us exactly the representation of+-- the nested output that we need. Semantically, 'zs' is not joined with all+-- tuples in 'ys', but only with those that survive the (outer) join with 'xs'. +-- As usual, a proper join tree should give the engine the freedom to re-arrange +-- the joins and drive them in a pipelined manner.+nestJoinChain :: VLRule BottomUpProps+nestJoinChain q =+  $(dagPatMatch 'q "R1 ((R3 (lj=(xs) NestJoin _ (ys))) PropReorder (R1 ((ys1) NestJoin p (zs))))"+   [| do+       xsWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "xs")+       ysWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "ys")+       zsWidth <- vectorWidth <$> vectorTypeProp <$> properties $(v "zs")++       predicate $ $(v "ys") == $(v "ys1")+       return $ do+         logRewrite "Redundant.Prop.NestJoinChain" q+++         let innermostCols = map Column [ xsWidth + 1 .. xsWidth + ysWidth + zsWidth ]+  +             -- As the left input of the top nestjoin now includes the+             -- columns from xs, we have to shift column references in+             -- the left predicate side.+             JoinPred conjs = $(v "p")+             p' = JoinPred $ fmap (shiftJoinPredCols xsWidth 0) conjs++         -- The R1 node on the left nest join might already exist, but+         -- we simply rely on hash consing.+         leftJoinR1  <- insert $ UnOp R1 $(v "lj")+         rightJoin   <- insert $ BinOp (NestJoin p') leftJoinR1 $(v "zs")+         rightJoinR1 <- insert $ UnOp R1 rightJoin+  +         -- Because the original produced only the columns of ys and+         -- zs in the PropReorder output, we have to remove the xs+         -- columns from the top NestJoin.+         void $ replaceWithNew q $ UnOp (Project innermostCols) rightJoinR1 |])++shiftJoinPredCols :: Int -> Int -> JoinConjunct Expr -> JoinConjunct Expr+shiftJoinPredCols leftOffset rightOffset (JoinConjunct leftExpr op rightExpr) =+    JoinConjunct (shiftExprCols leftOffset leftExpr) op (shiftExprCols rightOffset rightExpr)++--------------------------------------------------------------------------------+-- Eliminating operators whose output is not required++notReqNumber :: VLRule Properties+notReqNumber q =+  $(dagPatMatch 'q "Number (q1)"+    [| do+        w <- vectorWidth <$> vectorTypeProp <$> bu <$> properties $(v "q1")+        VProp (Just reqCols) <- reqColumnsProp <$> td <$> properties $(v "q")++        -- The number output in column w + 1 must not be required+        predicate $ all (<= w) reqCols++        return $ do+          logRewrite "Redundant.Req.Number" q+          -- Add a dummy column instead of the number output to keep+          -- column references intact.+          let proj = map Column [1..w] ++ [Constant $ VLInt 0xdeadbeef]+          void $ replaceWithNew q $ UnOp (Project proj) $(v "q1") |])++--------------------------------------------------------------------------------+-- Classical relational algebra rewrites++-- | Merge a selection that refers to both sides of a cartesian+-- product operators' inputs into a join.+selectCartProd :: VLRule BottomUpProps+selectCartProd q =+  $(dagPatMatch 'q "R1 (Select p (R1 ((q1) CartProduct (q2))))"+    [| do+        wl <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")+        BinApp (SBRelOp op) (Column lc) (Column rc)  <- return $(v "p")+  +        -- The left operand column has to be from the left input, the+        -- right operand from the right input.+        predicate $ lc <= wl+        predicate $ rc > wl++        return $ do+            logRewrite "Redundant.Relational.Join" q+            let joinPred = singlePred $ JoinConjunct (Column lc) op (Column $ rc - wl)+            joinNode <- insert $ BinOp (ThetaJoin joinPred) $(v "q1") $(v "q2")+            void $ replaceWithNew q $ UnOp R1 joinNode |])
+ src/Database/DSH/Optimizer/VL/Rewrite/Unused.hs view
@@ -0,0 +1,48 @@+{-# LANGUAGE TemplateHaskell #-}++{- Based on the ReqColumns property, remove columns or entire operators which+produce value vectors but whose payload output is not needed downstream. This+is of course only sound if the operator in question does not change the vertical+layout.  -}++module Database.DSH.Optimizer.VL.Rewrite.Unused where++{-+import           Control.Applicative++import           Database.Algebra.Dag.Common+import           Database.Algebra.VL.Data++import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.Optimizer.VL.Rewrite.Common++pruneUnused :: VLRewrite Bool+pruneUnused = applyToAll inferTopDown [ {- unusedProject -} ]++-}++{-++FIXME seems a bit fishy++unusedProject :: VLRule TopDownProps+unusedProject q =+  $(pattern 'q "[ProjectL | Project] _ (q1)"+    [| do+      -- Don't remove top-level projections. They ensure that all required+      -- columns required for the result type are actually there.+      predicate =<< not <$> elem q <$> getRootNodes++      reqColumns <- reqColumnsProp <$> properties q+      +      case reqColumns of+        VProp (Just []) -> return ()+        VProp (Just _)  -> fail "no match"+        p               -> error ("Unused.Project: " ++ show p)+        ++      return $ do+        logRewrite "Unused.Project" q+        replace q $(v "q1") |])+-}
+ src/Database/DSH/Optimizer/VL/Rewrite/Window.hs view
@@ -0,0 +1,159 @@+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE TemplateHaskell #-}+module Database.DSH.Optimizer.VL.Rewrite.Window where++import           Control.Applicative+import           Control.Monad+import           Data.List.NonEmpty                              (NonEmpty (..))++import           Database.Algebra.Dag.Common++import           Database.DSH.Common.Lang+import           Database.DSH.Optimizer.Common.Rewrite+import           Database.DSH.Optimizer.VL.Properties.ReqColumns+import           Database.DSH.Optimizer.VL.Properties.Types+import           Database.DSH.Optimizer.VL.Properties.VectorType+import           Database.DSH.Optimizer.VL.Rewrite.Common+import           Database.DSH.VL.Lang++pattern SingleJoinPred e1 op e2 = JoinPred ((JoinConjunct e1 op e2) :| [])+pattern DoubleJoinPred e11 op1 e12 e21 op2 e22 = JoinPred ((JoinConjunct e11 op1 e12)+                                                           :|+                                                           [JoinConjunct e21 op2 e22])+pattern AddExpr e1 e2 = BinApp (SBNumOp Add) e1 e2+pattern SubExpr e1 e2 = BinApp (SBNumOp Sub) e1 e2++aggrToWinFun :: AggrFun -> WinFun+aggrToWinFun (AggrSum _ e) = WinSum e+aggrToWinFun (AggrMin e)   = WinMin e+aggrToWinFun (AggrMax e)   = WinMax e+aggrToWinFun (AggrAvg e)   = WinAvg e+aggrToWinFun (AggrAll e)   = WinAll e+aggrToWinFun (AggrAny e)   = WinAny e+aggrToWinFun AggrCount     = WinCount++-- Turn a running aggregate based on a self-join into a window operator.+runningAggWin :: VLRule BottomUpProps+runningAggWin q =+  $(dagPatMatch 'q "(qo) UnboxScalar ((_) AggrS afun (R1 ((qn=Number (q1)) NestJoin p (Number (q2)))))"+    [| do+        predicate $ $(v "q1") == $(v "q2")+        predicate $ $(v "qo") == $(v "qn")++        w <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        -- We require a range predicate on the positions generated by+        -- Number.+        -- FIXME allow other forms of window specifications+        SingleJoinPred (Column nrCol) GtE (Column nrCol') <- return $(v "p")+        predicate $ nrCol == w + 1 && nrCol' == w + 1++        -- The aggregate should only reference columns from the right+        -- ThetaJoin input, i.e. columns from the partition generated+        -- for a input tuple.+        let isWindowColumn c = c >= w + 2 && c <= 2 * w + 1+        predicate $ all isWindowColumn (aggrReqCols $(v "afun"))++        return $ do+            logRewrite "Window.RunningAggr" q+            -- Shift column references in aggregate functions so that+            -- they are applied to partition columns.+            let afun' = aggrToWinFun $ mapAggrFun (mapExprCols (\c -> c - (w + 1))) $(v "afun")+                +            void $ replaceWithNew q $ UnOp (WinFun (afun', FAllPreceding)) $(v "qn") |])++-- | Employ a window function that maps to SQL's first_value when the+-- 'head' combinator is employed on a nestjoin-generated window.+-- +-- FIXME this rewrite is currently extremely ugly and fragile: We map+-- directly to first_value which produces only one value, but start+-- with head one potentially broader inputs. To bring them into sync,+-- we demand that only one column is required downstream and produce+-- that column. This involves too much fiddling with column+-- offsets. It would be less dramatic if we had name-based columns+-- (which we should really do).+firstValueWin :: VLRule Properties+firstValueWin q =+  $(dagPatMatch 'q "(UnboxRename (Number (q1))) PropRename (R1 (SelectPos1S selectArgs (R1 ((Number (q2)) NestJoin joinPred (Number (q3))))))"+    [| do+        predicate $ $(v "q1") == $(v "q2") && $(v "q1") == $(v "q3")++        inputWidth <- vectorWidth <$> vectorTypeProp <$> bu <$> properties $(v "q1")+        resWidth   <- vectorWidth <$> vectorTypeProp <$> bu <$> properties $(v "q1")++        VProp (Just [resCol]) <- reqColumnsProp <$> td <$> properties $(v "q")++        -- Perform a sanity check (because this rewrite is rather+        -- insane): the required column must originate from the inner+        -- window created by the nestjoin and must not be the+        -- numbering column.+        predicate $ resCol > inputWidth + 1+        predicate $ resCol < 2 * inputWidth + 2+       +        -- The evaluation of first_value produces only a single value+        -- for each input column. To employ first_value, the input has+        -- to consist of a single column.++        -- We expect the VL representation of 'head'+        (SBRelOp Eq, 1) <- return $(v "selectArgs")+  +        -- We expect a window specification that for each element+        -- includes its predecessor (if there is one) and the element+        -- itself.+        DoubleJoinPred e11 op1 e12 e21 op2 e22                   <- return $(v "joinPred")+        (SubExpr (Column nrCol) frameOffset, LtE, Column nrCol') <- return (e11, op1, e12)+        (Column nrCol'', GtE, Column nrCol''')                   <- return (e21, op2, e22)+        Constant (VLInt offset)                                  <- return frameOffset++        -- Check that all (assumed) numbering columns are actually the+        -- column added by the Number operator.+        predicate $ all (== (inputWidth + 1)) [nrCol, nrCol', nrCol'', nrCol''']++        return $ do+            logRewrite "Window.FirstValue" q+            let -- The input column for FirstValue is the column in+                -- the inner window mapped to the input vector's+                -- layout.+                inputCol     = resCol - (inputWidth + 1)+                winArgs      = (WinFirstValue $ Column inputCol, (FNPreceding offset))+                placeHolders = repeat $ Constant $ VLInt 0xdeadbeef+  +                -- Now comes the ugly stuff: to keep the schema intact+                -- (since columns are referred to by offset), we have+                -- to keep columns that are not required in place and+                -- replace them with placeholders.+                proj         = -- Unreferenced columns in front of the+                               -- required column+                               take (resCol - 1) placeHolders +                               -- The required column (which is added+                               -- by WinFun to the input columns+                               ++ [Column (inputWidth + 1)]+                               -- Unrefeferenced columns after the+                               -- required column+                               ++ take (resWidth - resCol) placeHolders+            winNode <- insert $ UnOp (WinFun winArgs) $(v "q1")+            void $ replaceWithNew q $ UnOp (Project proj) winNode |])++inlineWinAggrProject :: VLRule BottomUpProps+inlineWinAggrProject q =+  $(dagPatMatch 'q "WinFun args (Project proj (q1))"+    [| do+        w <- vectorWidth <$> vectorTypeProp <$> properties $(v "q1")++        return $ do+            logRewrite "Window.RunningAggr.Project" q++            let (afun, frameSpec) = $(v "args")+                env               = zip [1..] $(v "proj")+                -- Inline column expressions from the projection into+                -- the window function.+                afun'             = mapWinFun (mergeExpr env) afun++                -- WinAggr /adds/ the window function output to the+                -- input columns. We have to provide the schema of the+                -- input projection to which the window function+                -- output is added.+                proj' = $(v "proj") ++ [Column $ w + 1]++            winNode <- insert $ UnOp (WinFun (afun', frameSpec)) $(v "q1") +            void $ replaceWithNew q $ UnOp (Project proj') winNode |])
− src/Database/DSH/TH.hs
@@ -1,453 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}--module Database.DSH.TH ( deriveDSH-                       , deriveQA-                       , deriveTupleRangeQA-                       , deriveTA-                       , deriveTupleRangeTA-                       , deriveView-                       , deriveTupleRangeView-                       , deriveElim-                       , deriveSmartConstructors-                       , deriveTupleRangeSmartConstructors-                       ) where--import qualified Database.DSH.Internals  as DSH-import qualified Database.DSH.Impossible as DSH--import Language.Haskell.TH-import Control.Monad-import Data.Char---------------------------------------------- Deriving all DSH-relevant instances ----------------------------------------------deriveDSH :: Name -> Q [Dec]-deriveDSH n = do-  qaDecs    <- deriveQA n-  elimDecs  <- deriveElim n-  cc        <- countConstructors n-  viewDecs  <- if cc == 1-                  then deriveView n-                  else return []-  scDecs    <- deriveSmartConstructors n-  return (qaDecs ++ elimDecs ++ viewDecs ++ scDecs)---------------------- Deriving QA ----------------------deriveQA :: Name -> Q [Dec]-deriveQA name = do-  info <- reify name-  case info of-    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->-      deriveTyConQA name1 tyVarBndrs cons-    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->-      deriveTyConQA name1 tyVarBndrs [con]-    _ -> fail errMsgExoticType--deriveTupleRangeQA :: Int -> Int -> Q [Dec]-deriveTupleRangeQA x y = fmap concat (mapM (deriveQA . tupleTypeName) [x .. y])--deriveTyConQA :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]-deriveTyConQA name tyVarBndrs cons = do-  let context       = map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)])-                          tyVarBndrs-  let typ           = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)-  let instanceHead  = AppT (ConT ''DSH.QA) typ-  let repDec        = deriveRep typ cons-  toExpDec <- deriveToExp cons-  frExpDec <- deriveFrExp cons-  return [InstanceD context instanceHead [repDec,toExpDec,frExpDec]]---- Deriving the Rep type function--deriveRep :: Type -> [Con] -> Dec-deriveRep typ cons = TySynInstD ''DSH.Rep [typ] (deriveRepCons cons)--deriveRepCons :: [Con] -> Type-deriveRepCons []  = error errMsgExoticType-deriveRepCons [c] = deriveRepCon c-deriveRepCons cs  = foldr1 (AppT . AppT (ConT ''(,)))-                           (map (AppT (ConT ''[]) . deriveRepCon) cs)--deriveRepCon :: Con -> Type-deriveRepCon con = case conToTypes con of-  [] -> ConT ''()-  ts -> foldr1 (AppT . AppT (ConT ''(,)))-               (map (AppT (ConT ''DSH.Rep)) ts)---- Deriving the toExp function of the QA class--deriveToExp :: [Con] -> Q Dec-deriveToExp [] = fail errMsgExoticType-deriveToExp cons = do-  clauses <- sequence (zipWith3 deriveToExpClause (repeat (length cons)) [0 .. ] cons)-  return (FunD 'DSH.toExp clauses)--deriveToExpClause :: Int -- Total number of constructors-                  -> Int -- Index of the constructor-                  -> Con-                  -> Q Clause-deriveToExpClause 0 _ _ = fail errMsgExoticType-deriveToExpClause 1 _ con = do-  (pat1,names1) <- conToPattern con-  let exp1 = deriveToExpMainExp names1-  let body1 = NormalB exp1-  return (Clause [pat1] body1 [])-deriveToExpClause n i con = do-  (pat1,names1) <- conToPattern con-  let exp1 = deriveToExpMainExp names1-  expList1 <- [| DSH.ListE [ $(return exp1) ] |]-  expEmptyList <- [| DSH.ListE [] |]-  let lists = concat [ replicate i expEmptyList-                     , [expList1]-                     , replicate (n - i - 1) expEmptyList]-  let exp2 = foldr1 (AppE . AppE (ConE 'DSH.PairE)) lists-  let body1 = NormalB exp2-  return (Clause [pat1] body1 [])--deriveToExpMainExp :: [Name] -> Exp-deriveToExpMainExp []     = ConE 'DSH.UnitE-deriveToExpMainExp [name] = AppE (VarE 'DSH.toExp) (VarE name)-deriveToExpMainExp names  = foldr1 (AppE . AppE (ConE 'DSH.PairE))-                                   (map (AppE (VarE 'DSH.toExp) . VarE) names)--- Deriving to frExp function of the QA class--deriveFrExp :: [Con] -> Q Dec-deriveFrExp cons = do-  clauses <- sequence (zipWith3 deriveFrExpClause (repeat (length cons)) [0 .. ] cons)-  imp <- DSH.impossible-  let lastClause = Clause [WildP] (NormalB imp) []-  return (FunD 'DSH.frExp (clauses ++ [lastClause]))--deriveFrExpClause :: Int -- Total number of constructors-                  -> Int -- Index of the constructor-                  -> Con-                  -> Q Clause-deriveFrExpClause 1 _ con = do-  (_,names1) <- conToPattern con-  let pat1 = deriveFrExpMainPat names1-  let exp1 = foldl AppE-                   (ConE (conToName con))-                   (map (AppE (VarE 'DSH.frExp) . VarE) names1)-  let body1 = NormalB exp1-  return (Clause [pat1] body1 [])-deriveFrExpClause n i con = do-  (_,names1) <- conToPattern con-  let pat1 = deriveFrExpMainPat names1-  let patList1 = ConP 'DSH.ListE [ConP '(:) [pat1,WildP]]-  let lists = replicate i WildP ++ [patList1] ++ replicate (n - i - 1) WildP-  let pat2 = foldr1 (\p1 p2 -> ConP 'DSH.PairE [p1,p2]) lists-  let exp1 = foldl AppE-                   (ConE (conToName con))-                   (map (AppE (VarE 'DSH.frExp) . VarE) names1)-  let body1 = NormalB exp1-  return (Clause [pat2] body1 [])--deriveFrExpMainPat :: [Name] -> Pat-deriveFrExpMainPat [] = ConP 'DSH.UnitE []-deriveFrExpMainPat [name] = VarP name-deriveFrExpMainPat names  = foldr1 (\p1 p2 -> ConP 'DSH.PairE [p1,p2]) (map VarP names)---------------------- Deriving TA ----------------------deriveTA :: Name -> Q [Dec]-deriveTA name = do-  info <- reify name-  case info of-    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->-      deriveTyConTA name1 tyVarBndrs cons-    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->-      deriveTyConTA name1 tyVarBndrs [con]-    _ -> fail errMsgExoticType--deriveTupleRangeTA :: Int -> Int -> Q [Dec]-deriveTupleRangeTA x y = fmap concat (mapM (deriveTA . tupleTypeName) [x .. y])--deriveTyConTA :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]-deriveTyConTA name tyVarBndrs _cons = do-  let context       = map (\tv -> ClassP ''DSH.BasicType [VarT (tyVarBndrToName tv)])-                          tyVarBndrs-  let typ           = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)-  let instanceHead  = AppT (ConT ''DSH.TA) typ-  return [InstanceD context instanceHead []]------------------------ Deriving View ------------------------deriveView :: Name -> Q [Dec]-deriveView name = do-  info <- reify name-  case info of-    TyConI (DataD    _cxt name1 tyVarBndrs [con] _names) ->-      deriveTyConView name1 tyVarBndrs con-    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->-      deriveTyConView name1 tyVarBndrs con-    _ -> fail errMsgExoticType--deriveTupleRangeView :: Int -> Int -> Q [Dec]-deriveTupleRangeView x y = fmap concat (mapM (deriveView . tupleTypeName) [x .. y])--deriveTyConView :: Name -> [TyVarBndr] -> Con -> Q [Dec]-deriveTyConView name tyVarBndrs con = do-  let context = map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)]) tyVarBndrs-  let typ1 = AppT (ConT ''DSH.Q)-                  (foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs))-  let instanceHead = AppT (ConT ''DSH.View) typ1-  let typs = conToTypes con-  let typ2 = if null typs-                then AppT (ConT ''DSH.Q) (ConT ''())-                else foldl AppT (TupleT (length typs)) (map (AppT (ConT ''DSH.Q)) typs)-  let toViewDecTF = TySynInstD ''DSH.ToView [typ1] typ2-  viewDec <- deriveToView (length typs)-  return [InstanceD context instanceHead [toViewDecTF, viewDec]]--deriveToView :: Int -> Q Dec-deriveToView n = do-  en <- newName "e"-  let ep = VarP en-  let pat1 = ConP 'DSH.Q [ep]--  let fAux 0  e1 = [AppE (ConE 'DSH.Q) e1]-      fAux 1  e1 = [AppE (ConE 'DSH.Q) e1]-      fAux n1 e1 = let fste = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Fst)) e1-                       snde = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Snd)) e1-                   in  AppE (ConE 'DSH.Q) fste : fAux (n1 - 1) snde--  let body1 = TupE (fAux n (VarE en))-  let clause1 = Clause [pat1] (NormalB body1) []-  return (FunD 'DSH.view [clause1])------------------------ Deriving Elim ------------------------deriveElim :: Name -> Q [Dec]-deriveElim name = do-  info <- reify name-  case info of-    TyConI (DataD    _cxt name1 tyVarBndrs cons _names) ->-      deriveTyConElim name1 tyVarBndrs cons-    TyConI (NewtypeD _cxt name1 tyVarBndrs con  _names) ->-      deriveTyConElim name1 tyVarBndrs [con]-    _ -> fail errMsgExoticType--deriveTyConElim :: Name -> [TyVarBndr] -> [Con] -> Q [Dec]-deriveTyConElim name tyVarBndrs cons = do-  resultTyName <- newName "r"-  let resTy = VarT resultTyName-  let ty = foldl AppT (ConT name) (map (VarT . tyVarBndrToName) tyVarBndrs)-  let context = ClassP ''DSH.QA [resTy] :-                map (\tv -> ClassP ''DSH.QA [VarT (tyVarBndrToName tv)]) tyVarBndrs-  let instanceHead = AppT (AppT (ConT ''DSH.Elim) ty) resTy-  let eliminatorDec = deriveEliminator ty resTy cons-  elimDec <- deriveElimFun cons-  return [InstanceD context instanceHead [eliminatorDec,elimDec]]---- Deriving the Eliminator type function--deriveEliminator :: Type -> Type -> [Con] -> Dec-deriveEliminator typ resTy cons =-  TySynInstD ''DSH.Eliminator [typ,resTy] (deriveEliminatorCons resTy cons)--deriveEliminatorCons :: Type -> [Con] -> Type-deriveEliminatorCons _ []  = error errMsgExoticType-deriveEliminatorCons resTy cs  =-  foldr (AppT . AppT ArrowT . deriveEliminatorCon resTy)-        (AppT (ConT ''DSH.Q) resTy)-        cs--deriveEliminatorCon :: Type -> Con -> Type-deriveEliminatorCon resTy con =-  foldr (AppT . AppT ArrowT . AppT (ConT ''DSH.Q))-        (AppT (ConT ''DSH.Q) resTy)-        (conToTypes con)---- Deriving the elim function of the Elim type class--deriveElimFun :: [Con] -> Q Dec-deriveElimFun cons = do-  clause1 <- deriveElimFunClause cons-  return (FunD 'DSH.elim [clause1])--deriveElimFunClause :: [Con] -> Q Clause-deriveElimFunClause cons = do-  en  <- newName "e"-  fns <- mapM (\ _ -> newName "f") cons-  let fes = map VarE fns-  let pats1 = ConP 'DSH.Q [VarP en] : map VarP fns--  fes2 <- zipWithM deriveElimToLamExp fes (map (length . conToTypes) cons)--  let e       = VarE en-  let liste   = AppE (ConE 'DSH.ListE) (ListE (deriveElimFunClauseExp e fes2))-  let concate = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Concat)) liste-  let heade   = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Head)) concate-  let qe      = AppE (ConE 'DSH.Q) heade-  return (Clause pats1 (NormalB qe) [])--deriveElimToLamExp :: Exp -> Int -> Q Exp-deriveElimToLamExp f 0 =-  return (AppE (VarE 'const) (AppE (VarE 'DSH.unQ) f))-deriveElimToLamExp f 1 = do-  xn <- newName "x"-  let xe = VarE xn-  let xp = VarP xn-  let qe = AppE (ConE 'DSH.Q) xe-  let fappe = AppE f qe-  let unqe = AppE (VarE 'DSH.unQ) fappe-  return (LamE [xp] unqe)-deriveElimToLamExp f n = do-  xn <- newName "x"-  let xe = VarE xn-  let xp = VarP xn-  let fste = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Fst)) xe-  let snde = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Snd)) xe-  let qe = AppE (ConE 'DSH.Q) fste-  let fappe = AppE f qe-  f' <- deriveElimToLamExp fappe (n - 1)-  return (LamE [xp] (AppE f' snde))--deriveElimFunClauseExp :: Exp -> [Exp] -> [Exp]-deriveElimFunClauseExp _ [] = error errMsgExoticType-deriveElimFunClauseExp e [f] = [AppE (ConE 'DSH.ListE) (ListE [AppE f e])]-deriveElimFunClauseExp e fs = go e fs-  where-  go :: Exp -> [Exp] -> [Exp]-  go _ []  = error errMsgExoticType-  go e1 [f1] =-    let paire = AppE (AppE (ConE 'DSH.PairE) (AppE (ConE 'DSH.LamE) f1)) e1-    in  [AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Map)) paire]-  go e1 (f1 : fs1) =-    let fste  = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Fst)) e1-        snde  = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Snd)) e1-        paire = AppE (AppE (ConE 'DSH.PairE) (AppE (ConE 'DSH.LamE) f1)) fste-        mape  = AppE (AppE (ConE 'DSH.AppE) (ConE 'DSH.Map)) paire-    in  mape : go snde fs1-------------------------------------- Deriving Smart Constructors --------------------------------------deriveSmartConstructors :: Name -> Q [Dec]-deriveSmartConstructors name = do-  info <- reify name-  case info of-    TyConI (DataD    _cxt typConName tyVarBndrs cons _names) -> do-      decss <- zipWithM (deriveSmartConstructor typConName tyVarBndrs (length cons))-                        [0 .. ]-                        cons-      return (concat decss)-    TyConI (NewtypeD _cxt typConName tyVarBndrs con  _names) ->-      deriveSmartConstructor typConName tyVarBndrs 1 0 con-    _ -> fail errMsgExoticType--deriveTupleRangeSmartConstructors :: Int -> Int -> Q [Dec]-deriveTupleRangeSmartConstructors x y =-  fmap concat (mapM (deriveSmartConstructors . tupleTypeName) [x .. y])--deriveSmartConstructor :: Name -> [TyVarBndr] -> Int -> Int -> Con -> Q [Dec]-deriveSmartConstructor typConName tyVarBndrs n i con = do-  let smartConName = toSmartConName (conToName con)--  let boundTyps = map (VarT . tyVarBndrToName) tyVarBndrs--  let resTyp = AppT (ConT ''DSH.Q) (foldl AppT (ConT typConName) boundTyps)--  let smartConContext = map (ClassP ''DSH.QA . return) boundTyps--  let smartConTyp = foldr (AppT . AppT ArrowT . AppT (ConT ''DSH.Q))-                          resTyp-                          (conToTypes con)--  let smartConDec = SigD smartConName (ForallT tyVarBndrs smartConContext smartConTyp)--  ns <- mapM (\_ -> newName "e") (conToTypes con)-  let es = map VarE ns--  let smartConPat = map (ConP 'DSH.Q . return . VarP) ns--  let smartConExp = if null es-                       then (ConE 'DSH.UnitE)-                       else foldr1 (AppE . AppE (ConE 'DSH.PairE)) es-  smartConBody <- deriveSmartConBody n i smartConExp-  let smartConClause = Clause smartConPat (NormalB smartConBody) []--  let funDec = FunD smartConName [smartConClause]--  return [smartConDec,funDec]--deriveSmartConBody :: Int -- Total number of constructors-                   -> Int -- Index of the constructor-                   -> Exp-                   -> Q Exp-deriveSmartConBody 0 _ _ = fail errMsgExoticType-deriveSmartConBody 1 _ e = return (AppE (ConE 'DSH.Q) e)-deriveSmartConBody n i e = do-  listExp <- [| DSH.ListE [ $(return e) ] |]-  emptyListExp <- [| DSH.ListE [] |]-  let lists = concat [ replicate i emptyListExp-                     , [listExp]-                     , replicate (n - i - 1) emptyListExp-                     ]-  let pairExp = foldr1 (AppE . AppE (ConE 'DSH.PairE)) lists-  return (AppE (ConE 'DSH.Q) pairExp)--toSmartConName :: Name -> Name-toSmartConName name1 = case nameBase name1 of-  "()"                -> mkName "unit"-  '(' : cs            -> mkName ("tuple" ++ show (length (filter (== ',') cs) + 1))-  c : cs | isAlpha c  -> mkName (toLower c : cs)-  cs                  -> mkName (':' : cs)---- Helper Functions--conToTypes :: Con -> [Type]-conToTypes (NormalC _name strictTypes) = map snd strictTypes-conToTypes (RecC _name varStrictTypes) = map (\(_,_,t) -> t) varStrictTypes-conToTypes (InfixC st1 _name st2) = [snd st1,snd st2]-conToTypes (ForallC _tyVarBndrs _cxt con) = conToTypes con--tyVarBndrToName :: TyVarBndr -> Name-tyVarBndrToName (PlainTV name) = name-tyVarBndrToName (KindedTV name _kind) = name--conToPattern :: Con -> Q (Pat,[Name])-conToPattern (NormalC name strictTypes) = do-  ns <- mapM (\ _ -> newName "x") strictTypes-  return (ConP name (map VarP ns),ns)-conToPattern (RecC name varStrictTypes) = do-  ns <- mapM (\ _ -> newName "x") varStrictTypes-  return (ConP name (map VarP ns),ns)-conToPattern (InfixC st1 name st2) = do-  ns <- mapM (\ _ -> newName "x") [st1,st2]-  return (ConP name (map VarP ns),ns)-conToPattern (ForallC _tyVarBndr _cxt con) = conToPattern con--conToName :: Con -> Name-conToName (NormalC name _) = name-conToName (RecC name _) = name-conToName (InfixC _ name _) = name-conToName (ForallC _ _ con)	= conToName con--countConstructors :: Name -> Q Int-countConstructors name = do-  info <- reify name-  case info of-    TyConI (DataD    _ _ _ cons _)  -> return (length cons)-    TyConI (NewtypeD {})            -> return 1-    _ -> fail errMsgExoticType---- Error messages--errMsgExoticType :: String-errMsgExoticType =-  "Automatic derivation of DSH related type class instances only works for Haskell 98\-   \ types. Derivation of View patters is only supported for single-constructor data\-   \ types."
+ src/Database/DSH/Tools/VLDotGen.hs view
@@ -0,0 +1,83 @@+module Main where++import System.IO+import System.Exit+import System.Environment+import System.Console.GetOpt++import Data.ByteString.Lazy.Char8 (pack)+  +import Data.Maybe++import Database.DSH.VL.Render.JSON+import Database.DSH.VL.Render.Dot+  +data Options = Options { optInput          :: IO String+                       , optReuse          :: Bool+                       , optRootNodes      :: Maybe [Int]+                       , optProperties      :: Bool+                       }+               +startOptions :: Options+startOptions = Options { optInput            = getContents+                       , optReuse            = False+                       , optRootNodes        = Nothing+                       , optProperties       = False+                       }+               +options :: [OptDescr (Options -> IO Options)]+options =+  [ Option "i" ["input"]+      (ReqArg (\arg opt -> return opt { optInput = readFile arg })+       "FILE")+      "Input file"+  , Option "n" ["rootnodes"]+      (ReqArg (\arg opt -> return opt { optRootNodes = Just $ read arg })+       "ROOTNODES")+      "List of root nodes to use (must be in Haskell list syntax)"+  , Option "p" ["properties"]+      (NoArg (\opt -> return opt { optProperties = True }))+      "Infer properties and display them" +  , Option "h" ["help"]+      (NoArg+         (\_ -> do +             prg <- getProgName+             hPutStrLn stderr (usageInfo prg options)+             exitWith ExitSuccess))+      "Show help"+  ]+  +{-+propertyTags :: [AlgNode] -> NodeMap X100Algebra -> NodeMap [Tag] -> NodeMap [Tag]+propertyTags rs nm tags = +  let dag = normalizePlan $ mkDag nm rs+      topsorted = topsort dag+      bu = inferBottomUpProperties topsorted dag+      td = inferTopDownProperties bu topsorted dag+      buDocs = M.map renderBottomUpProps bu+      tdDocs = M.map renderTopDownProps td+      tagDocs = M.map (vcat . ((map text) . nub)) tags+      propsRendered = M.map render $ M.unionWith ($$) tagDocs $ M.unionWith ($$) buDocs tdDocs+      in M.map (\s -> [s]) propsRendered+-}+         +main :: IO ()+main = do+    args <- getArgs +    let (actions, _, _) = getOpt RequireOrder options args+    opts <- foldl (>>=) (return startOptions) actions+    let Options { optInput = input+                , optRootNodes = mRootNodes } = opts+    +    plan <- input+    +    let (tags, rs, m) = deserializePlan $ pack plan+    +    let rs' = fromMaybe rs mRootNodes+    {-+        tags' = if printProperties+                then propertyTags rs' m tags+                else tags+-}+    +    putStr $ renderVLDot tags rs' m
+ src/Database/DSH/Translate/Algebra2Query.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.Translate.Algebra2Query +    ( generateSqlQueries+    ) where++import           Database.DSH.Impossible++import           Database.Algebra.Dag+import           Database.Algebra.SQL.Compatibility+import           Database.Algebra.SQL.Materialization.CTE+import           Database.Algebra.SQL.Util+import           Database.Algebra.Table.Lang++import           Database.DSH.Common.QueryPlan+import           Database.DSH.Execute.Sql+import           Database.DSH.VL.Vector++-- | In a query shape, render each root node for the algebraic plan+-- into a separate SQL query.++-- FIXME use materialization "prelude"+generateSqlQueries :: QueryPlan TableAlgebra NDVec -> Shape (BackendCode SqlBackend)+generateSqlQueries taPlan = renderQueryCode $ queryShape taPlan+  where+    roots = rootNodes $ queryDag taPlan+    (_sqlShared, sqlQueries) = renderOutputDSHWith PostgreSQL materialize (queryDag taPlan)+    nodeToQuery  = zip roots sqlQueries+    lookupNode n = maybe $impossible SqlCode $ lookup n nodeToQuery++    renderQueryCode :: Shape NDVec -> Shape (BackendCode SqlBackend)+    renderQueryCode shape =+        case shape of+            SShape (ADVec r _) lyt -> SShape (lookupNode r) (convertLayout lyt)+            VShape (ADVec r _) lyt -> VShape (lookupNode r) (convertLayout lyt)++    convertLayout :: Layout NDVec -> Layout (BackendCode SqlBackend)+    convertLayout lyt =+        case lyt of+            LCol i                 -> LCol i+            LNest (ADVec r _) clyt -> LNest (lookupNode r) (convertLayout clyt)+            LTuple lyts            -> LTuple $ map convertLayout lyts
+ src/Database/DSH/Translate/CL2NKL.hs view
@@ -0,0 +1,383 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TemplateHaskell  #-}+{-# LANGUAGE BangPatterns     #-}++module Database.DSH.Translate.CL2NKL+  ( desugarComprehensions ) where++#ifdef DEBUGCOMP+import           Debug.Trace+import           Database.DSH.Common.Pretty+#endif++import           Data.List.NonEmpty          (NonEmpty(..))+import qualified Data.List.NonEmpty          as N+import qualified Data.Foldable               as F+import           Control.Monad.Reader+import           Control.Applicative+  +import           Database.DSH.Impossible+       +import           Database.DSH.Common.Type+import           Database.DSH.Common.Lang++import           Database.DSH.CL.Lang        (toList)+import qualified Database.DSH.CL.Lang        as CL+import qualified Database.DSH.NKL.Primitives as P+import qualified Database.DSH.NKL.Lang       as NKL+import           Database.DSH.NKL.Rewrite++--------------------------------------------------------------------------------+-- Conversion of primitive operators+       +prim1 :: Type -> CL.Prim1 -> CL.Expr -> NameEnv NKL.Expr+prim1 t p e = mkApp t <$> expr e+  where +    mkApp = +        case p of+            CL.Singleton        -> mkPrim1 NKL.Singleton+            CL.Length           -> mkPrim1 NKL.Length +            CL.Concat           -> mkPrim1 NKL.Concat +            -- Null in explicit form is useful during CL optimization+            -- to easily recognize universal/existential patterns. In+            -- backend implementations however, there currently is no+            -- need to store it explicitly. Therefore, we implement it+            -- using length in NKL.+            CL.Null             -> nklNull+            CL.Sum              -> mkPrim1 NKL.Sum +            CL.Avg              -> mkPrim1 NKL.Avg +            CL.The              -> mkPrim1 NKL.The +            CL.Head             -> mkPrim1 NKL.Head +            CL.Minimum          -> mkPrim1 NKL.Minimum +            CL.Maximum          -> mkPrim1 NKL.Maximum +            CL.Tail             -> mkPrim1 NKL.Tail +            CL.Reverse          -> mkPrim1 NKL.Reverse +            CL.And              -> mkPrim1 NKL.And +            CL.Or               -> mkPrim1 NKL.Or +            CL.Init             -> mkPrim1 NKL.Init +            CL.Last             -> mkPrim1 NKL.Last +            CL.Nub              -> mkPrim1 NKL.Nub +            CL.Number           -> mkPrim1 NKL.Number +            (CL.Reshape n)      -> mkPrim1 $ NKL.Reshape n+            CL.Transpose        -> mkPrim1 NKL.Transpose+            CL.TupElem i        -> mkPrim1 $ NKL.TupElem i+            CL.Guard            -> $impossible+    +    nklNull _ ne = NKL.BinOp boolT +                             (SBRelOp Eq)+                             (NKL.Const intT $ IntV 0)+                             (NKL.AppE1 intT NKL.Length ne)+                                       +    mkPrim1 nop nt ne = NKL.AppE1 nt nop ne+                                   ++-- | Transform applications of binary primitives. Regular primitives+-- are mapped to their direct NKL equivalent. Higher-order primitives+-- (concatMap, map, filter, sortWith, groupWith) are mapped to their+-- first-order NKL equivalent combined with a single-generator+-- comprehension.+prim2 :: Type -> CL.Prim2 -> CL.Expr -> CL.Expr -> NameEnv NKL.Expr+prim2 t o e1 e2 = mkApp2+  where+    mkApp2 =+        case o of+            CL.Append       -> mkPrim2 NKL.Append+            CL.Index        -> mkPrim2 NKL.Index +            CL.Zip          -> mkPrim2 NKL.Zip+            CL.CartProduct  -> mkPrim2 NKL.CartProduct+            CL.NestProduct  -> mkPrim2 NKL.NestProduct+            CL.ThetaJoin p  -> mkPrim2 $ NKL.ThetaJoin p+            CL.NestJoin p   -> mkPrim2 $ NKL.NestJoin p+            CL.SemiJoin p   -> mkPrim2 $ NKL.SemiJoin p+            CL.AntiJoin p   -> mkPrim2 $ NKL.AntiJoin p+            CL.Sort         -> mkPrim2 $ NKL.Sort+            CL.Group        -> mkPrim2 $ NKL.Group++    mkPrim2 :: NKL.Prim2 -> NameEnv NKL.Expr+    mkPrim2 nop = NKL.AppE2 t nop <$> expr e1 <*> expr e2++--------------------------------------------------------------------------------+-- Generator environments++-- | Access a component of a tuple variable+type TupleAccessor = Type -> Ident -> NKL.Expr++type EnvEntry = (Ident, Type, TupleAccessor)++-- | A generator environment stores generator variables that have+-- already been handled in the traversal of the qualifier list. For+-- each variable, we store it's type and an expression that projects+-- the variables' value out of the constructed tuple.+type GenEnv = N.NonEmpty EnvEntry+            +-- | Construct an environment from one generator variable+-- => (x, t, \n t -> Var t n)+mkEnv :: (Ident, Type) -> GenEnv+mkEnv (x, xt) = (x, xt, \n t -> NKL.Var n t) N.:| []++-- | Account for a new pair that has been added at the top of the+-- constructed tuple+updateEnvEntry :: EnvEntry -> EnvEntry+updateEnvEntry (x, t, ta) = (x, t, \n t' -> P.fst $ ta n t')++-- | Extend an environment with an additional generator variable.+extendEnv :: GenEnv -> (Ident, NKL.Expr) -> GenEnv+extendEnv entries (y, ys) =  entry N.<| fmap updateEnvEntry entries+  where+    entry = (y, elemT $ typeOf ys, \n t -> P.snd $ NKL.Var n t)++addGensToEnv :: NonEmpty (Ident, NKL.Expr) -> GenEnv -> GenEnv+addGensToEnv gens env = F.foldl' extendEnv env gens++--------------------------------------------------------------------------------+-- Conversion of CL expressions to NKL expressions++type NameEnv a = Reader [Ident] a++freshName :: NameEnv Ident+freshName = do+    boundNames <- ask+    return $ tryName 0 boundNames++  where+    tryName :: Int -> [Ident] -> Ident+    tryName i ns = if mkName i `elem` ns+                   then tryName (i + 1) ns+                   else mkName i++    mkName i = "f" ++ show i++-- | Map a CL expression to its NKL equivalent by desugaring all+-- comprehensions.+expr :: CL.Expr -> NameEnv NKL.Expr+expr (CL.MkTuple t es)           = NKL.MkTuple t <$> mapM expr es+expr (CL.Table t s cs ks)        = return $ NKL.Table t s cs ks+expr (CL.AppE1 t p e)            = prim1 t p e+expr (CL.AppE2 t p e1 e2)        = prim2 t p e1 e2+expr (CL.BinOp t o e1 e2)        = NKL.BinOp t o <$> expr e1 <*> expr e2+expr (CL.UnOp t o e)             = NKL.UnOp t o <$> expr e+expr (CL.If t c th el)           = NKL.If t <$> expr c <*> expr th <*> expr el+expr (CL.Lit t v)                = return $ NKL.Const t v+expr (CL.Var t v)                = return $ NKL.Var t v+expr (CL.Comp t e qs)            = desugarComprehension t e (toList qs)+expr (CL.Let t x e1 e2)          = NKL.Let t x <$> expr e1 <*> local (x :) (expr e2)++--------------------------------------------------------------------------------+-- Desugaring of comprehensions+--+-- We do not use a general desugaring scheme for monad comprehensions+-- but deal only with list comprehensions. The motivation for now is+-- to avoid inefficient patterns (e.g. the handling of guards via+-- 'if') already by construction.+-- +-- In the current qualifier list, we consider the longest prefix of+-- generators. The cartesian product of those generators is+-- computed. We compute the cartesian product using nested+-- concatMaps. This is necessary because a generator expression might+-- depend on a preceding generator variable. If a guard follows a+-- sequence of generators, it is turned into a filter applied to the+-- cartesian product of all preceding generators.+--+-- Example:+-- +-- [ e x y z | x <- xs, y <- ys, p1 x y, z <- zs, p2 y z ]+-- =>+-- map (\t -> e [x/fst (fst t)] [y/snd (fst t)] [z/snd t])+--     (filter (\t -> p2[y/snd (fst t)][z/snd t])+--             (concatMap (\t -> concatMap (\z -> [pair t z]) zs[x/fst t][y/snd t])+--                        (filter (\t -> p1[x/fst t][y/snd t])+--                                (concatMap (\t -> concatMap (\y -> pair t y) ys[x/t])+--                                           xs+     +-- | Split a qualifier list into a prefix of generators and the+-- remaining qualifiers.+takeGens :: [CL.Qual] -> ([(Ident, CL.Expr)], [CL.Qual])+takeGens (CL.BindQ x xs : qs) = let (binds, rest) = takeGens qs in ((x, xs) : binds, rest)+takeGens qs                   = ([], qs)++-- | Generate an identifier that does not occur in the list provided.+freshIdent :: [Ident] -> NameEnv Ident+freshIdent names = do+    visibleNames <- ask+    return $ checkCollision (0 :: Int) (names ++ visibleNames)+  where+    checkCollision i ns = if mkName i `elem` ns+                          then checkCollision (i + 1) ns+                          else mkName i++    mkName i = "v" ++ show i++-- | Construct a left-deep tuple from a list of expressions+mkTuple :: NonEmpty NKL.Expr -> NKL.Expr+mkTuple xs = F.foldl1 P.pair xs++-- | Produce the nested concatMaps from a sequence of generators. The+-- body of the innermost generator constructs the tuple of generator+-- variables.+-- x <- xs, y <- ys, z <- zs+-- =>+-- concatMap (\x -> concatMap (\y -> concatMap (\z -> (((t, x), y), z)) zs) ys) xs+-- where t is the binding variable for the base expression.+nestQualifiers :: NKL.Expr -> [(Ident, NKL.Expr)] -> NKL.Expr+nestQualifiers tupConst ((x, xs) : qs) = P.concat $ NKL.Iterator (listT bodyType) compHead x xs+  where+    compHead  = nestQualifiers tupConst qs+    bodyType = typeOf compHead+nestQualifiers tupConst []             = tupConst++-- | Desugar a sequence of generators. +desugarGens :: GenEnv -> NKL.Expr -> NonEmpty (Ident, NKL.Expr) -> NameEnv NKL.Expr+desugarGens env baseExpr qs = do+    -- Avoid all names that are bound by enclosing binders and the+    -- ones bound in the current generator list.+    visibleNames <- (++) (map fst $ N.toList qs) <$> ask+    +    -- Avoid all names that are bound in the generator expressions in+    -- which we will substitute.+    let boundNames = concatMap (boundVars . snd) $ N.toList qs+        avoidNames = boundNames ++ visibleNames++    outerName    <- freshIdent $ visibleNames ++ boundNames ++    let baseElemType   = elemT $ typeOf baseExpr+        +        -- Generator expressions might reference variables bound by+        -- preceding generators. These variables go out of scope during+        -- desugaring. To eliminate them, we have to replace references to+        -- generator variables in generator expressions by the appropriate+        -- tuple accessors for the outer concatMap variable.+        substGenExpr (n, e) = (n, substTupleAccesses avoidNames (outerName, baseElemType) env e)++    let qs'            = fmap substGenExpr qs++        tupConst       = P.sng $ mkTuple $ fmap mkVar ((outerName, baseExpr) N.<| qs')+        mkVar (x, xs)  = NKL.Var (elemT $ typeOf xs) x +        gensExpr       = nestQualifiers tupConst (N.toList qs')+        compTy         = (listT $ typeOf tupConst)+    return $ P.concat $ NKL.Iterator compTy gensExpr outerName baseExpr++-- | Replace every occurence of a generator variable with the+-- corresponding tuple access expression.+substTupleAccesses :: [Ident] -> (Ident, Type) -> GenEnv -> NKL.Expr -> NKL.Expr+substTupleAccesses visibleNames (n, t) env e = F.foldr substTupleAccess e env+  where+    substTupleAccess (x, _, xta) e' = subst (n : visibleNames) x (xta t n) e'++qualVar :: CL.Qual -> [Ident]+qualVar (CL.BindQ x _) = [x]+qualVar (CL.GuardQ _)  = []++-- | Transform a list of generator expressions to NKL+-- expressions. Every expression is transformed in the name+-- environment enriched with the current prefix of the generators.+genExprs :: NonEmpty (Ident, CL.Expr) -> NameEnv (NonEmpty (Ident, NKL.Expr))+genExprs ((n, e) :| [])       = do+    e' <- expr e+    return $ (n, e') :| []+genExprs ((n, e) :| (q : qs)) = do+    e'  <- expr e+    qs' <- local (n :) (genExprs $ q :| qs)+    return $ (n, e') N.<| qs'++-- | Desugar a list of qualifiers.+desugarQualsRec :: GenEnv -> NKL.Expr -> [CL.Qual] -> NameEnv (GenEnv, NKL.Expr)+-- If we encounter a generator, we produce the cartesian product of+-- the generator prefix of the current qualifier list.+desugarQualsRec env baseSrc (CL.BindQ x xs : qs) = do+    let (gens, remQuals) = takeGens qs+        genNames         = map fst gens+    nklGens  <- genExprs ((x, xs) :| gens)+    baseSrc' <- desugarGens env baseSrc nklGens+    let env' = addGensToEnv nklGens env ++    local (++ genNames) $ desugarQualsRec env' baseSrc' remQuals+                       +-- A guard is desugared by filtering the cartesian product of the+-- generators that have been encountered so far.+desugarQualsRec env baseSrc (CL.GuardQ p : qs)    = do+    p'           <- expr p+    visibleNames <- ask++    filterName   <- freshIdent $ visibleNames ++ boundVars p'+    srcName      <- freshName+    let srcVar = NKL.Var (typeOf baseSrc) srcName++    let elemType   = elemT $ typeOf baseSrc+        filterExpr = substTupleAccesses visibleNames (filterName, elemType) env p'+        predComp   = NKL.Iterator (listT boolT) filterExpr filterName srcVar+        filterSrc  = P.let_ srcName baseSrc (P.restrict srcVar predComp)++    desugarQualsRec env filterSrc qs++desugarQualsRec env baseSrc []                    = return (env, baseSrc)++-- | Kick off the recursive traversal of the qualifier list.+desugarQuals :: [CL.Qual] -> NameEnv (GenEnv, NKL.Expr, NKL.Expr -> NKL.Expr)+desugarQuals []                   = $impossible+-- If the first qualifier is a guard, employ an if with a [] else+-- branch.+desugarQuals (CL.GuardQ p : qs)   = do+    (env, genExpr, _) <- desugarQuals qs+    p'                <- expr p+    let wrapIf iter = P.if_  p' iter (NKL.Const (typeOf iter) (ListV []))+    return (env, genExpr, wrapIf)+-- If the first qualifier is a generator, it becomes the base source+-- expression.+desugarQuals (CL.BindQ x xs : qs) = do+    let xt  = elemT $ typeOf xs+    let env = mkEnv (x, xt)+    xs'             <- expr xs+    (env', genExpr) <- desugarQualsRec env xs' qs+    return (env', genExpr, id)++-- | Desugaring of comprehensions happens in two steps: Desugaring the+-- qualifiers leads to an expression that produces the (properly+-- filtered) cartesian product of all qualifiers. The head expression+-- ist then simply mapped over the resulting list.+desugarComprehension:: Type -> CL.Expr -> [CL.Qual] -> NameEnv NKL.Expr+desugarComprehension _ e qs = do+    -- Desugar the qualifiers+    (env, genExpr, wrapHead) <- desugarQuals qs++    let genNames = concatMap qualVar qs++    e'             <- local (++ genNames) (expr e)+    -- All names that are bound in enclosing scopes, including names+    -- bound by local generators+    visibleNames   <- (++) genNames <$> ask++    -- Avoid all visible names+    n              <- freshIdent $ visibleNames ++ boundVars e'++    let t       = elemT $ typeOf genExpr++        -- In the head expression, turn references to generator+        -- variables into references to the (freshly chosen) map+        -- variable. For substitution in the expression, we avoid all+        -- names that are currently visible, including generator names+        -- that are by now no longer visible. This should not hurt+        -- though, as the information is only used for alpha-conversion+        -- on lambdas during substitution.+        e''      = substTupleAccesses visibleNames (n, t) env e'+ +    return $ wrapHead $ NKL.Iterator (listT $ typeOf e') e'' n genExpr+        +-- | Express comprehensions through NKL iteration constructs map and+-- concatMap and filter.+desugarComprehensions :: CL.Expr -> NKL.Expr+desugarComprehensions e = +#ifdef DEBUGCOMP+    trace (debugPrint eo) eo++  where+    eo = runReader (expr e) []++    padSep :: String -> String+    padSep s = "\n" ++ s ++ " " ++ replicate (100 - length s) '=' ++ "\n"++    debugPrint :: NKL.Expr -> String+    debugPrint e' = padSep "Desugared NKL" ++ pp e' ++ padSep ""+#else+    runReader (expr e) []+#endif+
+ src/Database/DSH/Translate/FKL2VL.hs view
@@ -0,0 +1,222 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TupleSections   #-}++module Database.DSH.Translate.FKL2VL (specializeVectorOps) where++import           Control.Applicative              hiding (Const)++import           Control.Monad.Reader++import           Database.Algebra.Dag.Build+import qualified Database.Algebra.Dag.Common   as Alg++import           Database.DSH.Common.Lang+import           Database.DSH.Common.QueryPlan+import           Database.DSH.Common.Type+import           Database.DSH.FKL.Lang+import           Database.DSH.Impossible+import           Database.DSH.VL.Render.JSON      ()+import           Database.DSH.VL.Vector+import qualified Database.DSH.VL.Lang          as VL+import           Database.DSH.VL.Render.JSON   ()+import qualified Database.DSH.VL.Vectorize     as V+import           Database.DSH.VL.Primitives++--------------------------------------------------------------------------------+-- Extend the DAG builder monad with an environment for compiled VL+-- DAGs.++type Env = [(String, Shape VLDVec)]++type EnvBuild = ReaderT Env (Build VL.VL)++-- FIXME might need those when let-expressions have been introduced.+lookupEnv :: String -> EnvBuild (Shape VLDVec)+lookupEnv n = ask >>= \env -> case lookup n env of+    Just r -> return r+    Nothing -> $impossible++bind :: Ident -> Shape VLDVec -> Env -> Env+bind n e env = (n, e) : env++--------------------------------------------------------------------------------+-- Compilation from FKL expressions to a VL DAG.++fkl2VL :: FExpr -> EnvBuild (Shape VLDVec)+fkl2VL expr =+    case expr of+        Var _ n -> lookupEnv n+        Let _ n e1 e -> do+            e1' <- fkl2VL e1+            local (bind n e1') $ fkl2VL e+        Table _ n cs hs -> lift $ V.dbTable n cs hs+        Const t v -> lift $ V.mkLiteral t v+        BinOp _ o NotLifted e1 e2    -> do+            SShape p1 lyt <- fkl2VL e1+            SShape p2 _   <- fkl2VL e2+            p              <- lift $ vlBinExpr o p1 p2+            return $ SShape p lyt+        BinOp _ o Lifted e1 e2     -> do+            VShape p1 lyt <- fkl2VL e1+            VShape p2 _   <- fkl2VL e2+            p                  <- lift $ vlBinExpr o p1 p2+            return $ VShape p lyt+        UnOp _ o NotLifted e1 -> do+            SShape p1 lyt <- fkl2VL e1+            p              <- lift $ vlUnExpr o p1+            return $ SShape p lyt+        UnOp _ o Lifted e1 -> do+            VShape p1 lyt <- fkl2VL e1+            p                  <- lift $ vlUnExpr o p1+            return $ VShape p lyt+        If _ eb e1 e2 -> do+            eb' <- fkl2VL eb+            e1' <- fkl2VL e1+            e2' <- fkl2VL e2+            lift $ V.ifList eb' e1' e2'+        PApp1 t f l arg -> do+            arg' <- fkl2VL arg+            lift $ papp1 t f l arg'+        PApp2 _ f l arg1 arg2 -> do+            arg1' <- fkl2VL arg1+            arg2' <- fkl2VL arg2+            lift $ papp2 f l arg1' arg2'+        PApp3 _ p l arg1 arg2 arg3 -> do+            arg1' <- fkl2VL arg1+            arg2' <- fkl2VL arg2+            arg3' <- fkl2VL arg3+            lift $ papp3 p l arg1' arg2' arg3'+        Ext (Forget n _ arg) -> do+            arg' <- fkl2VL arg+            return $ V.forget n arg'+        Ext (Imprint n _ arg1 arg2) -> do+            arg1' <- fkl2VL arg1+            arg2' <- fkl2VL arg2+            return $ V.imprint n arg1' arg2'+        MkTuple _ Lifted args -> do+            args' <- mapM fkl2VL args+            lift $ V.tupleL args'+        MkTuple _ NotLifted args -> do+            args' <- mapM fkl2VL args+            lift $ V.tuple args'++papp3 :: Prim3 -> Lifted -> Shape VLDVec -> Shape VLDVec -> Shape VLDVec -> Build VL.VL (Shape VLDVec)+papp3 Combine Lifted    = V.combineL+papp3 Combine NotLifted = V.combine++papp1 :: Type -> Prim1 -> Lifted -> Shape VLDVec -> Build VL.VL (Shape VLDVec)+papp1 t f Lifted =+    case f of+        Singleton       -> V.singletonL+        Length          -> V.lengthL+        Concat          -> V.concatL+        The             -> V.theL+        Tail            -> V.tailL+        Reverse         -> V.reverseL+        Init            -> V.initL+        Last            -> V.lastL+        Nub             -> V.nubL+        Number          -> V.numberL+        Transpose       -> V.transposeL+        Reshape n       -> V.reshapeL n+        And             -> V.aggrL VL.AggrAll+        Or              -> V.aggrL VL.AggrAny+        Minimum         -> V.aggrL VL.AggrMin+        Maximum         -> V.aggrL VL.AggrMax+        Sum             -> V.aggrL $ VL.AggrSum $ typeToScalarType $ elemT t+        Avg             -> V.aggrL VL.AggrAvg+        TupElem i       -> V.tupElemL i++papp1 t f NotLifted =+    case f of+        Singleton        -> V.singleton+        Length           -> V.length_+        Reshape n        -> V.reshape n+        Transpose        -> V.transpose+        Number           -> V.number+        Nub              -> V.nub+        Last             -> V.last+        Init             -> V.init+        Reverse          -> V.reverse+        Tail             -> V.tail+        Concat           -> V.concat+        The              -> V.the+        Sum              -> V.aggr $ VL.AggrSum $ typeToScalarType t+        Avg              -> V.aggr VL.AggrAvg+        Or               -> V.aggr VL.AggrAny+        And              -> V.aggr VL.AggrAll+        Maximum          -> V.aggr VL.AggrMax+        Minimum          -> V.aggr VL.AggrMin+        TupElem i        -> V.tupElem i++papp2 :: Prim2 -> Lifted -> Shape VLDVec -> Shape VLDVec -> Build VL.VL (Shape VLDVec)+papp2 f Lifted =+    case f of+        Dist           -> V.distL+        Group          -> V.groupL+        Sort           -> V.sortL+        Restrict       -> V.restrictL+        Append         -> V.appendL+        Index          -> V.indexL+        Zip            -> V.zipL+        CartProduct    -> V.cartProductL+        NestProduct    -> V.nestProductL+        ThetaJoin p    -> V.thetaJoinL p+        NestJoin p     -> V.nestJoinL p+        SemiJoin p     -> V.semiJoinL p+        AntiJoin p     -> V.antiJoinL p++papp2 f NotLifted =+    case f of+        Dist            -> V.dist+        Group           -> V.group+        Sort            -> V.sort+        Restrict        -> V.restrict+        Append          -> V.append+        Index           -> V.index+        Zip             -> V.zip+        CartProduct     -> V.cartProduct+        NestProduct     -> V.nestProduct+        ThetaJoin p     -> V.thetaJoin p+        NestJoin p      -> V.nestJoin p+        SemiJoin p      -> V.semiJoin p+        AntiJoin p      -> V.antiJoin p++-- For each top node, determine the number of columns the vector has and insert+-- a dummy projection which just copies those columns. This is to ensure that+-- columns which are required from the top are not pruned by optimizations.+insertTopProjections :: Build VL.VL (Shape VLDVec) -> Build VL.VL (Shape VLDVec)+insertTopProjections g = g >>= traverseShape++  where+    traverseShape :: Shape VLDVec -> Build VL.VL (Shape VLDVec)+    traverseShape (VShape (VLDVec q) lyt) =+        insertProj lyt q VL.Project VLDVec VShape+    traverseShape (SShape (VLDVec q) lyt)     =+        insertProj lyt q VL.Project VLDVec SShape++    traverseLayout :: (Layout VLDVec) -> Build VL.VL (Layout VLDVec)+    traverseLayout (LCol c)               = return $ LCol c+    traverseLayout (LTuple lyts)          = LTuple <$> mapM traverseLayout lyts+    traverseLayout (LNest (VLDVec q) lyt) =+      insertProj lyt q VL.Project VLDVec LNest++    insertProj+      :: Layout VLDVec               -- ^ The node's layout+      -> Alg.AlgNode                    -- ^ The top node to consider+      -> ([VL.Expr] -> VL.UnOp)         -- ^ Constructor for the projection op+      -> (Alg.AlgNode -> v)             -- ^ Vector constructor+      -> (v -> (Layout VLDVec) -> t) -- ^ Layout/Shape constructor+      -> Build VL.VL t+    insertProj lyt q project vector describe = do+        let width = columnsInLayout lyt+            cols  = [1 .. width]+        qp   <- insert $ Alg.UnOp (project $ map VL.Column cols) q+        lyt' <- traverseLayout lyt+        return $ describe (vector qp) lyt'++-- | Compile a FKL expression into a query plan of vector operators (VL)+specializeVectorOps :: FExpr -> QueryPlan VL.VL VLDVec+specializeVectorOps e = mkQueryPlan opMap shape tagMap+  where+    (opMap, shape, tagMap) = runBuild (insertTopProjections $ runReaderT (fkl2VL e) [])
+ src/Database/DSH/Translate/Frontend2CL.hs view
@@ -0,0 +1,319 @@+{-# LANGUAGE FlexibleContexts    #-}+{-# LANGUAGE GADTs               #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell     #-}++-- | Translate DSH frontend expressions (implicitly typed through+-- GADT) into explicitly typed DSH backend expressions.+module Database.DSH.Translate.Frontend2CL (toComprehensions) where++import           Database.DSH.Impossible++import qualified Database.DSH.CL.Lang            as CL+import qualified Database.DSH.CL.Primitives      as CP+import qualified Database.DSH.Common.Lang        as L+import qualified Database.DSH.Common.Type        as T++import           Data.Text                       (unpack)+import           Database.DSH.Frontend.Funs+import           Database.DSH.Frontend.TupleTypes+import           Database.DSH.Frontend.Internals++import qualified Data.Map                        as M++import           Control.Applicative+import           Control.Monad+import           Control.Monad.State++import           Text.Printf++import           GHC.Exts                        (sortWith)++-- | For each column, we need the name of the column, a string+-- description of the type for error messsages and a function to check+-- a DSH base type for compability with the column.+type TableInfo = [(String, String, (T.Type -> Bool))]++type TableInfoCache = M.Map String TableInfo++type QueryTableInfo = String -> IO TableInfo++-- In the state, we store a counter for fresh variable names, the+-- cache for table information and the backend-specific IO function+-- that retrieves not-yet-cached table information.+type CompileState = (Integer, TableInfoCache, QueryTableInfo)++-- | The Compile monad provides fresh variable names, allows to+-- retrieve information about tables from the database backend and+-- caches table information.+type Compile = StateT  CompileState IO++-- | Lookup information that describes a table. If the information is+-- not present in the state then the connection is used to retrieve+-- the table information from the Database.+tableInfo :: String -> Compile TableInfo+tableInfo tableName = do+    (i, env, f) <- get+    case M.lookup tableName env of+        Nothing -> do+            inf <- getTableInfoFun tableName+            put (i, M.insert tableName inf env, f)+            return inf+        Just v -> return v++-- | Provide a fresh identifier name during compilation+freshVar :: Compile Integer+freshVar = do+    (i, m, f) <- get+    put (i + 1, m, f)+    return i++prefixVar :: Integer -> String+prefixVar i = "v" ++ show i++getTableInfoFun :: String -> Compile TableInfo+getTableInfoFun tableName = do+    (_, _, queryTableInfo) <- get+    lift $ queryTableInfo tableName++-- | Translate a DSH frontend expression into the internal+-- comprehension-based language. 'queryTableInfo' abstracts asking a+-- database for information about tables, which might be performed+-- using one of the existing backends (X100, SQL).+toComprehensions :: QueryTableInfo -> Exp a -> IO CL.Expr+toComprehensions queryTableInfo e = runCompile queryTableInfo $ translate e++-- | Execute the transformation computation. During compilation table+-- information can be retrieved from the database, therefore the result+-- is wrapped in the IO Monad.+runCompile :: QueryTableInfo -> Compile a -> IO a+runCompile f = liftM fst . flip runStateT (1, M.empty, f)++lamBody :: forall a b.(Reify a, Reify b) => (Exp a -> Exp b) -> Compile (L.Ident, Exp b)+lamBody f = do+    v <- freshVar+    return (prefixVar v, f (VarE v :: Exp a))++-- | Translate a frontend HOAS AST to a FOAS AST in Comprehension+-- Language (CL).+translate :: forall a. Exp a -> Compile CL.Expr+translate (TupleConstE tc) = let translateTupleConst = $(mkTranslateTupleTerm 16)+                             in translateTupleConst tc+translate UnitE = return $ CP.unit+translate (BoolE b) = return $ CP.bool b+translate (CharE c) = return $ CP.string [c]+translate (IntegerE i) = return $ CP.int (fromInteger i)+translate (DoubleE d) = return $ CP.double d+translate (TextE t) = return $ CP.string (unpack t)+translate (VarE i) = do+    let ty = reify (undefined :: a)+    return $ CP.var (translateType ty) (prefixVar i)+translate (ListE es) = do+    let ty = reify (undefined :: a)+    CP.list (translateType ty) <$> mapM translate es+-- We expect the query language to be first order. Lambdas must only+-- occur as an argument to higher-order built-in combinators (map,+-- concatMap, sortWith, ...). If lambdas occur in other places that+-- have not been eliminated by inlining in the frontend, additional+-- normalization rules or defunctionalization should be employed.+translate (LamE _) = $impossible+translate (TableE (TableDB tableName hints)) = do+    -- Reify the type of the table expression+    let ty = reify (undefined :: a)++    -- Extract the column types from the frontend type+    let ts = T.tupleElemTypes $ T.elemT $ translateType ty++    -- Fetch the actual type of the table from the database+    -- backend. Since we can't refer to columns by name from the+    -- Haskell side, we sort the columns by name to get a canonical+    -- order.+    tableDescr <- sortWith (\(n, _, _) -> n) <$> tableInfo tableName++    let tableTypeError = printf "DSH type and type of table %s are incompatible:\nDSH: %s\nDatabase: %s"+                                tableName+                                (show ts)+                                (show $ map (\(n, t, _) -> (n, t)) tableDescr)++    -- The DSH record/tuple type must match the number of columns in+    -- the database table+    if length tableDescr == length ts+        then return ()+        else error tableTypeError++    let matchTypes :: (String, String, T.Type -> Bool) -> T.Type -> (L.ColName, T.Type)+        matchTypes (colName, _, typesCompatible) dshType =+            if typesCompatible dshType+            then (L.ColName colName, dshType)+            else error tableTypeError++    let cols = zipWith matchTypes tableDescr ts++    return $ CP.table (translateType ty) tableName cols (compileHints hints)++translate (AppE f args) = translateApp f args++compileHints :: TableHints -> L.TableHints+compileHints hints = L.TableHints { L.keysHint = keys $ keysHint hints+                                  , L.nonEmptyHint = ne $ nonEmptyHint hints+                                  }+  where+    keys :: [Key] -> [L.Key]+    keys ks = [ L.Key [ L.ColName c | c <- k ] | Key k <- ks ]++    ne :: Emptiness -> L.Emptiness+    ne NonEmpty      = L.NonEmpty+    ne PossiblyEmpty = L.PossiblyEmpty+++translateApp3 :: (CL.Expr -> CL.Expr -> CL.Expr -> CL.Expr) -> Exp (a, b, c) -> Compile CL.Expr+translateApp3 f (TupleConstE (Tuple3E e1 e2 e3)) = f <$> translate e1 <*> translate e2 <*> translate e3+translateApp3 _ _ = $impossible++translateApp2 :: (CL.Expr -> CL.Expr -> CL.Expr) -> Exp (a, b) -> Compile CL.Expr+translateApp2 f (TupleConstE (Tuple2E e1 e2)) = f <$> translate e1 <*> translate e2+translateApp2 _ _ = $impossible++translateApp1 :: (CL.Expr -> CL.Expr) -> Exp a -> Compile CL.Expr+translateApp1 f e = f <$> translate e++-- | Translate DSH frontend types into backend types.+translateType :: Type a -> T.Type+translateType UnitT          = T.unitT+translateType BoolT          = T.boolT+translateType CharT          = T.stringT+translateType IntegerT       = T.intT+translateType DoubleT        = T.doubleT+translateType TextT          = T.stringT+translateType (ListT t)      = T.listT (translateType t)+translateType (TupleT tupTy) = let translateTupleType = $(mkTranslateType 16)+                               in translateTupleType tupTy+translateType (ArrowT t1 t2) = $impossible++-- | From the type of a table (a list of base records represented as+-- right-deep nested tuples) extract the types of the individual+-- fields.++translateApp :: Fun a b -> Exp a -> Compile CL.Expr+translateApp f args =+    case f of+       -- Builtin functions with arity three+       Cond -> translateApp3 CP.cond args++       -- Builtin functions with arity two+       Add          -> translateApp2 CP.add args+       Mul          -> translateApp2 CP.mul args+       Sub          -> translateApp2 CP.sub args+       Div          -> translateApp2 CP.div args+       Mod          -> translateApp2 CP.mod args+       Index        -> translateApp2 CP.index args+       Cons         -> translateApp2 CP.cons args++       -- Map to a comprehension+       Map          -> +           case args of+               TupleConstE (Tuple2E (LamE lam) xs) -> do+                   xs'                 <- translate xs+                   (boundVar, bodyExp) <- lamBody lam+                   bodyExp'            <- translate bodyExp+                   return $ CP.singleGenComp bodyExp' boundVar xs'+               _ -> $impossible++       -- Map to a comprehension and concat+       ConcatMap    -> +           case args of+               TupleConstE (Tuple2E (LamE lam) xs) -> do+                   xs'                 <- translate xs+                   (boundVar, bodyExp) <- lamBody lam+                   bodyExp'            <- translate bodyExp+                   return $ CP.concat $ CP.singleGenComp bodyExp' boundVar xs'+               _ -> $impossible+               +       -- Map to a first-order combinator 'sort'+       SortWith     -> +           case args of+               TupleConstE (Tuple2E (LamE lam) xs) -> do+                   xs'                 <- translate xs+                   (boundVar, bodyExp) <- lamBody lam+                   bodyExp'            <- translate bodyExp+                   genName             <- prefixVar <$> freshVar++                   let genVar = CL.Var (T.typeOf xs') genName+                       ss     = CP.singleGenComp bodyExp' boundVar genVar +                   return $ CP.let_ genName xs' (CP.sort genVar ss)+               _ -> $impossible++       -- Map to a comprehension with a guard+       Filter       -> +           case args of+               TupleConstE (Tuple2E (LamE lam) xs) -> do+                   xs'                 <- translate xs+                   (boundVar, bodyExp) <- lamBody lam+                   bodyExp'            <- translate bodyExp+                   let xt    = T.typeOf xs'+                       quals = CL.BindQ boundVar xs' CL.:* (CL.S $ CL.GuardQ bodyExp')+                   return $ CL.Comp xt (CL.Var xt boundVar) quals+               _ -> $impossible++       -- Map to a first-order combinator 'group'+       GroupWithKey ->+           case args of+               TupleConstE (Tuple2E (LamE lam) xs) -> do+                   xs'                 <- translate xs+                   (boundVar, bodyExp) <- lamBody lam+                   bodyExp'            <- translate bodyExp+                   genName             <- prefixVar <$> freshVar++                   let genVar = CL.Var (T.typeOf xs') genName+                       ss     = CP.singleGenComp bodyExp' boundVar genVar +                   return $ CP.let_ genName xs' (CP.group genVar ss)+               _ -> $impossible++       Append       -> translateApp2 CP.append args+       Zip          -> translateApp2 CP.zip args+       Equ          -> translateApp2 CP.eq args+       NEq          -> translateApp2 CP.neq args+       Conj         -> translateApp2 CP.conj args+       Disj         -> translateApp2 CP.disj args+       Lt           -> translateApp2 CP.lt args+       Lte          -> translateApp2 CP.lte args+       Gte          -> translateApp2 CP.gte args+       Gt           -> translateApp2 CP.gt args+       Like         -> translateApp2 CP.like args++       -- Builtin functions with arity one+       SubString f t   -> translateApp1 (CP.substring f t) args+       IntegerToDouble -> translateApp1 CP.castDouble args+       Not             -> translateApp1 CP.not args+       Sin             -> translateApp1 CP.sin args+       Cos             -> translateApp1 CP.cos args+       Tan             -> translateApp1 CP.tan args+       ASin            -> translateApp1 CP.asin args+       ACos            -> translateApp1 CP.acos args+       ATan            -> translateApp1 CP.atan args+       Sqrt            -> translateApp1 CP.sqrt args+       Log             -> translateApp1 CP.log args+       Exp             -> translateApp1 CP.exp args+       Fst             -> translateApp1 CP.fst args+       Snd             -> translateApp1 CP.snd args+       Head            -> translateApp1 CP.head args+       Tail            -> translateApp1 CP.tail args+       Minimum         -> translateApp1 CP.minimum args+       Maximum         -> translateApp1 CP.maximum args+       Concat          -> translateApp1 CP.concat args+       Sum             -> translateApp1 CP.sum args+       Avg             -> translateApp1 CP.avg args+       And             -> translateApp1 CP.and args+       Or              -> translateApp1 CP.or args+       Reverse         -> translateApp1 CP.reverse args+       Number          -> translateApp1 CP.number args+       Length          -> translateApp1 CP.length args+       Null            -> translateApp1 CP.null args+       Init            -> translateApp1 CP.init args+       Last            -> translateApp1 CP.last args+       Nub             -> translateApp1 CP.nub args+       Guard           -> translateApp1 CP.guard args+       Transpose       -> translateApp1 CP.transpose args+       Reshape n       -> translateApp1 (CP.reshape n) args+       TupElem te      -> let compileTupElem = $(mkTupElemCompile 16)+                          in compileTupElem te args
+ src/Database/DSH/Translate/NKL2FKL.hs view
@@ -0,0 +1,350 @@+{-# LANGUAGE TemplateHaskell #-}+-- | The Flattening Transformation+module Database.DSH.Translate.NKL2FKL (flatTransform) where++-- FIXME use more let bindings to avoid term replication, e.g. in if conditionals+-- FIXME make sure that no wrong shadowing occurs while lifting or restricting the environment.++import           Control.Monad.State+import           Control.Monad.Reader+import           Control.Applicative++import           Database.DSH.Impossible+import           Database.DSH.Common.Lang+import           Database.DSH.Common.Nat+import           Database.DSH.Common.Type+import qualified Database.DSH.FKL.Lang       as F+import qualified Database.DSH.FKL.Primitives as P+import           Database.DSH.FKL.Rewrite+import qualified Database.DSH.NKL.Lang       as N++-- | Transform an expression in the Nested Kernel Language into its+-- equivalent Flat Kernel Language expression by means of the+-- flattening transformation.+flatTransform :: N.Expr -> F.FExpr+flatTransform expr = optimizeFKL "FKL" +                     $ normalize +                     $ optimizeFKL "FKL Intermediate" +                     $ runFlat initEnv (flatten expr)++--------------------------------------------------------------------------------+-- The Flattening Transformation++--------------------------------------------------------------------------------+-- Translation of built-in combinators. Combinators are lifted+-- according to the iteration depth at which they are encountered.++prim1 :: N.Prim1 -> F.LExpr -> Nat -> F.LExpr+prim1 p =+    case p of+        N.Singleton -> P.sng+        N.Length    -> P.length+        N.Concat    -> P.concat+        N.Sum       -> P.sum+        N.Avg       -> P.avg+        N.The       -> P.the+        N.TupElem n -> P.tupElem n+        N.Head      -> P.head+        N.Tail      -> P.tail+        N.Minimum   -> P.minimum+        N.Maximum   -> P.maximum+        N.Reverse   -> P.reverse+        N.And       -> P.and+        N.Or        -> P.or+        N.Init      -> P.init+        N.Last      -> P.last+        N.Nub       -> P.nub+        N.Number    -> P.number+        N.Reshape n -> P.reshape n+        N.Transpose -> P.transpose++prim2 :: N.Prim2 -> F.LExpr -> F.LExpr -> Nat -> F.LExpr+prim2 p =+    case p of+        N.Group        -> P.group+        N.Sort         -> P.sort+        N.Restrict     -> P.restrict+        N.Append       -> P.append+        N.Index        -> P.index+        N.Zip          -> P.zip+        N.CartProduct  -> P.cartProduct+        N.NestProduct  -> P.nestProduct+        N.ThetaJoin jp -> P.thetaJoin jp+        N.NestJoin jp  -> P.nestJoin jp+        N.SemiJoin jp  -> P.semiJoin jp+        N.AntiJoin jp  -> P.antiJoin jp++--------------------------------------------------------------------------------+-- Flattening environment++type Flatten a = Reader Env a++runFlat :: Env -> Flatten a -> a+runFlat env ma = runReader ma env++envVar :: (Ident, Type) -> F.LExpr+envVar (n, t) = F.Var t n++-- | The environment stores all variables which are currently in scope and the current iteration depth.+data Env = Env+    { -- | All bindings which are currently in scope and need to be+      -- lifted to the current iteration context.+      inScope    :: [(Ident, Type)]++      -- | The current iteration depth+    , frameDepth :: Nat+    }++initEnv :: Env+initEnv = Env { inScope = [], frameDepth = Zero }++bindEnv :: Ident -> Type -> Env -> Env+bindEnv n t e = e { inScope = (n, t) : inScope e }++-- | Update the environment to express the descent into a+-- comprehension that binds the name 'x'. This involves binding 'x' in+-- the current environment frame and increasing the frame depth.+descendEnv :: (Ident, Type) -> Env -> Env+descendEnv x env = env { inScope    = x : inScope env +                       , frameDepth = Succ $ frameDepth env+                       }++frameDepthM :: Flatten Nat+frameDepthM = asks frameDepth++-- | Restrict all environment entries according to a boolean vector+-- ('then' or 'else' branch).+restrictEnv :: [(Ident, Type)] -> Nat -> F.LExpr -> F.LExpr -> F.LExpr+restrictEnv env d1 bs branchExpr = mkRestrictLet env+  where+    mkRestrictLet :: [(Ident, Type)] -> F.LExpr+    mkRestrictLet [] = $impossible+    mkRestrictLet (e : []) =+        P.let_ (fst e)+               (P.restrict (envVar e) bs d1)+               branchExpr+    mkRestrictLet (e : (e2 : es)) = +        P.let_ (fst e)+               (P.restrict (envVar e) bs d1)+               (mkRestrictLet (e2 : es))++-- | Lift all names bound in the environment: the value is replicated+-- for each element of the current context. The chain of 'let's is+-- terminated by the flattened head expression of the current+-- iterator.+liftEnv :: (Ident, Type) -> Nat -> F.LExpr -> [(Ident, Type)] -> F.LExpr+liftEnv ctx d headExpr env = mkLiftingLet env+  where+    mkLiftingLet :: [(Ident, Type)] -> F.LExpr+    mkLiftingLet []        = headExpr+    mkLiftingLet (e : [])  =+        P.let_ (fst e) (P.dist (envVar e) cv d) headExpr+    mkLiftingLet (e : (e2 : es)) =+        P.let_ (fst e) (P.dist (envVar e) cv d) (mkLiftingLet (e2 : es))++    cv :: F.LExpr+    cv = envVar ctx+++--------------------------------------------------------------------------------++-- | Transform top-level expressions which are not nested in an+-- iterator.+flatten :: N.Expr -> Flatten F.LExpr+flatten (N.Table t n cs hs)  = return $ F.Table t n cs hs+flatten (N.UnOp t op e1)     = P.un t op <$> flatten e1 <*> pure Zero+flatten (N.BinOp t op e1 e2) = P.bin t op <$> flatten e1 <*> flatten e2 <*> pure Zero+flatten (N.Const t v)        = return $ F.Const t v+flatten (N.Var t v)          = return $ F.Var t v+flatten (N.If t ce te ee)    = F.If t <$> flatten ce <*> flatten te <*> flatten ee+flatten (N.AppE1 _ p e)      = prim1 p <$> flatten e <*> pure Zero+flatten (N.AppE2 _ p e1 e2)  = prim2 p <$> flatten e1 <*> flatten e2 <*> pure Zero+flatten (N.Let _ x xs e)     = P.let_ x <$> flatten xs <*> local (bindEnv x (typeOf xs)) (flatten e)+flatten (N.MkTuple _ es)     = P.tuple <$> mapM flatten es <*> pure Zero+flatten (N.Iterator _ h x xs)    = do+    -- Prepare an environment in which the current generator is the+    -- context+    let initCtx    = (x, typeOf xs)+    +    -- In this environment, transform the iterator head+    flatHead <- local (descendEnv initCtx) (deepFlatten initCtx h)++    P.let_ x <$> flatten xs <*> (liftEnv initCtx Zero flatHead <$> asks inScope)++--------------------------------------------------------------------------------++-- | Compile expressions nested in an iterator.+deepFlatten :: (Ident, Type) -> N.Expr -> Flatten F.LExpr+deepFlatten _   (N.Var t v)          = frameDepthM >>= \d -> return $ F.Var (liftTypeN d t) v+deepFlatten ctx (N.Table t n cs hs)  = P.broadcast (F.Table t n cs hs) (envVar ctx) <$> frameDepthM+deepFlatten ctx (N.Const t v)        = P.broadcast (F.Const t v) (envVar ctx) <$> frameDepthM+deepFlatten ctx (N.UnOp t op e1)     = P.un t op <$> deepFlatten ctx e1 <*> frameDepthM+deepFlatten ctx (N.BinOp t op e1 e2) = P.bin t op <$> deepFlatten ctx e1 <*> deepFlatten ctx e2 <*> frameDepthM+deepFlatten ctx (N.MkTuple _ es)     = frameDepthM >>= \d -> P.tuple <$> mapM (deepFlatten ctx) es <*> pure d+deepFlatten ctx (N.AppE1 _ p e)      = prim1 p <$> deepFlatten ctx e <*> frameDepthM+deepFlatten ctx (N.AppE2 _ p e1 e2)  = prim2 p <$> deepFlatten ctx e1 <*> deepFlatten ctx e2 <*> frameDepthM++deepFlatten ctx (N.Let _ x xs e)     = P.let_ x <$> deepFlatten ctx xs +                                                <*> local (bindEnv x (typeOf xs)) (deepFlatten ctx e)++deepFlatten ctx (N.If _ ce te ee)    = do+    Succ d1      <- frameDepthM+    +    -- Lift the condition+    bs           <- deepFlatten ctx ce+    +    -- Lift the THEN branch. Note that although the environment record+    -- does not change, all environment variables are re-bound to a+    -- restricted environment by 'restrictEnv'.+    thenExpr     <- deepFlatten ctx te++    -- Lift the ELSE branch. See comment above.+    elseExpr     <- deepFlatten ctx ee++    env          <- asks inScope++    -- Construct the restricted environments in which the THEN and+    -- ELSE branches are evaluated.+    let notL xs = P.un boolT (SUBoolOp Not) xs (Succ d1) +    +        thenRes = restrictEnv env d1 bs thenExpr++        elseRes = restrictEnv env d1 (notL bs) elseExpr++    return $ P.combine bs thenRes elseRes d1++-- FIXME lift types in the environment (add one list type constructor)+deepFlatten ctx (N.Iterator _ h x xs)    = do+    d           <- frameDepthM+    env         <- asks inScope+    let ctx' = (x, liftTypeN (Succ d) (typeOf xs))+    headExpr    <- local (descendEnv ctx') $ deepFlatten ctx' h ++    xs'         <- deepFlatten ctx xs++    return $ P.let_ x xs' (liftEnv ctx' d headExpr env)+++--------------------------------------------------------------------------------+-- Normalization of intermediate flat expressions into the final+-- form. This step eliminates higher-lifted occurences of built-in+-- combinators.++type Supply = Int++type NormFlat a = State Supply a++freshNameN :: NormFlat Ident+freshNameN = do+    i <- get+    put $ i + 1+    return $ "nf" ++ show i++normalize :: F.LExpr -> F.FExpr+normalize e = evalState (normLifting e) 0++implementBroadcast :: F.BroadcastExt -> NormFlat F.FExpr+implementBroadcast (F.Broadcast d _ e1 e2) = do+    e1' <- normLifting e1+    e2' <- normLifting e2+    case d of+        Zero             -> $impossible+        Succ Zero        -> return $ P.fdist e1' e2'+        -- FIXME use let-binding+        Succ d1@(Succ _) -> return $ P.imprint d1 e2' (P.fdist e1' (P.forget d1 e2'))++-- | Reduce all higher-lifted occurences of primitive combinators and+-- operators to singly lifted variants by flattening the arguments and+-- restoring the original list shape on the result.+normLifting :: F.LExpr -> NormFlat F.FExpr+normLifting (F.Table t n cs hs)    = return $ F.Table t n cs hs+normLifting (F.If t ce te ee)      = F.If t <$> normLifting ce <*> normLifting te <*> normLifting ee+normLifting (F.Const t v)          = return $ F.Const t v+normLifting (F.Var t n)            = return $ F.Var t n+normLifting (F.Let t x e1 e2)      = F.Let t x <$> normLifting e1 <*> normLifting e2+normLifting (F.Ext b)              = implementBroadcast b+normLifting (F.MkTuple t l es)     =+    case l of+        F.LiftedN Zero         -> F.MkTuple t F.NotLifted <$> mapM normLifting es+        F.LiftedN (Succ Zero)  -> F.MkTuple t F.Lifted <$> mapM normLifting es+        F.LiftedN (Succ d)     -> do+            e1' : es' <- mapM normLifting es+            n         <- freshNameN+            let v   = F.Var (typeOf e1') n+                app = F.MkTuple (unliftTypeN d t) F.Lifted (P.forget d v : map (P.forget d) es')+            return $ P.let_ n e1' $ P.imprint d v app++normLifting (F.UnOp t op l e)      = +    case l of+        F.LiftedN Zero         -> F.UnOp t op F.NotLifted <$> normLifting e+        F.LiftedN (Succ Zero)  -> F.UnOp t op F.Lifted <$> normLifting e+        F.LiftedN (Succ d)     -> do+            e' <- normLifting e+            n  <- freshNameN+            let v   = F.Var (typeOf e') n+                app = F.UnOp (unliftTypeN d t) op F.Lifted (P.forget d v)+            return $ P.let_ n e' $ P.imprint d v app++normLifting (F.BinOp t op l e1 e2)  = +    case l of+        F.LiftedN Zero         -> F.BinOp t op F.NotLifted+                                            <$> normLifting e1+                                            <*> normLifting e2+        F.LiftedN (Succ Zero)  -> F.BinOp t op F.Lifted+                                            <$> normLifting e1+                                            <*> normLifting e2+        F.LiftedN (Succ d)     -> do+            e1' <- normLifting e1+            e2' <- normLifting e2+            n   <- freshNameN+            let v   = F.Var (typeOf e1') n+                app = F.BinOp (unliftTypeN d t) op F.Lifted (P.forget d v) (P.forget d e2')+            return $ P.let_ n e1' $ P.imprint d v app++normLifting (F.PApp1 t p l e)    = +    case l of+        F.LiftedN Zero         -> F.PApp1 t p F.NotLifted <$> normLifting e+        F.LiftedN (Succ Zero)  -> F.PApp1 t p F.Lifted <$> normLifting e+        F.LiftedN (Succ d)     -> do+            e' <- normLifting e+            n  <- freshNameN+            let v   = F.Var (typeOf e') n+                app = F.PApp1 (unliftTypeN d t) p F.Lifted (P.forget d v)+            return $ P.let_ n e' (P.imprint d v app)++normLifting (F.PApp2 t p l e1 e2)   = +    case l of+        F.LiftedN Zero         -> F.PApp2 t p F.NotLifted+                                              <$> normLifting e1+                                              <*> normLifting e2+        F.LiftedN (Succ Zero)  -> F.PApp2 t p F.Lifted+                                              <$> normLifting e1+                                              <*> normLifting e2+        F.LiftedN (Succ d)     -> do+            e1' <- normLifting e1+            e2' <- normLifting e2+            n   <- freshNameN+            let v   = F.Var (typeOf e1') n+                app = F.PApp2 (unliftTypeN d t) p F.Lifted (P.forget d v) (P.forget d e2')+            return $ P.let_ n e1' $ P.imprint d v app++normLifting (F.PApp3 t p l e1 e2 e3)    = +    case l of+        F.LiftedN Zero        -> F.PApp3 t p F.NotLifted+                                             <$> normLifting e1+                                             <*> normLifting e2+                                             <*> normLifting e3+        F.LiftedN (Succ Zero) -> F.PApp3 t p F.Lifted+                                             <$> normLifting e1+                                             <*> normLifting e2+                                             <*> normLifting e3+        F.LiftedN (Succ d)    -> do+            e1' <- normLifting e1+            e2' <- normLifting e2+            e3' <- normLifting e3+            n   <- freshNameN+            let v   = F.Var (typeOf e1') n+                app = F.PApp3 (unliftTypeN d t) p F.Lifted (P.forget d v) +                                                           (P.forget d e2') +                                                           (P.forget d e3')+            return $ P.let_ n e1' $ P.imprint d v app
+ src/Database/DSH/Translate/VL2Algebra.hs view
@@ -0,0 +1,380 @@+{-# LANGUAGE TemplateHaskell  #-}+{-# LANGUAGE FlexibleContexts #-}++module Database.DSH.Translate.VL2Algebra+    ( implementVectorOpsPF+    ) where++import qualified Data.IntMap                          as IM+import           Data.List+import qualified Data.Map                             as M+import           Data.Maybe++import           Control.Applicative+import           Control.Monad.State++import qualified Database.Algebra.Dag                 as D+import qualified Database.Algebra.Dag.Build           as B+import           Database.Algebra.Dag.Common+import qualified Database.Algebra.Table.Lang          as TA++import           Database.DSH.Impossible+import           Database.DSH.Common.QueryPlan+import           Database.DSH.Translate.FKL2VL        ()+import           Database.DSH.VL.Vector+import qualified Database.DSH.VL.Lang                 as V+import           Database.DSH.VL.VectorAlgebra+import           Database.DSH.VL.VectorAlgebra.TA     ()++-- | A layer on top of the DAG builder monad that caches the+-- translation result of VL nodes.+type VecBuild a v = StateT (M.Map AlgNode (Res v)) (B.Build a)++runVecBuild :: VectorAlgebra v a => VecBuild a v r -> (D.AlgebraDag a, r, NodeMap [Tag])+runVecBuild c = B.runBuild $ fst <$> runStateT c M.empty++data Res v = Prop    AlgNode+           | Rename  AlgNode+           | RDVec   v+           | RLPair   (Res v) (Res v)+           | RTriple (Res v) (Res v) (Res v)+         deriving Show++fromDict :: VectorAlgebra v a => AlgNode -> VecBuild a v (Maybe (Res v))+fromDict n = do+    dict <- get+    return $ M.lookup n dict++insertTranslation :: VectorAlgebra v a => AlgNode -> Res v -> VecBuild a v ()+insertTranslation n res = modify (M.insert n res)++fromPVec :: PVec -> Res v+fromPVec (PVec p) = Prop p++toPVec :: Res v -> PVec+toPVec (Prop p) = PVec p+toPVec _       = error "toPVec: Not a prop vector"++fromRVec :: RVec -> Res v+fromRVec (RVec r) = Rename r++toRVec :: Res v -> RVec+toRVec (Rename r) = RVec r+toRVec _          = error "toRVec: Not a rename vector"++fromDVec :: v -> Res v+fromDVec v = RDVec v++toDVec :: Res v -> v+toDVec (RDVec v) = v+toDVec _         = error "toDVec: Not a NDVec"++refreshLyt :: VectorAlgebra v a => Layout VLDVec -> VecBuild a v (Layout v)+refreshLyt (LCol c) = return $ LCol c+refreshLyt (LNest (VLDVec n) lyt) = do+    Just n' <- fromDict n+    lyt'    <- refreshLyt lyt+    return $ LNest (toDVec n') lyt'+refreshLyt (LTuple lyts) = LTuple <$> mapM refreshLyt lyts++refreshShape :: VectorAlgebra v a => Shape VLDVec -> VecBuild a v (Shape v)+refreshShape (VShape (VLDVec n) lyt) = do+    mv <- fromDict n+    case mv of+        Just v -> do+            lyt' <- refreshLyt lyt+            return $ VShape (toDVec v) lyt'+        _ -> $impossible+refreshShape (SShape (VLDVec n) lyt) = do+    mv <- fromDict n+    case mv of+        Just (RDVec v) -> do+            lyt'              <- refreshLyt lyt+            return $ SShape v lyt'+        _ -> $impossible++translate :: VectorAlgebra v a => NodeMap V.VL -> AlgNode -> VecBuild a v (Res v)+translate vlNodes n = do+    r <- fromDict n++    case r of+        -- The VL node has already been encountered and translated.+        Just res -> return $ res++        -- The VL node has not been translated yet.+        Nothing  -> do+            let vlOp = getVL n vlNodes+            r' <- case vlOp of+                TerOp t c1 c2 c3 -> do+                    c1' <- translate vlNodes c1+                    c2' <- translate vlNodes c2+                    c3' <- translate vlNodes c3+                    lift $ translateTerOp t c1' c2' c3'+                BinOp b c1 c2    -> do+                    c1' <- translate vlNodes c1+                    c2' <- translate vlNodes c2+                    lift $ translateBinOp b c1' c2'+                UnOp u c1        -> do+                    c1' <- translate vlNodes c1+                    lift $ translateUnOp u c1'+                NullaryOp o      -> lift $ translateNullary o++            insertTranslation n r'+            return r'++getVL :: AlgNode -> NodeMap V.VL -> V.VL+getVL n vlNodes = case IM.lookup n vlNodes of+    Just op -> op+    Nothing -> error $ "getVL: node " ++ (show n) ++ " not in VL nodes map " ++ (pp vlNodes)++pp :: NodeMap V.VL -> String+pp m = intercalate ",\n" $ map show $ IM.toList m++vl2Algebra :: VectorAlgebra v a => NodeMap V.VL -> Shape VLDVec -> VecBuild a v (Shape v)+vl2Algebra vlNodes plan = do+    mapM_ (translate vlNodes) roots++    refreshShape plan+  where+    roots :: [AlgNode]+    roots = shapeNodes plan++translateTerOp :: VectorAlgebra v a => V.TerOp -> Res v -> Res v -> Res v -> B.Build a (Res v)+translateTerOp t c1 c2 c3 =+    case t of+        V.Combine -> do+            (d, r1, r2) <- vecCombine (toDVec c1) (toDVec c2) (toDVec c3)+            return $ RTriple (fromDVec d) (fromRVec r1) (fromRVec r2)++translateBinOp :: VectorAlgebra v a => V.BinOp -> Res v -> Res v -> B.Build a (Res v)+translateBinOp b c1 c2 = case b of+    V.DistLift -> do+        (v, p) <- vecDistLift (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromPVec p)++    V.PropRename -> fromDVec <$> vecPropRename (toRVec c1) (toDVec c2)++    V.PropFilter -> do+        (v, r) <- vecPropFilter (toRVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.PropReorder -> do+        (v, p) <- vecPropReorder (toPVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromPVec p)++    V.UnboxNested -> do+        (v, r) <- vecUnboxNested (toRVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.UnboxScalar -> RDVec <$> vecUnboxScalar (toDVec c1) (toDVec c2)++    V.Append -> do+        (v, r1, r2) <- vecAppend (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromRVec r1) (fromRVec r2)++    V.AppendS -> do+        (v, r1, r2) <- vecAppendS (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromRVec r1) (fromRVec r2)++    V.AggrS a -> fromDVec <$> vecAggrS a (toDVec c1) (toDVec c2)+++    V.SelectPos o -> do+        (v, r, ru) <- vecSelectPos (toDVec c1) o (toDVec c2)+        return $ RTriple (fromDVec v) (fromRVec r) (fromRVec ru)++    V.SelectPosS o -> do+        (v, rp, ru) <- vecSelectPosS (toDVec c1) o (toDVec c2)+        return $ RTriple (fromDVec v) (fromRVec rp) (fromRVec ru)++    V.Zip -> fromDVec <$> vecZip (toDVec c1) (toDVec c2)+    V.Align -> fromDVec <$> vecZip (toDVec c1) (toDVec c2)++    V.ZipS -> do+        (v, r1 ,r2) <- vecZipS (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromRVec r1) (fromRVec r2)++    V.CartProduct -> do+        (v, p1, p2) <- vecCartProduct (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.CartProductS -> do+        (v, p1, p2) <- vecCartProductS (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.NestProductS -> do+        (v, p2) <- vecNestProductS (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromPVec p2)++    V.ThetaJoin p -> do+        (v, p1, p2) <- vecThetaJoin p (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.NestProduct -> do+        (v, p1, p2) <- vecNestProduct (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.NestJoin p -> do+        (v, p1, p2) <- vecNestJoin p (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.ThetaJoinS p -> do+        (v, p1, p2) <- vecThetaJoinS p (toDVec c1) (toDVec c2)+        return $ RTriple (fromDVec v) (fromPVec p1) (fromPVec p2)++    V.NestJoinS p -> do+        (v, p2) <- vecNestJoinS p (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromPVec p2)++    V.SemiJoin p -> do+        (v, r) <- vecSemiJoin p (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.SemiJoinS p -> do+        (v, r) <- vecSemiJoinS p (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.AntiJoin p -> do+        (v, r) <- vecAntiJoin p (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.AntiJoinS p -> do+        (v, r) <- vecAntiJoinS p (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec v) (fromRVec r)++    V.TransposeS -> do+        (qo, qi) <- vecTransposeS (toDVec c1) (toDVec c2)+        return $ RLPair (fromDVec qo) (fromDVec qi)++translateUnOp :: VectorAlgebra v a => V.UnOp -> Res v -> B.Build a (Res v)+translateUnOp unop c = case unop of+    V.AggrNonEmptyS a  -> fromDVec <$> vecAggrNonEmptyS a (toDVec c)+    V.UniqueS          -> fromDVec <$> vecUniqueS (toDVec c)+    V.Number           -> fromDVec <$> vecNumber (toDVec c)+    V.NumberS          -> fromDVec <$> vecNumberS (toDVec c)+    V.UnboxRename      -> fromRVec <$> descToRename (toDVec c)+    V.Segment          -> fromDVec <$> vecSegment (toDVec c)+    V.Unsegment        -> fromDVec <$> vecUnsegment (toDVec c)+    V.Aggr a           -> fromDVec <$> vecAggr a (toDVec c)+    V.WinFun  (a, w)   -> fromDVec <$> vecWinFun a w (toDVec c)+    V.AggrNonEmpty as  -> fromDVec <$> vecAggrNonEmpty as (toDVec c)+    V.Select e         -> do+        (d, r) <- vecSelect e (toDVec c)+        return $ RLPair (fromDVec d) (fromRVec r)+    V.SortS es         -> do+        (d, p) <- vecSortS es (toDVec c)+        return $ RLPair (fromDVec d) (fromPVec p)+    V.GroupS es -> do+        (qo, qi, p) <- vecGroupS es (toDVec c)+        return $ RTriple (fromDVec qo) (fromDVec qi) (fromPVec p)+    V.Project cols -> fromDVec <$> vecProject cols (toDVec c)+    V.Reverse      -> do+        (d, p) <- vecReverse (toDVec c)+        return $ RLPair (fromDVec d) (fromPVec p)+    V.ReverseS      -> do+        (d, p) <- vecReverseS (toDVec c)+        return $ RLPair (fromDVec d) (fromPVec p)+    V.SelectPos1 (op, pos) -> do+        (d, p, u) <- vecSelectPos1 (toDVec c) op pos+        return $ RTriple (fromDVec d) (fromRVec p) (fromRVec u)+    V.SelectPos1S (op, pos) -> do+        (d, p, u) <- vecSelectPos1S (toDVec c) op pos+        return $ RTriple (fromDVec d) (fromRVec p) (fromRVec u)+    V.GroupAggr (g, as) -> fromDVec <$> vecGroupAggr g as (toDVec c)++    V.Reshape n -> do+        (qo, qi) <- vecReshape n (toDVec c)+        return $ RLPair (fromDVec qo) (fromDVec qi)+    V.ReshapeS n -> do+        (qo, qi) <- vecReshapeS n (toDVec c)+        return $ RLPair (fromDVec qo) (fromDVec qi)+    V.Transpose -> do+        (qo, qi) <- vecTranspose (toDVec c)+        return $ RLPair (fromDVec qo) (fromDVec qi)+    V.R1            -> case c of+        (RLPair c1 _)     -> return c1+        (RTriple c1 _ _) -> return c1+        _                -> error "R1: Not a tuple"+    V.R2            -> case c of+        (RLPair _ c2)     -> return c2+        (RTriple _ c2 _) -> return c2+        _                -> error "R2: Not a tuple"+    V.R3            -> case c of+        (RTriple _ _ c3) -> return c3+        _                -> error "R3: Not a tuple"++translateNullary :: VectorAlgebra v a => V.NullOp -> B.Build a (Res v)+translateNullary V.SingletonDescr          = fromDVec <$> singletonDescr+translateNullary (V.Lit (_, tys, vals))    = fromDVec <$> vecLit tys vals+translateNullary (V.TableRef (n, tys, hs)) = fromDVec <$> vecTableRef n tys hs++-- | Insert SerializeRel operators in TA.TableAlgebra plans to define+-- descr and order columns as well as the required payload columns.+-- FIXME: once we are a bit more flexible wrt surrogates, determine the+-- surrogate (i.e. descr) columns from information in NDVec.+insertSerialize :: VecBuild TA.TableAlgebra NDVec (Shape NDVec) +                -> VecBuild TA.TableAlgebra NDVec (Shape NDVec)+insertSerialize g = g >>= traverseShape++  where+    traverseShape :: Shape NDVec -> VecBuild TA.TableAlgebra NDVec (Shape NDVec)+    traverseShape (VShape dvec lyt) = do+        mLyt' <- traverseLayout lyt+        case mLyt' of+            Just lyt' -> do+                dvec' <- insertOp dvec noDescr needAbsPos+                return $ VShape dvec' lyt'+            Nothing   -> do+                dvec' <- insertOp dvec noDescr needRelPos+                return $ VShape dvec' lyt++    traverseShape (SShape dvec lyt)     = do+        mLyt' <- traverseLayout lyt+        case mLyt' of+            Just lyt' -> do+                dvec' <- insertOp dvec noDescr needAbsPos+                return $ SShape dvec' lyt'+            Nothing   -> do+                dvec' <- insertOp dvec noDescr noPos+                return $ SShape dvec' lyt++    traverseLayout :: (Layout NDVec) -> VecBuild TA.TableAlgebra NDVec (Maybe (Layout NDVec))+    traverseLayout (LCol _) = return Nothing+    traverseLayout (LTuple lyts) = do+        mLyts <- mapM traverseLayout lyts+        if all isNothing mLyts+            then return Nothing+            else return $ Just $ LTuple $ zipWith (\l ml -> maybe l id ml) lyts mLyts+    traverseLayout (LNest dvec lyt) = do+        mLyt' <- traverseLayout lyt+        case mLyt' of+            Just lyt' -> do+                dvec' <- insertOp dvec needDescr needAbsPos+                return $ Just $ LNest dvec' lyt'+            Nothing   -> do+                dvec' <- insertOp dvec needDescr needRelPos+                return $ Just $ LNest dvec' lyt+++    -- | Insert a Serialize node for the given vector+    insertOp :: NDVec -> Maybe TA.DescrCol -> TA.SerializeOrder -> VecBuild TA.TableAlgebra NDVec NDVec+    insertOp (ADVec q cols) descr pos = do+        let cs = map (TA.PayloadCol . ("item" ++) . show) cols+            op = TA.Serialize (descr, pos, cs)++        qp   <- lift $ B.insert $ UnOp op q+        return $ ADVec qp cols++    needDescr = Just (TA.DescrCol "descr")+    noDescr   = Nothing++    needAbsPos = TA.AbsPos "pos"+    needRelPos = TA.RelPos ["pos"]+    noPos      = TA.NoPos++implementVectorOpsPF :: QueryPlan V.VL VLDVec -> QueryPlan TA.TableAlgebra NDVec+implementVectorOpsPF vlPlan = mkQueryPlan dag shape tagMap+  where+    taPlan               = vl2Algebra (D.nodeMap $ queryDag vlPlan) (queryShape vlPlan)+    serializedPlan       = insertSerialize taPlan+    (dag, shape, tagMap) = runVecBuild serializedPlan
+ src/Database/DSH/VL/Lang.hs view
@@ -0,0 +1,194 @@+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE GADTs                #-}+{-# LANGUAGE RankNTypes           #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Database.DSH.VL.Lang where++import qualified Data.List.NonEmpty as N+import           Data.Aeson.TH++import           Database.Algebra.Dag        (Operator, opChildren, replaceOpChild)+import           Database.Algebra.Dag.Common++import qualified Database.DSH.Common.Lang as L++data ScalarType = Int +                | Bool +                | Double+                | String +                | Unit+             deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''ScalarType)++type VLColumn = (L.ColName, ScalarType)+type DBCol = Int++data VLVal = VLInt Int+           | VLBool Bool+           | VLString String+           | VLDouble Double+           | VLUnit+           deriving (Eq, Ord, Show, Read)++$(deriveJSON defaultOptions ''VLVal)++data Expr = BinApp L.ScalarBinOp Expr Expr+          | UnApp L.ScalarUnOp Expr+          | Column DBCol+          | Constant VLVal+          | If Expr Expr Expr+          deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''Expr)++-- | Helper function: Shift all column indexes in an expression by a certain offset.+shiftExprCols :: Int -> Expr -> Expr+shiftExprCols o (BinApp op e1 e2) = BinApp op (shiftExprCols o e1) +                                              (shiftExprCols o e2)+shiftExprCols o (UnApp op e)      = UnApp op (shiftExprCols o e)+shiftExprCols o (Column c)        = Column $ c + o+shiftExprCols _ (Constant v)      = Constant v+shiftExprCols o (If c t e)        = If (shiftExprCols o c) +                                       (shiftExprCols o t) +                                       (shiftExprCols o e)++data AggrFun = AggrSum ScalarType Expr+             | AggrMin Expr+             | AggrMax Expr+             | AggrAvg Expr+             | AggrAll Expr+             | AggrAny Expr+             | AggrCount+             deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''AggrFun)++data WinFun = WinSum Expr+            | WinMin Expr+            | WinMax Expr+            | WinAvg Expr+            | WinAll Expr+            | WinAny Expr+            | WinFirstValue Expr+            | WinCount+            deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''WinFun)+++-- | Specification of a window for the window aggregate operator.+data FrameSpec = -- | All elements up to and including the current+                 -- element are in the window+                 FAllPreceding+                 -- | All n preceding elements up to and including the+                 -- current one.+               | FNPreceding Int+                deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''FrameSpec)++--------------------------------------------------------------------------------+-- Vector Language operators. Documentation can be found in module+-- VectorPrimitives.++data NullOp = SingletonDescr+            | Lit (L.Emptiness, [ScalarType], [[VLVal]])+            | TableRef (String, [VLColumn], L.TableHints)+            deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''NullOp)++data UnOp = UnboxRename+          | Segment+          | Unsegment++          | R1+          | R2+          | R3++          | Project [Expr]+          | Select Expr++          | GroupAggr ([Expr], N.NonEmpty AggrFun)+          | Aggr AggrFun+          | AggrNonEmpty (N.NonEmpty AggrFun)+          | AggrNonEmptyS (N.NonEmpty AggrFun)++          | Number+          | NumberS+          | UniqueS+          | Reverse+          | ReverseS+          | SelectPos1 (L.ScalarBinOp, Int)+          | SelectPos1S (L.ScalarBinOp, Int)+          | SortS [Expr]+          | GroupS [Expr]+          | WinFun (WinFun, FrameSpec)++          | Reshape Integer+          | ReshapeS Integer+          | Transpose+    deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''UnOp)++data BinOp = DistLift++           | PropRename+           | PropFilter+           | PropReorder+           +           | UnboxNested+           | UnboxScalar+           | Align++           | AggrS AggrFun+           | Append+           | AppendS+           | SelectPos L.ScalarBinOp+           | SelectPosS L.ScalarBinOp+           | Zip+           | ZipS+           | CartProduct+           | CartProductS+           | ThetaJoin (L.JoinPredicate Expr)+           | ThetaJoinS (L.JoinPredicate Expr)+           | SemiJoin (L.JoinPredicate Expr)+           | SemiJoinS (L.JoinPredicate Expr)+           | AntiJoin (L.JoinPredicate Expr)+           | AntiJoinS (L.JoinPredicate Expr)+           | NestJoin (L.JoinPredicate Expr)+           | NestJoinS (L.JoinPredicate Expr)+           | NestProduct+           | NestProductS+           | TransposeS+    deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''BinOp)++data TerOp = Combine  -- (DBV, RenameVector, RenameVector)+    deriving (Eq, Ord, Show)++$(deriveJSON defaultOptions ''TerOp)++type VL = Algebra TerOp BinOp UnOp NullOp AlgNode++checkRep :: Eq a => a -> a -> a -> a+checkRep orig new x = if x == orig then new else x++instance Operator VL where+    opChildren (TerOp _ c1 c2 c3) = [c1, c2, c3]+    opChildren (BinOp _ c1 c2) = [c1, c2]+    opChildren (UnOp _ c) = [c]+    opChildren (NullaryOp _) = []++    replaceOpChild oper old new = replaceChild old new oper+     where+         replaceChild :: forall t b u n c. Eq c => c -> c -> Algebra t b u n c -> Algebra t b u n c+         replaceChild o n (TerOp op c1 c2 c3) = TerOp op (checkRep o n c1) (checkRep o n c2) (checkRep o n c3)+         replaceChild o n (BinOp op c1 c2) = BinOp op (checkRep o n c1) (checkRep o n c2)+         replaceChild o n (UnOp op c) = UnOp op (checkRep o n c)+         replaceChild _ _ (NullaryOp op) = NullaryOp op
+ src/Database/DSH/VL/Primitives.hs view
@@ -0,0 +1,341 @@+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE TypeSynonymInstances #-}++module Database.DSH.VL.Primitives where++import           Database.DSH.Common.Nat+import qualified Database.DSH.Common.Lang      as L+import qualified Database.DSH.Common.Type      as Ty+import           Database.DSH.VL.Vector++import           Database.DSH.Impossible++import           Database.Algebra.Dag.Build+import           Database.Algebra.Dag.Common+import           Database.DSH.VL.Lang          hiding (DBCol)+import qualified Database.DSH.VL.Lang          as D++--------------------------------------------------------------------------------+-- Construct different types of vectors from algebraic nodes++type VecConst r v = Build VL AlgNode -> Build VL v++dvec :: VecConst r VLDVec+dvec = fmap VLDVec++pvec :: Build a AlgNode -> Build a PVec+pvec = fmap PVec++rvec :: Build a AlgNode -> Build a RVec+rvec = fmap RVec+     +--------------------------------------------------------------------------------+-- Insert VL operators and appropriate R1/R2/R3 nodes++vec :: VL -> VecConst r a -> Build VL a+vec op mkVec = mkVec $ insert op++pairVec :: VL -> VecConst r a -> VecConst r b -> Build VL (a, b)+pairVec op mkVec1 mkVec2 = do+    r <- insert op+    r1 <- mkVec1 $ insert $ UnOp R1 r+    r2 <- mkVec2 $ insert $ UnOp R2 r+    return (r1, r2)++tripleVec :: VL +          -> VecConst r a +          -> VecConst r b +          -> VecConst r c +          -> Build VL (a, b ,c)+tripleVec op mkVec1 mkVec2 mkVec3 = do+    r <- insert op+    r1 <- mkVec1 $ insert $ UnOp R1 r+    r2 <- mkVec2 $ insert $ UnOp R2 r+    r3 <- mkVec3 $ insert $ UnOp R3 r+    return (r1, r2, r3)++--------------------------------------------------------------------------------++mapSnd :: (b -> c) -> (a, b) -> (a, c)+mapSnd f (a, b) = (a, f b)++pVal :: L.Val -> VLVal+pVal (L.IntV i)    = VLInt i+pVal (L.BoolV b)   = VLBool b+pVal (L.StringV s) = VLString s+pVal (L.DoubleV d) = VLDouble d+pVal L.UnitV       = VLUnit+pVal _             = error "pVal: Not a supported value"++typeToScalarType :: Ty.Type -> ScalarType+typeToScalarType t = case t of+  Ty.IntT      -> D.Int+  Ty.BoolT     -> D.Bool+  Ty.StringT   -> D.String+  Ty.UnitT     -> D.Unit+  Ty.DoubleT   -> D.Double+  Ty.ListT _   -> $impossible+  Ty.TupleT _  -> $impossible++----------------------------------------------------------------------------------+-- Convert join expressions into regular VL expressions++-- | Determine the horizontal relational schema width of a type+recordWidth :: Ty.Type -> Int+recordWidth t =+    case t of+        Ty.IntT        -> 1+        Ty.BoolT       -> 1+        Ty.DoubleT     -> 1+        Ty.StringT     -> 1+        Ty.UnitT       -> 1+        Ty.TupleT ts   -> sum $ map recordWidth ts+        Ty.ListT _     -> 0++data ColExpr = Offset Int | Expr Expr++-- | If the child expressions are tuple operators which only give the+-- column offset, convert it into a proper expression first.+offsetExpr :: ColExpr -> Expr+offsetExpr (Offset o) = Column $ o + 1+offsetExpr (Expr e)   = e++addOffset :: Int -> ColExpr -> ColExpr+addOffset _ (Expr _)   = $impossible+addOffset i (Offset o) = Offset $ o + i++toGeneralBinOp :: L.JoinBinOp -> L.ScalarBinOp+toGeneralBinOp (L.JBNumOp o)    = L.SBNumOp o+toGeneralBinOp (L.JBStringOp o) = L.SBStringOp o++toGeneralUnOp :: L.JoinUnOp -> L.ScalarUnOp+toGeneralUnOp (L.JUNumOp o)  = L.SUNumOp o+toGeneralUnOp (L.JUCastOp o) = L.SUCastOp o+toGeneralUnOp (L.JUTextOp o) = L.SUTextOp o++toVLjoinConjunct :: L.JoinConjunct L.JoinExpr -> L.JoinConjunct Expr+toVLjoinConjunct (L.JoinConjunct e1 o e2) = +    L.JoinConjunct (joinExpr e1) o (joinExpr e2)++toVLJoinPred :: L.JoinPredicate L.JoinExpr -> L.JoinPredicate Expr+toVLJoinPred (L.JoinPred cs) = L.JoinPred $ fmap toVLjoinConjunct cs++-- | Convert join expressions into VL expressions. The main challenge+-- here is to convert sequences of tuple accessors (fst/snd) into VL+-- column indices.+joinExpr :: L.JoinExpr -> Expr+joinExpr expr = offsetExpr $ aux expr+  where+    -- Construct expressions in a bottom-up way. For a given join+    -- expression, return the following:+    -- pair accessors   -> column offset in the flat relational representation+    -- scalar operation -> corresponding VL expression+    aux :: L.JoinExpr -> ColExpr+    -- FIXME VL joins should include join expressions!+    aux (L.JBinOp _ op e1 e2)  = Expr $ BinApp (toGeneralBinOp op)+                                               (offsetExpr $ aux e1)+                                               (offsetExpr $ aux e2)+    aux (L.JUnOp _ op e)       = Expr $ UnApp (toGeneralUnOp op) (offsetExpr $ aux e)+    aux (L.JTupElem _ i e)           =+        case Ty.typeOf e of+            -- Compute the record width of all preceding tuple elements in the type+            Ty.TupleT ts -> addOffset (sum $ map recordWidth $ take (tupleIndex i - 1) ts) (aux e)+            _            -> $impossible+    aux (L.JLit _ v)           = Expr $ Constant $ pVal v+    aux (L.JInput _)           = Offset 0+++----------------------------------------------------------------------------------+-- DAG constructor functions for VL operators++vlUniqueS :: VLDVec -> Build VL VLDVec+vlUniqueS (VLDVec c) = vec (UnOp UniqueS c) dvec++vlNumber :: VLDVec -> Build VL VLDVec+vlNumber (VLDVec c) = vec (UnOp Number c) dvec++vlNumberS :: VLDVec -> Build VL VLDVec+vlNumberS (VLDVec c) = vec (UnOp NumberS c) dvec++vlGroupS :: [Expr] -> VLDVec -> Build VL (VLDVec, VLDVec, PVec)+vlGroupS groupExprs (VLDVec c) = tripleVec (UnOp (GroupS groupExprs) c) dvec dvec pvec++vlSortS :: [Expr] -> VLDVec -> Build VL (VLDVec, PVec)+vlSortS sortExprs (VLDVec c1) = pairVec (UnOp (SortS sortExprs) c1) dvec pvec++vlAggr :: AggrFun -> VLDVec -> Build VL VLDVec+vlAggr aFun (VLDVec c) = vec (UnOp (Aggr aFun) c) dvec++vlAggrS :: AggrFun -> VLDVec -> VLDVec -> Build VL VLDVec+vlAggrS aFun (VLDVec c1) (VLDVec c2) = vec (BinOp (AggrS aFun) c1 c2) dvec++vlUnboxRename :: VLDVec -> Build VL RVec+vlUnboxRename (VLDVec c) = vec (UnOp UnboxRename c) rvec++vlNestProduct :: VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlNestProduct (VLDVec c1) (VLDVec c2) = tripleVec (BinOp NestProduct c1 c2) dvec pvec pvec++vlDistLift :: VLDVec -> VLDVec -> Build VL (VLDVec, PVec)+vlDistLift (VLDVec c1) (VLDVec c2) = pairVec (BinOp DistLift c1 c2) dvec pvec++vlPropRename :: RVec -> VLDVec -> Build VL VLDVec+vlPropRename (RVec c1) (VLDVec c2) = vec (BinOp PropRename c1 c2) dvec++vlUnboxNested :: RVec -> VLDVec -> Build VL (VLDVec, RVec)+vlUnboxNested (RVec c1) (VLDVec c2) = pairVec (BinOp UnboxNested c1 c2) dvec rvec++vlUnboxScalar :: VLDVec -> VLDVec -> Build VL VLDVec+vlUnboxScalar (VLDVec c1) (VLDVec c2) = vec (BinOp UnboxScalar c1 c2) dvec++vlPropFilter :: RVec -> VLDVec -> Build VL (VLDVec, RVec)+vlPropFilter (RVec c1) (VLDVec c2) = pairVec (BinOp PropFilter c1 c2) dvec rvec++vlPropReorder :: PVec -> VLDVec -> Build VL (VLDVec, PVec)+vlPropReorder (PVec c1) (VLDVec c2) = pairVec (BinOp PropReorder c1 c2) dvec pvec++vlSingletonDescr :: Build VL VLDVec+vlSingletonDescr = vec (NullaryOp SingletonDescr) dvec++vlAppend :: VLDVec -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlAppend (VLDVec c1) (VLDVec c2) = tripleVec (BinOp Append c1 c2) dvec rvec rvec++vlAppendS :: VLDVec -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlAppendS (VLDVec c1) (VLDVec c2) = tripleVec (BinOp AppendS c1 c2) dvec rvec rvec++vlSegment :: VLDVec -> Build VL VLDVec+vlSegment (VLDVec c) = vec (UnOp Segment c) dvec++vlUnsegment :: VLDVec -> Build VL VLDVec+vlUnsegment (VLDVec c) = vec (UnOp Unsegment c) dvec++vlCombine :: VLDVec -> VLDVec -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlCombine (VLDVec c1) (VLDVec c2) (VLDVec c3) = +    tripleVec (TerOp Combine c1 c2 c3) dvec rvec rvec++vlLit :: L.Emptiness -> [Ty.Type] -> [[VLVal]] -> Build VL VLDVec+vlLit em tys vals = vec (NullaryOp $ Lit (em, map typeToScalarType tys, vals)) dvec++vlTableRef :: String -> [VLColumn] -> L.TableHints -> Build VL VLDVec+vlTableRef n tys hs = vec (NullaryOp $ TableRef (n, tys, hs)) dvec++vlUnExpr :: L.ScalarUnOp -> VLDVec -> Build VL VLDVec+vlUnExpr o (VLDVec c) = vec (UnOp (Project [UnApp o (Column 1)]) c) dvec++vlBinExpr :: L.ScalarBinOp -> VLDVec -> VLDVec -> Build VL VLDVec+vlBinExpr o (VLDVec c1) (VLDVec c2) = do+    z <- insert $ BinOp Align c1 c2+    r <- dvec $ insert $ UnOp (Project [BinApp o (Column 1) (Column 2)]) z+    return r++vlSelect :: Expr -> VLDVec -> Build VL (VLDVec, RVec)+vlSelect p (VLDVec c) = pairVec (UnOp (Select p) c) dvec rvec++vlSelectPos :: VLDVec -> L.ScalarBinOp -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlSelectPos (VLDVec c1) op (VLDVec c2) = tripleVec (BinOp (SelectPos op) c1 c2) dvec rvec rvec++vlSelectPos1 :: VLDVec -> L.ScalarBinOp -> Int -> Build VL (VLDVec, RVec, RVec)+vlSelectPos1 (VLDVec c1) op posConst = +    tripleVec (UnOp (SelectPos1 (op, posConst)) c1) dvec rvec rvec++vlSelectPosS :: VLDVec -> L.ScalarBinOp -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlSelectPosS (VLDVec c1) op (VLDVec c2) = do+    tripleVec (BinOp (SelectPosS op) c1 c2) dvec rvec rvec++vlSelectPos1S :: VLDVec -> L.ScalarBinOp -> Int -> Build VL (VLDVec, RVec, RVec)+vlSelectPos1S (VLDVec c1) op posConst = +    tripleVec (UnOp (SelectPos1S (op, posConst)) c1) dvec rvec rvec++vlProject :: [Expr] -> VLDVec -> Build VL VLDVec+vlProject projs (VLDVec c) = dvec $ insert $ UnOp (Project projs) c++vlZip :: VLDVec -> VLDVec -> Build VL VLDVec+vlZip (VLDVec c1) (VLDVec c2) = vec (BinOp Zip c1 c2) dvec++vlAlign :: VLDVec -> VLDVec -> Build VL VLDVec+vlAlign (VLDVec c1) (VLDVec c2) = vec (BinOp Align c1 c2) dvec++vlZipS :: VLDVec -> VLDVec -> Build VL (VLDVec, RVec, RVec)+vlZipS (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp ZipS c1 c2) dvec rvec rvec++vlCartProduct :: VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlCartProduct (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp CartProduct c1 c2) dvec pvec pvec++vlCartProductS :: VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlCartProductS (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp CartProductS c1 c2) dvec pvec pvec++vlThetaJoin :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlThetaJoin joinPred (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp (ThetaJoin joinPred') c1 c2) dvec pvec pvec+  where+    joinPred' = toVLJoinPred joinPred++vlNestJoin :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlNestJoin joinPred (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp (NestJoin joinPred') c1 c2) dvec pvec pvec+  where+    joinPred' = toVLJoinPred joinPred++vlThetaJoinS :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, PVec, PVec)+vlThetaJoinS joinPred (VLDVec c1) (VLDVec c2) =+    tripleVec (BinOp (ThetaJoinS joinPred') c1 c2) dvec pvec pvec+  where+    joinPred' = toVLJoinPred joinPred++vlNestJoinS :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, PVec)+vlNestJoinS joinPred (VLDVec c1) (VLDVec c2) =+    pairVec (BinOp (NestJoinS joinPred') c1 c2) dvec pvec+  where+    joinPred' = toVLJoinPred joinPred++vlNestProductS :: VLDVec -> VLDVec -> Build VL (VLDVec, PVec)+vlNestProductS (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp NestProductS c1 c2) dvec pvec++vlSemiJoin :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, RVec)+vlSemiJoin joinPred (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp (SemiJoin joinPred') c1 c2) dvec rvec+  where+    joinPred' = toVLJoinPred joinPred++vlSemiJoinS :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, RVec)+vlSemiJoinS joinPred (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp (SemiJoinS joinPred') c1 c2) dvec rvec+  where+    joinPred' = toVLJoinPred joinPred++vlAntiJoin :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, RVec)+vlAntiJoin joinPred (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp (AntiJoin joinPred') c1 c2) dvec rvec+  where+    joinPred' = toVLJoinPred joinPred++vlAntiJoinS :: L.JoinPredicate L.JoinExpr -> VLDVec -> VLDVec -> Build VL (VLDVec, RVec)+vlAntiJoinS joinPred (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp (AntiJoinS joinPred') c1 c2) dvec rvec+  where+    joinPred' = toVLJoinPred joinPred++vlReverse :: VLDVec -> Build VL (VLDVec, PVec)+vlReverse (VLDVec c) = pairVec (UnOp Reverse c) dvec pvec++vlReverseS :: VLDVec -> Build VL (VLDVec, PVec)+vlReverseS (VLDVec c) = pairVec (UnOp ReverseS c) dvec pvec++vlTranspose :: VLDVec -> Build VL (VLDVec, VLDVec)+vlTranspose (VLDVec c) = pairVec (UnOp Transpose c) dvec dvec++vlTransposeS :: VLDVec -> VLDVec -> Build VL (VLDVec, VLDVec)+vlTransposeS (VLDVec c1) (VLDVec c2) = do+    pairVec (BinOp TransposeS c1 c2) dvec dvec++vlReshape :: Integer -> VLDVec -> Build VL (VLDVec, VLDVec)+vlReshape n (VLDVec c) = do+    pairVec (UnOp (Reshape n) c) dvec dvec++vlReshapeS :: Integer -> VLDVec -> Build VL (VLDVec, VLDVec)+vlReshapeS n (VLDVec c) = do+    pairVec (UnOp (ReshapeS n) c) dvec dvec
+ src/Database/DSH/VL/Render/Dot.hs view
@@ -0,0 +1,371 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.VL.Render.Dot(renderVLDot, renderTblVal) where++import qualified Data.IntMap                 as Map+import qualified Data.List.NonEmpty          as N+import           Data.List++import           Text.PrettyPrint++import qualified Database.Algebra.Dag        as Dag+import           Database.Algebra.Dag.Common as C++import           Database.DSH.Common.Pretty+import           Database.DSH.Common.Lang+import           Database.DSH.VL.Lang++nodeToDoc :: AlgNode -> Doc+nodeToDoc n = (text "id:") <+> (int n)++tagsToDoc :: [Tag] -> Doc+tagsToDoc ts = vcat $ map text ts++labelToDoc :: AlgNode -> String -> Doc -> [Tag] -> Doc+labelToDoc n s as ts = (nodeToDoc n) $$ ((text s) <> (parens as)) $$ (tagsToDoc $ nub ts)++lookupTags :: AlgNode -> NodeMap [Tag] -> [Tag]+lookupTags n m = Map.findWithDefault [] n m++renderFun :: Doc -> [Doc] -> Doc+renderFun name args = name <> parens (hsep $ punctuate comma args)++renderFrameSpec :: FrameSpec -> Doc+renderFrameSpec FAllPreceding   = text "allprec"+renderFrameSpec (FNPreceding n) = int n <+> text "prec"++renderAggrFun :: AggrFun -> Doc+renderAggrFun (AggrSum t c) = renderFun (text "sum" <> char '_' <> renderColumnType t) +                                        [renderExpr c]+renderAggrFun (AggrMin c)   = renderFun (text "min") [renderExpr c]+renderAggrFun (AggrMax c)   = renderFun (text "max") [renderExpr c]+renderAggrFun (AggrAvg c)   = renderFun (text "avg") [renderExpr c]+renderAggrFun (AggrAny c)   = renderFun (text "any") [renderExpr c]+renderAggrFun (AggrAll c)   = renderFun (text "all") [renderExpr c]+renderAggrFun AggrCount     = renderFun (text "count") []++renderWinFun :: WinFun -> Doc+renderWinFun (WinSum c)        = renderFun (text "sum") [renderExpr c]+renderWinFun (WinMin c)        = renderFun (text "min") [renderExpr c]+renderWinFun (WinMax c)        = renderFun (text "max") [renderExpr c]+renderWinFun (WinAvg c)        = renderFun (text "avg") [renderExpr c]+renderWinFun (WinAny c)        = renderFun (text "any") [renderExpr c]+renderWinFun (WinAll c)        = renderFun (text "all") [renderExpr c]+renderWinFun (WinFirstValue c) = renderFun (text "first_value") [renderExpr c]+renderWinFun WinCount          = renderFun (text "count") []++renderColumnType :: ScalarType -> Doc+renderColumnType = text . show++renderData :: [[VLVal]] -> Doc+renderData [] = brackets empty+renderData xs = (flip (<>) semi . sep . punctuate semi . map renderRow) xs++renderRow :: [VLVal] -> Doc+renderRow = hcat . punctuate comma . map renderTblVal++renderTblVal :: VLVal -> Doc+renderTblVal (VLInt i) = integer $ fromIntegral i+renderTblVal (VLBool b) = text $ show b+renderTblVal (VLString s) = doubleQuotes $ text $ escape s+renderTblVal (VLDouble d) = double d+renderTblVal VLUnit = text "()"++escape :: String -> String+escape (x@'\\':xs) = '\\':'\\':'\\':x:escape xs+escape (x@'\'':xs) = '\\':x:escape xs+escape (x@'"':xs) = '\\':'\\':x:escape xs+escape (x:xs) = x:escape xs+escape [] = []++bracketList :: (a -> Doc) -> [a] -> Doc+bracketList f = brackets . hsep . punctuate comma . map f++renderColName :: ColName -> Doc+renderColName (ColName c) = text c++renderTableType :: VLColumn -> Doc+renderTableType (c, t) = renderColName c <> text "::" <> renderColumnType t++renderTableHints :: TableHints -> Doc+renderTableHints hs = renderTableKeys (keysHint hs) <> renderEmptiness (nonEmptyHint hs)++renderEmptiness :: Emptiness -> Doc+renderEmptiness NonEmpty      = text " NONEMPTY"+renderEmptiness PossiblyEmpty = empty++renderTableKeys :: [Key] -> Doc+renderTableKeys [x]    = renderTableKey x+renderTableKeys (x:xs) = renderTableKey x $$ renderTableKeys xs+renderTableKeys []     = empty+++renderTableKey :: Key -> Doc+renderTableKey (Key ks) = hsep $ punctuate comma $ map renderColName ks++renderProj :: Doc -> Expr -> Doc+renderProj d e = d <> colon <> renderExpr e++renderJoinConjunct :: JoinConjunct Expr -> Doc+renderJoinConjunct (JoinConjunct e1 o e2) = +    parenthize1 e1 <+> (text $ show o) <+> (parenthize1 e2)++renderJoinPred :: JoinPredicate Expr -> Doc+renderJoinPred (JoinPred conjs) = brackets+                                  $ hsep +                                  $ punctuate (text "&&")+                                  $ map renderJoinConjunct $ N.toList conjs++renderExpr :: Expr -> Doc+renderExpr (BinApp op e1 e2) = (parenthize1 e1) <+> (text $ pp op) <+> (parenthize1 e2)+renderExpr (UnApp op e)      = (text $ pp op) <+> (parens $ renderExpr e)+renderExpr (Constant val)    = renderTblVal val+renderExpr (Column c)        = text "col" <> int c+renderExpr (If c t e)        = text "if" +                                 <+> renderExpr c +                                 <+> text "then" +                                 <+> renderExpr t +                                 <+> text "else" +                                 <+> renderExpr e++parenthize1 :: Expr -> Doc+parenthize1 e@(Constant _)   = renderExpr e+parenthize1 e@(Column _)     = renderExpr e+parenthize1 e@(BinApp _ _ _) = parens $ renderExpr e+parenthize1 e@(UnApp _ _)    = parens $ renderExpr e+parenthize1 e@(If _ _ _)     = renderExpr e++-- | Create the node label from an operator description+opDotLabel :: NodeMap [Tag] -> AlgNode -> VL -> Doc+opDotLabel tm i (UnOp (WinFun (wfun, wspec)) _) = labelToDoc i "WinAggr"+    (renderWinFun wfun <> comma <+> renderFrameSpec wspec)+    (lookupTags i tm)+opDotLabel tm i (NullaryOp (SingletonDescr)) = labelToDoc i "SingletonDescr" empty (lookupTags i tm)+opDotLabel tm i (NullaryOp (Lit (em, tys, vals))) = labelToDoc i "LIT"+        (renderEmptiness em <+> bracketList renderColumnType tys <> comma+        $$ renderData vals) (lookupTags i tm)+opDotLabel tm i (NullaryOp (TableRef (n, tys, hs))) = labelToDoc i "TableRef"+        (quotes (text n) <> comma <+> bracketList (\t -> renderTableType t <> text "\n") tys <> comma $$ renderTableHints hs)+        (lookupTags i tm)+opDotLabel tm i (UnOp UniqueS _) = labelToDoc i "UniqueS" empty (lookupTags i tm)+opDotLabel tm i (UnOp Number _) = labelToDoc i "Number" empty (lookupTags i tm)+opDotLabel tm i (UnOp NumberS _) = labelToDoc i "NumberS" empty (lookupTags i tm)+opDotLabel tm i (UnOp UnboxRename _) = labelToDoc i "UnboxRename" empty (lookupTags i tm)+opDotLabel tm i (UnOp Segment _) = labelToDoc i "Segment" empty (lookupTags i tm)+opDotLabel tm i (UnOp Unsegment _) = labelToDoc i "Unsegment" empty (lookupTags i tm)+opDotLabel tm i (UnOp Reverse _) = labelToDoc i "Reverse" empty (lookupTags i tm)+opDotLabel tm i (UnOp ReverseS _) = labelToDoc i "ReverseS" empty (lookupTags i tm)+opDotLabel tm i (UnOp R1 _) = labelToDoc i "R1" empty (lookupTags i tm)+opDotLabel tm i (UnOp R2 _) = labelToDoc i "R2" empty (lookupTags i tm)+opDotLabel tm i (UnOp R3 _) = labelToDoc i "R3" empty (lookupTags i tm)+opDotLabel tm i (UnOp (Project pCols) _) =+  labelToDoc i "Project" pLabel (lookupTags i tm)+  where pLabel = valCols+        valCols = bracketList (\(j, p) -> renderProj (itemLabel j) p) $ zip ([1..] :: [Int]) pCols+        itemLabel j = (text "i") <> (int j)+opDotLabel tm i (UnOp (Select e) _) = labelToDoc i "Select" (renderExpr e) (lookupTags i tm)+opDotLabel tm i (UnOp (SelectPos1 (o, p)) _)  = labelToDoc i "SelectPos1" ((text $ show o) <+> int p) (lookupTags i tm)+opDotLabel tm i (UnOp (SelectPos1S (o, p)) _) = labelToDoc i "SelectPos1S" ((text $ show o) <+> int p) (lookupTags i tm)+opDotLabel tm i (UnOp (GroupAggr (g, as)) _) = labelToDoc i "GroupAggr" (bracketList renderExpr g <+> bracketList renderAggrFun (N.toList as)) (lookupTags i tm)+opDotLabel tm i (UnOp (Aggr a) _) = labelToDoc i "Aggr" (renderAggrFun a) (lookupTags i tm)+opDotLabel tm i (UnOp (Reshape n) _) = +  labelToDoc i "Reshape" (integer n) (lookupTags i tm)+opDotLabel tm i (BinOp (AggrS a) _ _) = labelToDoc i "AggrS" (renderAggrFun a) (lookupTags i tm)+opDotLabel tm i (UnOp (AggrNonEmpty as) _) = labelToDoc i "AggrNonEmpty" (bracketList renderAggrFun (N.toList as)) (lookupTags i tm)+opDotLabel tm i (UnOp (AggrNonEmptyS as) _) = labelToDoc i "AggrNonEmptyS" (bracketList renderAggrFun (N.toList as)) (lookupTags i tm)+opDotLabel tm i (UnOp (SortS cols) _) = labelToDoc i "Sort" (bracketList renderExpr cols) (lookupTags i tm)+opDotLabel tm i (UnOp (GroupS cols) _) = labelToDoc i "GroupS" (bracketList renderExpr cols) (lookupTags i tm)+opDotLabel tm i (BinOp NestProduct _ _) = labelToDoc i "NestProduct" empty (lookupTags i tm)+opDotLabel tm i (BinOp DistLift _ _) = labelToDoc i "DistLift" empty (lookupTags i tm)+opDotLabel tm i (BinOp PropRename _ _) = labelToDoc i "PropRename" empty (lookupTags i tm)+opDotLabel tm i (BinOp UnboxNested _ _) = labelToDoc i "UnboxNested" empty (lookupTags i tm)+opDotLabel tm i (BinOp UnboxScalar _ _) = labelToDoc i "UnboxScalar" empty (lookupTags i tm)+opDotLabel tm i (BinOp PropFilter _ _) = labelToDoc i "PropFilter" empty (lookupTags i tm)+opDotLabel tm i (BinOp PropReorder _ _) = labelToDoc i "PropReorder" empty (lookupTags i tm)+opDotLabel tm i (BinOp Append _ _) = labelToDoc i "Append" empty (lookupTags i tm)+opDotLabel tm i (BinOp AppendS _ _) = labelToDoc i "AppendS" empty (lookupTags i tm)+opDotLabel tm i (BinOp (SelectPos o) _ _) = labelToDoc i "SelectPos" (text $ show o) (lookupTags i tm)+opDotLabel tm i (BinOp (SelectPosS o) _ _) = labelToDoc i "SelectPosS" (text $ show o) (lookupTags i tm)+opDotLabel tm i (BinOp Zip _ _) = labelToDoc i "Zip" empty (lookupTags i tm)+opDotLabel tm i (BinOp Align _ _) = labelToDoc i "Align" empty (lookupTags i tm)+opDotLabel tm i (BinOp ZipS _ _) = labelToDoc i "ZipS" empty (lookupTags i tm)+opDotLabel tm i (BinOp CartProduct _ _) = labelToDoc i "CartProduct" empty (lookupTags i tm)+opDotLabel tm i (BinOp CartProductS _ _) = labelToDoc i "CartProductS" empty (lookupTags i tm)+opDotLabel tm i (BinOp NestProductS _ _) = labelToDoc i "NestProductS" empty (lookupTags i tm)+opDotLabel tm i (BinOp (ThetaJoin p) _ _) =+  labelToDoc i "ThetaJoin" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (NestJoin p) _ _) =+  labelToDoc i "NestJoin" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (ThetaJoinS p) _ _) =+  labelToDoc i "ThetaJoinS" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (NestJoinS p) _ _) =+  labelToDoc i "NestJoinS" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (SemiJoin p) _ _) =+  labelToDoc i "SemiJoin" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (SemiJoinS p) _ _) =+  labelToDoc i "SemiJoinS" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (AntiJoin p) _ _) =+  labelToDoc i "AntiJoin" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (BinOp (AntiJoinS p) _ _) =+  labelToDoc i "AntiJoinS" (renderJoinPred p) (lookupTags i tm)+opDotLabel tm i (UnOp (ReshapeS n) _) = +  labelToDoc i "ReshapeS" (integer n) (lookupTags i tm)+opDotLabel tm i (UnOp Transpose _) = labelToDoc i "Transpose" empty (lookupTags i tm)+opDotLabel tm i (TerOp Combine _ _ _) = labelToDoc i "Combine" empty (lookupTags i tm)+opDotLabel tm i (BinOp TransposeS _ _) = labelToDoc i "TransposeS" empty (lookupTags i tm)++opDotColor :: VL -> DotColor+opDotColor (BinOp NestProduct _ _)       = DCRed+opDotColor (BinOp CartProduct _ _)       = DCRed+opDotColor (BinOp CartProductS _ _)      = DCRed+opDotColor (BinOp NestProductS _ _)      = DCRed+opDotColor (BinOp (ThetaJoin _) _ _)     = DCGreen+opDotColor (BinOp (NestJoin _) _ _)      = DCGreen+opDotColor (BinOp (ThetaJoinS _) _ _)    = DCGreen+opDotColor (BinOp (NestJoinS _) _ _)     = DCGreen+opDotColor (BinOp (SemiJoin _) _ _)      = DCGreen+opDotColor (BinOp (SemiJoinS _) _ _)     = DCGreen+opDotColor (BinOp (AntiJoin _) _ _)      = DCGreen+opDotColor (BinOp (AntiJoinS _) _ _)     = DCGreen+opDotColor (BinOp Zip _ _)               = DCYelloGreen+opDotColor (UnOp (SortS _) _)            = DCTomato+opDotColor (UnOp (GroupS _) _)           = DCTomato+opDotColor (BinOp PropRename _ _)        = DCTan+opDotColor (BinOp UnboxNested _ _)       = DCTan+opDotColor (BinOp UnboxScalar _ _)       = DCTan+opDotColor (BinOp PropReorder _ _)       = DCTan+opDotColor (BinOp DistLift _ _)          = DCTan+opDotColor (BinOp Align _ _)             = DCTan+opDotColor (TerOp Combine _ _ _)         = DCDodgerBlue+opDotColor (UnOp (Select _) _)           = DCLightSkyBlue+opDotColor (UnOp (Aggr _) _)             = DCCrimson+opDotColor (BinOp (AggrS _) _ _)         = DCCrimson+opDotColor (UnOp (WinFun _) _)           = DCTomato+opDotColor (UnOp (AggrNonEmpty _) _)     = DCCrimson+opDotColor (UnOp (AggrNonEmptyS _) _)    = DCCrimson+opDotColor (UnOp (GroupAggr (_, _)) _)   = DCTomato+opDotColor (UnOp (Project _) _)          = DCLightSkyBlue+opDotColor (UnOp Transpose _)            = DCHotPink+opDotColor (BinOp TransposeS _ _)        = DCHotPink+opDotColor (UnOp (ReshapeS _) _)         = DCHotPink+opDotColor (UnOp (Reshape _) _)          = DCHotPink+opDotColor _                             = DCGray++-- Dot colors+data DotColor = DCTomato+              | DCSalmon+              | DCGray+              | DimDCGray+              | DCGold+              | DCTan+              | DCRed+              | DCCrimson+              | DCGreen+              | DCSeaGreen+              | DCYelloGreen+              | DCSienna+              | DCBeige+              | DCDodgerBlue+              | DCLightSkyBlue+              | DCHotPink++renderColor :: DotColor -> Doc+renderColor DCTomato = text "tomato"+renderColor DCSalmon = text "salmon"+renderColor DCGray = text "gray"+renderColor DimDCGray = text "dimgray"+renderColor DCGold = text "gold"+renderColor DCTan = text "tan"+renderColor DCRed = text "red"+renderColor DCCrimson = text "crimson"+renderColor DCGreen = text "green"+renderColor DCSeaGreen = text "seagreen"+renderColor DCYelloGreen = text "yellowgreen"+renderColor DCSienna = text "sienna"+renderColor DCBeige = text "beige"+renderColor DCDodgerBlue = text "dodgerblue"+renderColor DCLightSkyBlue = text "lightskyblue"+renderColor DCHotPink      = text "hotpink"++escapeLabel :: String -> String+escapeLabel s = concatMap escapeChar s++escapeChar :: Char -> [Char]+escapeChar '\n' = ['\\', 'n']+escapeChar '\\' = ['\\', '\\']+escapeChar '\"' = ['\\', '"']+escapeChar c = [c]++-- Type of Dot style options+data DotStyle = Dashed++-- label of Dot nodes+type DotLabel = String++-- id of Dot nodes+type DotNodeID = Int++-- Type of Dot nodes+data DotNode = DotNode DotNodeID DotLabel DotColor (Maybe DotStyle)++-- Type of Dot edges+data DotEdge = DotEdge DotNodeID DotNodeID++-- Generate the preamble of a Dot file+preamble :: Doc+preamble = graphAttributes $$ nodeAttributes+    where nodeAttributes = text "node" <+> (brackets $ text "style=filled" <> comma <+> text "shape=box") <> semi+          graphAttributes = text "ordering=out;"++renderDotNode :: DotNode -> Doc+renderDotNode (DotNode n l c s) =+    int n+    <+> (brackets $ (((text "label=") <> (doubleQuotes $ text l))+                     <> comma+                     <+> (text "color=") <> (renderColor c)+                     <> styleDoc))+    <> semi+    where styleDoc =+              case s of+                  Just Dashed -> comma <+> text "style=dashed"+                  Nothing     -> empty++renderDotEdge :: DotEdge -> Doc+renderDotEdge (DotEdge u v) = int u <+> text "->" <+> int v <> semi++-- | Render a Dot document from the preamble, nodes and edges+renderDot :: [DotNode] -> [DotEdge] -> Doc+renderDot ns es = text "digraph" <> (braces $ preamble $$ nodeSection $$ edgeSection)+    where nodeSection = vcat $ map renderDotNode ns+          edgeSection = vcat $ map renderDotEdge es++-- | Create an abstract Dot node from an X100 operator description+constructDotNode :: [AlgNode] -> NodeMap [Tag] -> (AlgNode, VL) -> DotNode+constructDotNode rootNodes ts (n, op) =+    if elem n rootNodes then+        DotNode n l c (Just Dashed)+    else+        DotNode n l c Nothing+    where l = escapeLabel $ render $ opDotLabel ts n op+          c = opDotColor op++-- | Create an abstract Dot edge+constructDotEdge :: (AlgNode, AlgNode) -> DotEdge+constructDotEdge = uncurry DotEdge++-- | extract the operator descriptions and list of edges from a DAG+-- FIXME no apparent reason to use topological ordering here+extractGraphStructure :: Dag.AlgebraDag VL+                     -> ([(AlgNode, VL)], [(AlgNode, AlgNode)])+extractGraphStructure d = (operators, childs)+    where nodes = Dag.topsort d+          operators = zip nodes $ map (flip Dag.operator d) nodes+          childs = concat $ map (\(n, op) -> zip (repeat n) (Dag.opChildren op)) operators++-- | Render an VL plan into a dot file (GraphViz).+renderVLDot :: NodeMap [Tag] -> [AlgNode] -> NodeMap VL -> String+renderVLDot ts roots m = render $ renderDot dotNodes dotEdges+    where (opLabels, edges) = extractGraphStructure d+          d = Dag.mkDag m roots+          dotNodes = map (constructDotNode roots ts) opLabels+          dotEdges = map constructDotEdge edges
+ src/Database/DSH/VL/Render/JSON.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE TemplateHaskell #-}++module Database.DSH.VL.Render.JSON+    ( serializePlan+    , deserializePlan+    , planToFile+    , planFromFile+    ) where++import           Control.Monad+import qualified Data.IntMap                 as M++import           Data.Aeson                  (decode, encode)+import           Data.Aeson.TH+import qualified Data.ByteString.Lazy.Char8  as BL++import           Database.Algebra.Dag.Common++import           Database.DSH.VL.Lang++data Plan = Plan { tags :: [(AlgNode, [Tag])]+                 , roots :: [AlgNode]+                 , graph :: [(AlgNode, VL)]+                 }++$(deriveJSON defaultOptions ''Plan)++serializePlan :: (NodeMap [Tag], [AlgNode], NodeMap VL) -> BL.ByteString+serializePlan (ts, rs, g) = let tags' = M.toList ts+                                graph' = M.toList g+                             in encode $ Plan {tags = tags', roots = rs, graph = graph'}++deserializePlan :: BL.ByteString -> (NodeMap [Tag], [AlgNode], NodeMap VL)+deserializePlan s = let Just (Plan ts rs g) = decode s+                    in (M.fromList ts, rs, M.fromList g)++planToFile :: FilePath -> (NodeMap [Tag], [AlgNode], NodeMap VL) -> IO ()+planToFile f t = BL.writeFile f $ serializePlan t++planFromFile :: FilePath -> IO (NodeMap [Tag], [AlgNode], NodeMap VL)+planFromFile f = liftM deserializePlan $ BL.readFile f
+ src/Database/DSH/VL/Vector.hs view
@@ -0,0 +1,72 @@+{-# LANGUAGE TemplateHaskell      #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE FlexibleInstances    #-}++-- | This module defines the kinds of vectors that occur in VL+-- programs.+module Database.DSH.VL.Vector+    ( DBCol+    , DagVector+    , vectorNodes+    , updateVector+    , ADVec(..)+    , VLDVec(..)+    , NDVec+    , PVec(..)+    , RVec(..)+    ) where++import           Data.Aeson.TH++import           Database.Algebra.Dag.Common++import           Database.DSH.VL.Lang++-- | Common properties of data vectors+class DagVector v where+    -- | Return all graph nodes which represent the vector.+    vectorNodes :: v -> [AlgNode]++    -- | Replace a node in the vector+    updateVector :: AlgNode -> AlgNode -> v -> v++-- | Data vectors. A data vector references a result in an algebra DAG+-- and stores the number of payload columns that it has. 'ADVec'+-- abstracts over the type of references into the graph.+data ADVec r = ADVec r [DBCol]+    deriving (Show, Read)++-- | Data vectors that reference single nodes in an algebra graph+-- (used for table algebra and X100 with an n-ary storage model).+type NDVec = ADVec AlgNode++instance DagVector NDVec where+    vectorNodes (ADVec q _) = [q]++    updateVector n1 n2 (ADVec q cols) +        | q == n1   = ADVec n2 cols+        | otherwise = ADVec q cols++-- | A VL data vector references an operator in a VL DAG.+newtype VLDVec = VLDVec AlgNode+    deriving (Show, Read)++instance DagVector VLDVec where+    vectorNodes (VLDVec q) = [q]++    updateVector n1 n2 (VLDVec q) +        | q == n1   = VLDVec n2+        | otherwise = VLDVec q+++-- | Propagation vectors. A @PVec@ simply references a node in an+-- algebra Dag.+data PVec = PVec AlgNode++-- | Rename vectors. A @RVec@ simply references a node in an algebra+-- Dag.+data RVec = RVec AlgNode++$(deriveJSON defaultOptions ''ADVec)+$(deriveJSON defaultOptions ''PVec)+$(deriveJSON defaultOptions ''RVec)
+ src/Database/DSH/VL/VectorAlgebra.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE MultiParamTypeClasses #-}++module Database.DSH.VL.VectorAlgebra where++import qualified Data.List.NonEmpty              as N+import           Database.DSH.Common.Lang+import           Database.DSH.VL.Vector+import           Database.DSH.VL.Lang+import           Database.Algebra.Dag.Build++class VectorAlgebra v a where+    -- | A vector with one segment+    singletonDescr :: Build a v++    -- | A vector representing a literal list.+    vecLit :: [ScalarType] -> [[VLVal]] -> Build a v++    -- | A reference to a database-resident table.+    vecTableRef :: String -> [VLColumn] -> TableHints -> Build a v++    -- | Perform duplicate elimination per segment.+    vecUniqueS :: v -> Build a v++    -- | /Materialize/ vector positions. The operator adds an item+    -- column that contains the dense positions of the vector's+    -- elements.+    vecNumber :: v -> Build a v++    -- | /Materialize/ vector positions per segment. The operator adds+    -- an item column that contains the dense positions of the+    -- vector's elements in each segment.+    vecNumberS :: v -> Build a v++    descToRename :: v -> Build a RVec++    -- | From a vector with only one segment, create a segmented+    -- version in which every value in the original segment inhabits+    -- its own segment.+    vecSegment :: v -> Build a v++    vecUnsegment :: v -> Build a v++    vecAggr :: AggrFun -> v -> Build a v+    vecAggrS :: AggrFun -> v -> v -> Build a v+    vecAggrNonEmpty :: N.NonEmpty AggrFun -> v -> Build a v+    vecAggrNonEmptyS :: N.NonEmpty AggrFun -> v -> Build a v++    vecWinFun :: WinFun -> FrameSpec -> v -> Build a v++    -- | SelectPos filters a vector positionally as specified by the+    -- comparison operator and the position value from the right+    -- input. Next to the filtered value vector it produces two rename+    -- vectors:+    --+    -- * Mapping old to new positions (for re-aligning inner vectors)+    -- * Mapping old positions to segment descriptors (for unboxing one+    -- inner segment)+    -- FIXME should be restricted to RelOp!+    vecSelectPos :: v -> ScalarBinOp -> v -> Build a (v, RVec, RVec)++    -- | Filter a vector positionally /by segment/. The right input+    -- vector provides a position offset /for each segment/. The+    -- operator produces the same triple of vectors as its non-segmented+    -- variant.+    vecSelectPosS :: v -> ScalarBinOp -> v -> Build a (v, RVec, RVec)++    -- | Filter a vector positionally on a /constant/ position.+    vecSelectPos1 :: v -> ScalarBinOp -> Int -> Build a (v, RVec, RVec)++    -- | Filter a vector positionally based on a /constant+    -- position/. The operator filters by segment, but the constant+    -- position argument is the same for all segments.+    vecSelectPos1S :: v -> ScalarBinOp -> Int -> Build a (v, RVec, RVec)++    -- | Reverse a vector.+    vecReverse :: v -> Build a (v, PVec)++    -- | Reverse each segment of a vector individually.+    vecReverseS :: v -> Build a (v, PVec)++    -- | Filter a vector by applying a scalar boolean predicate.+    vecSelect:: Expr -> v -> Build a (v, RVec)++    -- | Segmented sorting of a vector. +    vecSortS :: [Expr] -> v -> Build a (v, PVec)++    vecGroupS :: [Expr] -> v -> Build a (v, v, PVec)++    -- | The VL aggregation operator groups the input vector by the+    -- given columns and then performs the list of aggregations+    -- described by the second argument. The result is a flat vector,+    -- since all groups are reduced via aggregation. The operator+    -- operates segmented, i.e. always groups by descr first. This+    -- operator must be used with care: It does not determine the+    -- complete set of descr value to check for empty inner lists.+    -- The output payload columns are the grouping columns followed by+    -- the aggregation results.+    vecGroupAggr :: [Expr] -> N.NonEmpty AggrFun -> v -> Build a v+++    -- | Construct a new vector as the result of a list of scalar+    -- expressions per result column.+    vecProject :: [Expr] -> v -> Build a v++    -- FIXME is distprim really necessary? could maybe be replaced by distdesc+    vecDistDesc :: v -> v -> Build a (v, PVec)+    vecDistLift :: v -> v -> Build a (v, PVec)++    -- | propRename uses a propagation vector to rename a vector (no+    -- filtering or reordering).+    vecPropRename :: RVec -> v -> Build a v++    -- | propFilter uses a propagation vector to rename and filter a+    -- vector (no reordering).+    vecPropFilter :: RVec -> v -> Build a (v, RVec)++    -- | propReorder uses a propagation vector to rename, filter and+    -- reorder a vector.+    vecPropReorder :: PVec -> v -> Build a (v, PVec)++    -- | Specialized unbox operator that merges DescrToRename+    -- and PropRename. It takes an inner and outer vector, and+    -- pulls the segment that is referenced by the outer vector+    -- into the outer segment. Notice that there must be+    -- /exactly one/ segment referenced by the outer+    -- vector. Inner segments that are not referenced are+    -- silently discarded.+    --+    -- Output: @(DVec r, RVec)@+    vecUnboxNested :: RVec -> v -> Build a (v, RVec)++    vecUnboxScalar :: v -> v -> Build a v++    vecAppend :: v -> v -> Build a (v, RVec, RVec)+    vecAppendS :: v -> v -> Build a (v, RVec, RVec)++    -- | Align two vectors positionally. However, in contrast to+    -- 'vecZip', these are not arbitrary vectors, but vectors which+    -- are guaranteed to have the same length because they are+    -- operands to lifted operators.+    vecAlign :: v -> v -> Build a v++    -- | Positionally align two vectors. Basically: @zip xs ys@+    vecZip :: v -> v -> Build a v++    -- | Positionally align two vectors per segment: @map zip xss+    -- yss@.+    vecZipS :: v -> v -> Build a (v, RVec, RVec)++    vecCartProduct :: v -> v -> Build a (v, PVec, PVec)+    vecCartProductS :: v -> v -> Build a (v, PVec, PVec)+    vecNestProduct :: v -> v -> Build a (v, PVec, PVec)+    -- FIXME inner result vector contains the outer values. Produce a+    -- propagation vector to align the layout.+    vecNestProductS :: v -> v -> Build a (v, PVec)++    vecThetaJoin :: JoinPredicate Expr -> v -> v -> Build a (v, PVec, PVec)+    vecNestJoin :: JoinPredicate Expr -> v -> v -> Build a (v, PVec, PVec)+    vecThetaJoinS :: JoinPredicate Expr -> v -> v -> Build a (v, PVec, PVec)+    vecNestJoinS :: JoinPredicate Expr -> v -> v -> Build a (v, PVec)++    vecSemiJoin :: JoinPredicate Expr -> v -> v -> Build a (v, RVec)+    vecSemiJoinS :: JoinPredicate Expr -> v -> v -> Build a (v, RVec)++    vecAntiJoin :: JoinPredicate Expr -> v -> v -> Build a (v, RVec)+    vecAntiJoinS :: JoinPredicate Expr -> v -> v -> Build a (v, RVec)++    vecCombine :: v -> v -> v -> Build a (v, RVec, RVec)++    -- | Experimental: @reshape m@ partitions a vector of length @n*m@+    -- into @n@ vectors of length @m@.+    --+    -- reshapeS can be computed only on the inner vector. As its+    -- result is one list nesting level deeper, it computes the new+    -- innermost vector from the old inner vector and then derives+    -- from that a 'middle' descriptor vector which represents lists+    -- at nesting depth 1.+    vecReshape :: Integer -> v -> Build a (v, v)++    -- | Experimental: segmented version of reshape.+    vecReshapeS :: Integer -> v -> Build a (v, v)++    -- | Experimental: Matrix transposition+    vecTranspose :: v -> Build a (v, v)++    -- | Experimental: Segmented matrix transposition+    vecTransposeS :: v -> v -> Build a (v, v)+
+ src/Database/DSH/VL/VectorAlgebra/TA.hs view
@@ -0,0 +1,908 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE ParallelListComp      #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeSynonymInstances  #-}+{-# LANGUAGE MultiParamTypeClasses #-}++-- | Implementation of vector primitives in terms of table algebra+-- operators.+module Database.DSH.VL.VectorAlgebra.TA () where++import           Control.Applicative              hiding (Const)+import qualified Data.List.NonEmpty               as N+import           GHC.Exts++import           Database.Algebra.Dag.Build+import           Database.Algebra.Dag.Common+import           Database.Algebra.Table.Construct+import           Database.Algebra.Table.Lang++import qualified Database.DSH.Common.Lang         as L+import           Database.DSH.Impossible+import           Database.DSH.VL.Vector+import qualified Database.DSH.VL.Lang             as VL+import           Database.DSH.VL.VectorAlgebra+++--------------------------------------------------------------------------------+-- Some general helpers++-- | Results are stored in column:+pos, item', item, descr, descr', descr'', pos', pos'', pos''', posold, posnew, ordCol, resCol, absPos, descri, descro, posi, poso:: Attr+pos       = "pos"+item      = "item1"+item'     = "itemtmp"+descr     = "descr"+descr'    = "descr1"+descr''   = "descr2"+pos'      = "pos1"+pos''     = "pos2"+pos'''    = "pos3"+posold    = "posold"+posnew    = "posnew"+ordCol    = "ord"+resCol    = "res"+absPos    = "abspos"+descro    = "descro"+descri    = "descri"+poso      = "poso"+posi      = "posi"++itemi :: Int -> Attr+itemi i = "item" ++ show i++itemi' :: Int -> Attr+itemi' i = "itemtmp" ++ show i++algVal :: VL.VLVal -> AVal+algVal (VL.VLInt i) = int (fromIntegral i)+algVal (VL.VLBool t) = bool t+algVal VL.VLUnit = int (-1)+algVal (VL.VLString s) = string s+algVal (VL.VLDouble d) = double d++algTy :: VL.ScalarType -> ATy+algTy (VL.Int)    = intT+algTy (VL.Double) = doubleT+algTy (VL.Bool)   = boolT+algTy (VL.String) = stringT+algTy (VL.Unit)   = intT++cP :: Attr -> Proj+cP a = (a, ColE a)++eP :: Attr -> Expr -> Proj+eP = (,)++mP :: Attr -> Attr -> Proj+mP n o = (n, ColE o)++projAddCols :: [DBCol] -> [Proj] -> AlgNode -> Build TableAlgebra AlgNode+projAddCols cols projs q = proj ([cP descr, cP pos] ++ map (cP . itemi) cols ++ projs) q++itemProj :: [DBCol] -> [Proj] -> [Proj]+itemProj cols projs = projs ++ [ cP $ itemi i | i <- cols ]++binOp :: L.ScalarBinOp -> BinFun+binOp (L.SBNumOp L.Add)     = Plus+binOp (L.SBNumOp L.Sub)     = Minus+binOp (L.SBNumOp L.Div)     = Div+binOp (L.SBNumOp L.Mul)     = Times+binOp (L.SBNumOp L.Mod)     = Modulo+binOp (L.SBRelOp L.Eq)      = Eq+binOp (L.SBRelOp L.NEq)     = NEq+binOp (L.SBRelOp L.Gt)      = Gt+binOp (L.SBRelOp L.GtE)     = GtE+binOp (L.SBRelOp L.Lt)      = Lt+binOp (L.SBRelOp L.LtE)     = LtE+binOp (L.SBBoolOp L.Conj)   = And+binOp (L.SBBoolOp L.Disj)   = Or+binOp (L.SBStringOp L.Like) = Like++unOp :: L.ScalarUnOp -> UnFun+unOp (L.SUBoolOp L.Not)             = Not+unOp (L.SUCastOp (L.CastDouble))    = Cast doubleT+unOp (L.SUNumOp L.Sin)              = Sin+unOp (L.SUNumOp L.Cos)              = Cos+unOp (L.SUNumOp L.Tan)              = Tan+unOp (L.SUNumOp L.ASin)             = ASin+unOp (L.SUNumOp L.ACos)             = ACos+unOp (L.SUNumOp L.ATan)             = ATan+unOp (L.SUNumOp L.Sqrt)             = Sqrt+unOp (L.SUNumOp L.Exp)              = Exp+unOp (L.SUNumOp L.Log)              = Log+unOp (L.SUTextOp (L.SubString f t)) = SubString f t+unOp L.SUDateOp                     = $unimplemented++taExprOffset :: Int -> VL.Expr -> Expr+taExprOffset o (VL.BinApp op e1 e2) = BinAppE (binOp op) (taExprOffset o e1) (taExprOffset o e2)+taExprOffset o (VL.UnApp op e)      = UnAppE (unOp op) (taExprOffset o e)+taExprOffset o (VL.Column c)        = ColE $ itemi $ c + o+taExprOffset _ (VL.Constant v)      = ConstE $ algVal v+taExprOffset o (VL.If c t e)        = IfE (taExprOffset o c) (taExprOffset o t) (taExprOffset o e)++taExpr :: VL.Expr -> Expr+taExpr = taExprOffset 0++aggrFun :: VL.AggrFun -> AggrType+aggrFun (VL.AggrSum _ e) = Sum $ taExpr e+aggrFun (VL.AggrMin e)   = Min $ taExpr e+aggrFun (VL.AggrMax e)   = Max $ taExpr e+aggrFun (VL.AggrAvg e)   = Avg $ taExpr e+aggrFun (VL.AggrAll e)   = All $ taExpr e+aggrFun (VL.AggrAny e)   = Any $ taExpr e+aggrFun VL.AggrCount     = Count++-- Common building blocks++-- | For a segmented aggregate operator, apply the aggregate+-- function's default value for the empty segments. The first argument+-- specifies the outer descriptor vector, while the second argument+-- specifies the result vector of the aggregate.+segAggrDefault :: AlgNode -> AlgNode -> AVal -> Build TableAlgebra AlgNode+segAggrDefault qo qa dv =+    return qa+    `unionM`+    projM [cP descr, eP item (ConstE dv)]+        (differenceM+            (proj [mP descr pos] qo)+            (proj [cP descr] qa))++-- | If an aggregate's input is empty, add the aggregate functions+-- default value. The first argument 'q' is the original input vector,+-- whereas the second argument 'qa' is the aggregate's output.+aggrDefault :: AlgNode -> AlgNode -> AVal -> Build TableAlgebra AlgNode+aggrDefault q qa dv = do+    -- If the input is empty, produce a tuple with the default value.+    qd <- projM [eP descr (ConstE $ nat 2), eP pos (ConstE $ nat 1), eP item (ConstE dv)]+          $ (litTable (nat 1) descr ANat)+            `differenceM`+            (proj [cP descr] q)++    -- For an empty input, there will be two tuples in+    -- the union result: the aggregate output with NULL+    -- and the default value.+    qu <- qa `union` qd++    -- Perform an argmax on the descriptor to get either+    -- the sum output (for a non-empty input) or the+    -- default value (which has a higher descriptor).+    projM [eP descr (ConstE $ nat 1), cP pos, cP item]+       $ eqJoinM descr' descr+            (aggr [(Max $ ColE descr, descr')] [] qu)+            (return qu)+++-- | The default value for sums over empty lists for all possible+-- numeric input types.+sumDefault :: VL.ScalarType -> (ATy, AVal)+sumDefault VL.Int    = (AInt, int 0)+sumDefault VL.Double = (ADouble, double 0)+sumDefault _         = $impossible++doZip :: (AlgNode, [DBCol]) -> (AlgNode, [DBCol]) -> Build TableAlgebra (AlgNode, [DBCol])+doZip (q1, cols1) (q2, cols2) = do+  let offset = length cols1+  let cols' = cols1 ++ map (+offset) cols2+  r <- projM (cP descr : cP pos : map (cP . itemi) cols')+         $ eqJoinM pos pos'+           (return q1)+           (proj ((mP pos' pos):[ mP (itemi $ i + offset) (itemi i) | i <- cols2 ]) q2)+  return (r, cols')++joinPredicate :: Int -> L.JoinPredicate VL.Expr -> [(Expr, Expr, JoinRel)]+joinPredicate o (L.JoinPred conjs) = N.toList $ fmap joinConjunct conjs+  where+    joinConjunct :: L.JoinConjunct VL.Expr -> (Expr, Expr, JoinRel)+    joinConjunct (L.JoinConjunct e1 op e2) = (taExpr e1, taExprOffset o e2, joinOp op)++    joinOp :: L.BinRelOp -> JoinRel+    joinOp L.Eq  = EqJ+    joinOp L.Gt  = GtJ+    joinOp L.GtE = GeJ+    joinOp L.Lt  = LtJ+    joinOp L.LtE = LeJ+    joinOp L.NEq = NeJ++windowFunction :: VL.WinFun -> WinFun+windowFunction (VL.WinSum e)        = WinSum $ taExpr e+windowFunction (VL.WinMin e)        = WinMin $ taExpr e+windowFunction (VL.WinMax e)        = WinMax $ taExpr e+windowFunction (VL.WinAvg e)        = WinAvg $ taExpr e+windowFunction (VL.WinAll e)        = WinAll $ taExpr e+windowFunction (VL.WinAny e)        = WinAny $ taExpr e+windowFunction (VL.WinFirstValue e) = WinFirstValue $ taExpr e+windowFunction VL.WinCount          = WinCount++frameSpecification :: VL.FrameSpec -> FrameBounds+frameSpecification VL.FAllPreceding   = ClosedFrame FSUnboundPrec FECurrRow+frameSpecification (VL.FNPreceding n) = ClosedFrame (FSValPrec n) FECurrRow++-- The VectorAlgebra instance for TA algebra++instance VectorAlgebra NDVec TableAlgebra where+  vecAlign (ADVec q1 cols1) (ADVec q2 cols2) = do+    (r, cols') <- doZip (q1, cols1) (q2, cols2)+    return $ ADVec r cols'++  vecZip (ADVec q1 cols1) (ADVec q2 cols2) = do+    (r, cols') <- doZip (q1, cols1) (q2, cols2)+    return $ ADVec r cols'++  vecLit tys vs = do+    qr <- flip litTable' ((descr, natT):(pos, natT):[(itemi i, algTy t) | (i, t) <- zip [1..] tys])+                                 $ map (map algVal) vs+    return $ ADVec qr [1..length tys]++  vecPropRename (RVec q1) (ADVec q2 cols) = do+    q <- tagM "propRename"+         $ projM (itemProj cols [mP descr posnew, cP pos])+         $ eqJoin posold descr q1 q2+    return $ ADVec q cols++  vecPropFilter (RVec q1) (ADVec q2 cols) = do+    q <- rownumM pos' [posnew, pos] [] $ eqJoin posold descr q1 q2+    qr1 <- flip ADVec cols <$> proj (itemProj cols [mP descr posnew, mP pos pos']) q+    qr2 <- RVec <$> proj [mP posold pos, mP posnew pos'] q+    return $ (qr1, qr2)++  -- For TA algebra, the filter and reorder cases are the same, since+  -- numbering to generate positions is done with a rownum and involves sorting.+  vecPropReorder (PVec q1) e2 = do+    (p, (RVec r)) <- vecPropFilter (RVec q1) e2+    return (p, PVec r)++  vecUnboxNested (RVec qu) (ADVec qi cols) = do+    -- Perform a segment join between inner vector and outer unboxing+    -- rename vector. This implicitly discards any unreferenced+    -- segments in qi.+    q <- projM (itemProj cols [mP descr posnew, cP pos, mP posold pos'])+         $ rownumM pos [pos'] []+         $ eqJoinM posold descr'+             (return qu)+             (proj (itemProj cols [mP descr' descr, mP pos' pos]) qi)++    -- The unboxed vector containing one segment from the inner vector.+    qv <- proj (itemProj cols [cP descr, cP pos]) q+    -- A rename vector in case the inner vector has inner vectors as+    -- well.+    qr <- proj [mP posnew pos, cP posold] q++    return (ADVec qv cols, RVec qr)++  vecCombine (ADVec qb _) (ADVec q1 cols) (ADVec q2 _) = do+    d1 <- projM [cP pos', cP pos]+            $ rownumM pos' [pos] []+            $ select (ColE item) qb+    d2 <- projM [cP pos', cP pos]+          $ rownumM pos' [pos] []+          $ select (UnAppE Not (ColE item)) qb+    q <- eqJoinM pos' posold+            (return d1)+            (proj (itemProj cols [mP posold pos, cP descr]) q1)+         `unionM`+         eqJoinM pos' posold+            (return d2)+            (proj (itemProj cols [mP posold pos, cP descr]) q2)+    qr <- proj (itemProj cols [cP descr, cP pos]) q+    qp1 <- proj [mP posnew pos, mP posold pos'] d1+    qp2 <- proj [mP posnew pos, mP posold pos'] d2+    return $ (ADVec qr cols, RVec qp1, RVec qp2)++  vecSegment (ADVec q cols) = do+    flip ADVec cols <$> proj (itemProj cols [mP descr pos, cP pos]) q++  vecUnsegment (ADVec q cols) = do+    qr <- proj (itemProj cols [cP pos, eP descr (ConstE $ nat 1)]) q+    return $ ADVec qr cols++  vecDistDesc (ADVec q1 cols) (ADVec q2 _) = do+    q <- projM (itemProj cols [mP descr pos, mP pos pos'', cP posold])+           $ rownumM pos'' [pos, pos'] []+           $ crossM+               (proj [cP pos] q2)+               (proj (itemProj cols [mP pos' pos, mP posold pos]) q1)+    qr1 <- flip ADVec cols <$> proj (itemProj cols [cP descr, cP pos]) q+    qr2 <- PVec <$> proj [cP posold, mP posnew pos] q+    return $ (qr1, qr2)++  vecDistLift (ADVec q1 cols1) (ADVec q2 cols2) = do+    let cols2'    = [ i + length cols1 | i <- cols2 ]+        shiftProj = [ mP (itemi i') (itemi i) | i <- cols2 | i' <- cols2' ]+        resCols   = cols1 ++ cols2'+    q   <- eqJoinM pos' descr+             (proj (itemProj cols1 [mP pos' pos]) q1)+             (proj ([cP descr, cP pos] ++ shiftProj) q2)++    qr1 <- proj (itemProj resCols [cP descr, cP pos]) q+    qr2 <- proj [mP posold pos', mP posnew pos] q+    return (ADVec qr1 resCols, PVec qr2)++  vecWinFun a w (ADVec q cols1) = do+    let wfun      = windowFunction a+        frameSpec = frameSpecification w+        winCol    = itemi $ length cols1 + 1+    qw <- winFun (winCol, wfun) [] [(ColE pos, Asc)] (Just frameSpec) q+    return $ ADVec qw (cols1 ++ [length cols1 + 1])++  vecAggr a (ADVec q _) = do+    -- The aggr operator itself+    qa <- projM [eP descr (ConstE $ nat 1), eP pos (ConstE $ nat 1), cP item]+          $ aggr [(aggrFun a, item)] [] q+    -- For sum, add the default value for empty inputs+    qd <- case a of+              VL.AggrSum t _ -> aggrDefault q qa (snd $ sumDefault t)+              VL.AggrAll _   -> aggrDefault q qa (bool True)+              VL.AggrAny _   -> aggrDefault q qa (bool False)+              _              -> return qa++    return $ ADVec qd [1]++  vecAggrNonEmpty as (ADVec q _) = do+    let resCols = [1 .. N.length as]++    let aggrFuns = [ (aggrFun a, itemi i)+                   | a <- N.toList as+                   | i <- resCols+                   ]++    qa <- projM (itemProj resCols [eP descr (ConstE $ nat 1), eP pos (ConstE $ nat 1)])+          $ aggr aggrFuns [] q++    return $ ADVec qa resCols+++  vecAggrS a (ADVec qo _) (ADVec qi _) = do+    qa <- aggr [(aggrFun a, item)] [(descr, ColE descr)] qi+    qd <- case a of+              VL.AggrSum t _ -> segAggrDefault qo qa (snd $ sumDefault t)+              VL.AggrAny _   -> segAggrDefault qo qa (bool False)+              VL.AggrAll _   -> segAggrDefault qo qa (bool True)++              VL.AggrCount   -> segAggrDefault qo qa (int 0)+              _              -> return qa++    qr <- rownum' pos [(ColE descr, Asc)] [] qd++    return $ ADVec qr [1]++  vecAggrNonEmptyS as (ADVec q _) = do+    let resCols = [1 .. N.length as]++    let aggrFuns = [ (aggrFun a, itemi i)+                   | a <- N.toList as+                   | i <- resCols+                   ]++    -- Compute aggregate output per segment and new positions+    qa <- projM (itemProj resCols [cP descr, cP pos])+          $ rownumM pos [descr] []+          $ aggr aggrFuns [(descr, ColE descr)] q++    return $ ADVec qa resCols++  vecReverse (ADVec q cols) = do+    q' <- rownum' pos' [(ColE pos, Desc)] [] q+    r <- proj (itemProj cols [cP descr, mP pos pos']) q'+    p <- proj [mP posold pos, mP posnew pos'] q'+    return (ADVec r cols, PVec p)++  vecReverseS (ADVec q cols) = do+    q' <- rownum' pos' [(ColE descr, Asc), (ColE pos, Desc)] [] q+    r <- proj (itemProj cols [cP descr, mP pos pos']) q'+    p <- proj [mP posold pos, mP posnew pos'] q'+    return (ADVec r cols, PVec p)++  vecUniqueS (ADVec q cols) = do+    let groupCols = map (\c -> (c, ColE c)) (descr : map itemi cols)+    qr <- rownumM pos [pos] []+          $ aggr [(Min (ColE pos), pos)] groupCols q+    return $ ADVec qr cols++  descToRename (ADVec q1 _) = RVec <$> proj [mP posnew descr, mP posold pos] q1++  singletonDescr = do+    q <- litTable' [[nat 1, nat 1]] [(descr, natT), (pos, natT)]+    return $ ADVec q []++  vecAppend (ADVec q1 cols) (ADVec q2 _) = do+    q <- rownumM posnew [ordCol, pos] []+           $ projAddCols cols [eP ordCol (ConstE (nat 1))] q1+             `unionM`+             projAddCols cols [eP ordCol (ConstE (nat 2))] q2+    qv <- tagM "append r" (proj (itemProj cols [mP pos posnew, cP descr]) q)+    qp1 <- tagM "append r1"+           $ projM [mP posold pos, cP posnew]+           $ select (BinAppE Eq (ColE ordCol) (ConstE $ nat 1)) q+    qp2 <- tagM "append r2"+           $ projM [mP posold pos, cP posnew]+           $ select (BinAppE Eq (ColE ordCol) (ConstE $ nat 2)) q+    return $ (ADVec qv cols, RVec qp1, RVec qp2)++  vecAppendS (ADVec q1 cols) (ADVec q2 _) = do+    q <- rownumM posnew [descr, ordCol, pos] []+           $ projAddCols cols [eP ordCol (ConstE (nat 1))] q1+             `unionM`+             projAddCols cols [eP ordCol (ConstE (nat 2))] q2+    qv <- tagM "append r" (proj (itemProj cols [mP pos posnew, cP descr]) q)+    qp1 <- tagM "append r1"+           $ projM [mP posold pos, cP posnew]+           $ select (BinAppE Eq (ColE ordCol) (ConstE $ nat 1)) q+    qp2 <- tagM "append r2"+           $ projM [mP posold pos, cP posnew]+           $ select (BinAppE Eq (ColE ordCol) (ConstE $ nat 2)) q+    return $ (ADVec qv cols, RVec qp1, RVec qp2)++  vecSelect expr (ADVec q cols) = do+    qs <- rownumM posnew [pos] []+          $ select (taExpr expr) q+    qv <- proj (itemProj cols [cP descr, mP pos posnew]) qs+    qr <- proj [mP posold pos, cP posnew] qs+    return (ADVec qv cols, RVec qr)++  vecTableRef tableName columns hints = do+    q <- -- generate the pos column+         rownumM pos orderCols []+         -- map table columns to item columns, add constant descriptor+         $ projM (eP descr (ConstE (nat 1)) : [ mP (itemi i) c | (c, i) <- numberedColNames ])+         $ dbTable tableName taColumns (map Key taKeys)+    return $ ADVec q (map snd numberedColNames)++    where+      numberedColNames = zipWith (\((L.ColName c), _) i -> (c, i)) columns [1..]++      taColumns = [ (c, algTy t) | (L.ColName c, t) <- columns ]++      taKeys =    [ [ itemi $ colIndex c | L.ColName c <- k ] | L.Key k <- L.keysHint hints ]++      colIndex :: Attr -> Int+      colIndex n =+          case lookup n numberedColNames of+              Just i  -> i+              Nothing -> $impossible++      -- the initial table order is generated as follows:+      -- * if there are known keys for the table, we take the shortest one, in the hope+      --   that it will be the primary key. A sorting operation then might be able to+      --   use a primary key index.+      -- * without a key, we just take an arbitrary column (here, the first).+      orderCols = case sortWith length taKeys of+                      k : _ -> k+                      []    -> [itemi 1]++  vecGroupS groupExprs (ADVec q1 cols1) = do+      -- apply the grouping expressions and compute surrogate values+      -- from the grouping values+      let groupProjs = [ eP (itemi' i) (taExpr e) | e <- groupExprs | i <- [1..] ]+          groupCols = map fst groupProjs+      qg <- rowrankM resCol [ (ColE c, Asc) | c <- (descr : groupCols) ]+            $ proj (itemProj cols1 ([cP descr, cP pos] ++ groupProjs)) q1++      -- Create the outer vector, containing surrogate values and the+      -- grouping values+      qo <- distinctM+            $ proj ([cP descr, mP pos resCol]+                    ++ [ mP (itemi i) c | c <- groupCols | i <- [1..] ]) qg++      -- Create new positions for the inner vector+      qp <- rownum posnew [resCol, pos] [] qg++      -- Create the inner vector, containing the actual groups+      qi <- proj (itemProj cols1 [mP descr resCol, mP pos posnew]) qp++      qprop <- proj [mP posold pos, cP posnew] qp++      return (ADVec qo [1 .. length groupExprs], ADVec qi cols1, PVec qprop)++  vecCartProduct (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([cP descr, cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [pos', pos''] []+           $ crossM+             (proj ([cP descr, mP pos' pos] ++ itemProj1) q1)+             (proj ((mP pos'' pos) : shiftProj2) q2)++    qv <- proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  vecCartProductS (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'+    q <- projM ([cP descr, cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [descr, descr', pos', pos''] []+           $ eqJoinM descr descr'+             (proj ([cP descr, mP pos' pos] ++ itemProj1) q1)+             (proj ([mP descr' descr, mP pos'' pos] ++ shiftProj2) q2)+    qv <- proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  vecNestProduct (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([mP descr pos', cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [pos', pos''] []+           $ crossM+             (proj ([cP descr, mP pos' pos] ++ itemProj1) q1)+             (proj ((mP pos'' pos) : shiftProj2) q2)++    qv <- proj ([cP descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  -- FIXME merge common parts of vecCartProductS and vecNestProductS+  vecNestProductS (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([mP descr pos', cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [descr, pos', pos''] []+           $ eqJoinM descr descr'+             (proj ([cP descr, mP pos' pos] ++ itemProj1) q1)+             (proj ([mP descr' descr, mP pos'' pos] ++ shiftProj2) q2)+    qv <- proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp2)++  vecThetaJoin joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([cP descr, cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [pos', pos''] []+           $ thetaJoinM (joinPredicate (length cols1) joinPred)+             (proj ([ cP descr+                    , mP pos' pos+                    ] ++ itemProj1) q1)+             (proj ([ mP pos'' pos+                    ] ++ shiftProj2) q2)++    qv <- tagM "eqjoin/1" $ proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  vecNestJoin joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [pos', pos''] []+           $ thetaJoinM (joinPredicate (length cols1) joinPred)+             (proj ([ mP pos' pos+                    ] ++ itemProj1) q1)+             (proj ([ mP pos'' pos+                    ] ++ shiftProj2) q2)++    qv <- tagM "eqjoin/1" $ proj ([mP descr pos', cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  vecThetaJoinS joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([cP descr, cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [pos', pos''] []+           $ thetaJoinM ((ColE descr, ColE descr', EqJ) : joinPredicate (length cols1) joinPred)+             (proj ([ cP descr+                    , mP pos' pos+                    ] ++ itemProj1) q1)+             (proj ([ mP descr' descr+                    , mP pos'' pos+                    ] ++ shiftProj2) q2)++    qv <- proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp1 <- proj [mP posold pos', mP posnew pos] q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp1, PVec qp2)++  -- There is only one difference between EquiJoinS and NestJoinS. For+  -- NestJoinS, we 'segment' after the join, i.e. use the left input+  -- positions as the result descriptor.+  -- FIXME merge the common parts.+  vecNestJoinS joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let itemProj1  = map (cP . itemi) cols1+        cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)+        itemProj2  = map (cP . itemi) cols2'++    q <- projM ([mP descr pos', cP pos, cP pos', cP pos''] ++ itemProj1 ++ itemProj2)+           $ rownumM pos [descr, pos', pos''] []+           $ thetaJoinM ((ColE descr, ColE descr', EqJ) : joinPredicate (length cols1) joinPred)+             (proj ([ cP descr+                    , mP pos' pos+                    ] ++ itemProj1) q1)+             (proj ([ mP descr' descr+                    , mP pos'' pos+                    ] ++ shiftProj2) q2)++    qv <- proj ([cP  descr, cP pos] ++ itemProj1 ++ itemProj2) q+    qp2 <- proj [mP posold pos'', mP posnew pos] q+    return (ADVec qv (cols1 ++ cols2'), PVec qp2)++  vecUnboxScalar (ADVec qo colso) (ADVec qi colsi) = do+    let colsi'     = [((length colso) + 1) .. ((length colso) + (length colsi))]+        shiftProji = zipWith mP (map itemi colsi') (map itemi colsi)+        itemProji  = map (cP . itemi) colsi'++    qu <- projM ([cP descr, cP pos] ++ (map (cP . itemi) colso) ++ itemProji)+              $ eqJoinM pos descr'+                  (return qo)+                  (proj ([mP descr' descr] ++ shiftProji) qi)+    return $ ADVec qu (colso ++ colsi')++  vecSelectPos (ADVec qe cols) op (ADVec qi _) = do+    qs <- selectM (BinAppE (binOp op) (ColE pos) (UnAppE (Cast natT) (ColE item')))+          $ crossM+              (return qe)+              (proj [mP item' item] qi)++    q' <- case op of+            -- If we select positions from the beginning, we can re-use the old+            -- positions+            (L.SBRelOp L.Lt)  -> projAddCols cols [mP posnew pos] qs+            (L.SBRelOp L.LtE) -> projAddCols cols [mP posnew pos] qs+            -- Only if selected positions don't start at the beginning (i.e. 1)+            -- do we have to recompute them.+            _      -> rownum posnew [pos] [] qs++    qr <- proj (itemProj cols [cP descr, mP pos posnew]) q'+    -- A regular rename vector for re-aligning inner vectors+    qp <- proj [ mP posold pos, cP posnew ] q'+    -- An unboxing rename vector+    qu <- proj [ mP posold pos, mP posnew descr ] q'+    return $ (ADVec qr cols, RVec qp, RVec qu)++  vecSelectPosS (ADVec qe cols) op (ADVec qi _) = do+    qs <- rownumM posnew [pos] []+          $ selectM (BinAppE (binOp op) (ColE absPos) (UnAppE (Cast natT) (ColE item')))+          $ eqJoinM descr pos'+              (rownum absPos [pos] [ColE descr] qe)+              (proj [mP pos' pos, mP item' item] qi)++    qr <- proj (itemProj cols [cP descr, mP pos posnew]) qs+    qp <- proj [ mP posold pos, cP posnew ] qs+    qu <- proj [ mP posnew descr, mP posold pos] qs+    return $ (ADVec qr cols, RVec qp, RVec qu)++  vecSelectPos1 (ADVec qe cols) op posConst = do+    let posConst' = VNat $ fromIntegral posConst+    qs <- select (BinAppE (binOp op) (ColE pos) (ConstE posConst')) qe++    q' <- case op of+            -- If we select positions from the beginning, we can re-use the old+            -- positions+            (L.SBRelOp L.Lt)  -> projAddCols cols [mP posnew pos] qs+            (L.SBRelOp L.LtE) -> projAddCols cols [mP posnew pos] qs+            -- Only if selected positions don't start at the beginning (i.e. 1)+            -- do we have to recompute them.+            _      -> rownum posnew [pos] [] qs++    qr <- proj (itemProj cols [cP descr, mP pos posnew]) q'+    qp <- proj [ mP posold pos, cP posnew ] q'+    qu <- proj [ mP posold pos, mP posnew descr ] q'+    return $ (ADVec qr cols, RVec qp, RVec qu)++  -- If we select positions in a lifted way, we need to recompute+  -- positions in any case.+  vecSelectPos1S (ADVec qe cols) op posConst = do+    let posConst' = VNat $ fromIntegral posConst+    qs <- rownumM posnew [pos] []+          $ selectM (BinAppE (binOp op) (ColE absPos) (ConstE posConst'))+          $ rownum absPos [pos] [ColE descr] qe++    qr <- proj (itemProj cols [cP descr, mP pos posnew]) qs+    qp <- proj [ mP posold pos, cP posnew ] qs+    qu <- proj [ mP posold pos, mP posnew descr ] qs+    return $ (ADVec qr cols, RVec qp, RVec qu)++  vecProject projs (ADVec q _) = do+    let projs' = zipWith (\i e -> (itemi i, taExpr e)) [1 .. length projs] projs+    qr <- proj ([cP descr, cP pos] ++ projs') q+    return $ ADVec qr [1 .. (length projs)]++  vecZipS (ADVec q1 cols1) (ADVec q2 cols2) = do+    q1' <- rownum pos'' [pos] [ColE descr] q1+    q2' <- rownum pos''' [pos] [ColE descr] q2+    let offset      = length cols1+        cols2'      = map (+ offset) cols2+        allCols     = cols1 ++ cols2'+        allColsProj = map (cP . itemi) allCols+        shiftProj   = zipWith mP (map itemi cols2') (map itemi cols2)+    qz <- rownumM posnew [descr, pos''] []+          $ projM ([cP pos', cP pos, cP descr] ++ allColsProj)+          $ thetaJoinM [(ColE descr, ColE descr', EqJ), (ColE pos'', ColE pos''', EqJ)]+              (return q1')+              (proj ([mP descr' descr, mP pos' pos, cP pos'''] ++ shiftProj) q2')++    r1 <- proj [mP posold pos'', cP posnew] qz+    r2 <- proj [mP posold pos''', cP posnew] qz+    qr <- proj ([cP descr, mP pos posnew] ++ allColsProj) qz+    return (ADVec qr allCols, RVec r1, RVec r2)++  vecGroupAggr groupExprs aggrFuns (ADVec q _) = do+    let partAttrs = (descr, cP descr)+                    :+                    [ (itemi i, eP (itemi i) (taExpr e)) | e <- groupExprs | i <- [1..] ]++        pw = length groupExprs++        pfAggrFuns = [ (aggrFun a, itemi $ pw + i) | a <- N.toList aggrFuns | i <- [1..] ]++    -- GroupAggr(e, f) has to mimic the behaviour of GroupS(e) ++    -- AggrS(f) exactly. GroupScalarS determines the order of the+    -- groups by the sort order of the grouping keys (implicitly via+    -- RowRank). GroupAggr has to provide the aggregated groups in the+    -- same order to be aligned. Therefore, we sort by /all/ grouping+    -- attributes.+    qa <- rownumM pos (map fst partAttrs) []+          $ aggr pfAggrFuns (map snd partAttrs) q++    return $ ADVec qa [1 .. length groupExprs + N.length aggrFuns]++  vecNumber (ADVec q cols) = do+    let nrIndex = length cols + 1+        nrItem = itemi nrIndex+    qr <- projAddCols cols [eP nrItem (UnAppE (Cast natT) (ColE pos))] q+    return $ ADVec qr (cols ++ [nrIndex])++  -- The TA implementation of lifted number does not come for+  -- free: To generate the absolute numbers for every sublist+  -- (i.e. descriptor partition), we have to use a partitioned+  -- rownumber.+  vecNumberS (ADVec q cols) = do+    let nrIndex = length cols + 1+        nrItem = itemi nrIndex+    qr <- rownum nrItem [pos] [ColE descr] q+    return $ ADVec qr (cols ++ [nrIndex])++  vecSemiJoin joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)++    q <- rownumM pos [posold] []+         $ projM (itemProj cols1 [cP descr, mP posold pos])+         $ semiJoinM (joinPredicate (length cols1) joinPred)+             (proj (itemProj cols1 [cP descr, cP pos]) q1)+             (proj shiftProj2 q2)+    qj <- tagM "semijoin/1" $ proj (itemProj cols1 [cP descr, cP pos]) q+    r  <- proj [cP posold, mP posold posnew] q+    return $ (ADVec qj cols1, RVec r)++  vecSemiJoinS joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)++    q <- rownumM pos [descr, posold] []+         $ projM (itemProj cols1 [cP descr, mP posold pos])+         $ semiJoinM ((ColE descr, ColE descr', EqJ) : joinPredicate (length cols1) joinPred)+             (proj (itemProj cols1 [cP descr, cP pos]) q1)+             (proj ([mP descr' descr] ++ shiftProj2) q2)+    qj <- tagM "semijoinLift/1" $ proj (itemProj cols1 [cP descr, cP pos]) q+    r  <- proj [cP posold, mP posold posnew] q+    return $ (ADVec qj cols1, RVec r)++  vecAntiJoin joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)++    q <- rownumM pos [posold] []+         $ projM (itemProj cols1 [cP descr, mP posold pos])+         $ antiJoinM (joinPredicate (length cols1) joinPred)+             (proj (itemProj cols1 [cP descr, cP pos]) q1)+             (proj shiftProj2 q2)+    qj <- tagM "antijoin/1" $ proj (itemProj cols1 [cP descr, cP pos]) q+    r  <- proj [cP posold, mP posold posnew] q+    return $ (ADVec qj cols1, RVec r)++  vecAntiJoinS joinPred (ADVec q1 cols1) (ADVec q2 cols2) = do+    let cols2'     = [((length cols1) + 1) .. ((length cols1) + (length cols2))]+        shiftProj2 = zipWith mP (map itemi cols2') (map itemi cols2)++    q <- rownumM pos [descr, posold] []+         $ projM (itemProj cols1 [cP descr, mP posold pos])+         $ antiJoinM ((ColE descr, ColE descr', EqJ) : joinPredicate (length cols1) joinPred)+             (proj (itemProj cols1 [cP descr, cP pos]) q1)+             (proj ([mP descr' descr] ++ shiftProj2) q2)+    qj <- tagM "antijoinLift/1" $ proj (itemProj cols1 [cP descr, cP pos]) q+    r  <- proj [cP posold, mP posold posnew] q+    return $ (ADVec qj cols1, RVec r)++  vecSortS sortExprs (ADVec q1 cols1) = do+    let sortProjs = zipWith (\i e -> (itemi' i, taExpr e)) [1..] sortExprs+    -- Including positions de facto implements stable sorting+    qs <- rownumM pos' ([descr] ++ map fst sortProjs ++ [pos]) []+          $ projAddCols cols1 sortProjs q1++    qr1 <- proj (itemProj cols1 [cP descr, mP pos pos']) qs+    qr2 <- proj [mP posold pos, mP posnew pos'] qs++    return (ADVec qr1 cols1, PVec qr2)++  -- FIXME none of vecReshape, vecReshapeS, vecTranspose and+  -- vecTransposeS deal with empty inner inputs correctly!+  vecReshape n (ADVec q cols) = do+    let dExpr = BinAppE Div (BinAppE Minus (ColE pos) (ConstE $ int 1)) (ConstE $ int $ n + 1)+    qi <- proj (itemProj cols [cP pos, eP descr dExpr]) q+    qo <- projM [eP descr (ConstE $ nat 1), cP pos]+          $ distinctM+          $ proj [mP pos descr] qi+    return (ADVec qo [], ADVec qi cols)++  vecReshapeS n (ADVec q cols) = do+    let dExpr = BinAppE Div (BinAppE Minus (ColE absPos) (ConstE $ int 1)) (ConstE $ int $ n + 1)+    qr <- -- Make the new descriptors valid globally+          -- FIXME need a rowrank instead!+          rownumM descr'' [descr, descr'] []+          -- Assign the inner list elements to sublists. Generated+          -- descriptors are _per_ inner list!+          $ projM (itemProj cols [cP descr, cP pos, eP descr' dExpr])+          -- Generate absolute positions for the inner lists+          $ rownum absPos [pos] [ColE descr] q++    -- We can compute the 'middle' descriptor vector from the original+    -- inner vector.+    qm <- distinctM $ proj [cP descr, mP pos descr''] qr++    qi <- proj (itemProj cols [mP descr descr'', cP pos]) qr++    return (ADVec qm [], ADVec qi cols)++  vecTranspose (ADVec q cols) = do+    qi <- projM (itemProj cols [mP descr descr', mP pos pos'])+          -- Generate new positions. We use absolute positions as the+          -- new descriptor here. This implements the swapping of row+          -- and column ids (here: descr and pos) that is the core of+          -- transposition.+          $ rownumM pos' [descr', pos] []+          -- Generate absolute positions for the inner lists+          $ rownum descr' [pos] [ColE descr] q++    qo <- projM [eP descr (ConstE $ nat 1), cP pos]+          $ distinctM+          $ proj [mP pos descr] qi++    return (ADVec qo [], ADVec qi cols)++  vecTransposeS (ADVec qo _) (ADVec qi cols) = do+    qr  <- -- Generate new globally valid positions for the inner vector+           rownumM pos' [descr', absPos] []+           -- Absolute positions form the new inner descriptor. However, so+           -- far they are relative to the outer descriptor. Here, make them+           -- "globally" valid.+           $ rowrankM descr' [(ColE descro, Asc), (ColE absPos, Asc)]+           -- As usual, generate absolute positions+           $ rownumM absPos [posi] [ColE descri]+           -- Join middle and inner vector because we need to know to which+           -- outer list each leaf element belongs+           $ eqJoinM poso descri+               (proj [mP descro descr, mP poso pos] qo)+               (proj (itemProj cols [mP descri descr, mP posi pos]) qi)++    qi' <- proj (itemProj cols [mP descr descr', mP pos pos']) qr+    qm  <- distinctM $ proj [mP descr descro, mP pos descr'] qr++    return (ADVec qm [], ADVec qi' cols)
+ src/Database/DSH/VL/Vectorize.hs view
@@ -0,0 +1,861 @@+{-# LANGUAGE TemplateHaskell  #-}+{-# LANGUAGE ParallelListComp #-}++-- | Vectorising constructor functions that implement FKL primitives+-- using VL operators.+module Database.DSH.VL.Vectorize where++import           Debug.Trace++import           Control.Applicative+import qualified Data.List                     as List+import           Prelude                       hiding (reverse, zip)+import qualified Prelude                       as P++import           Database.Algebra.Dag.Build++import qualified Database.DSH.Common.Lang      as L+import           Database.DSH.Common.Nat+import           Database.DSH.Common.QueryPlan+import           Database.DSH.Common.Type+import           Database.DSH.Impossible+import           Database.DSH.VL.Lang          (AggrFun (..), Expr (..), VL (),+                                                VLVal (..))+import           Database.DSH.VL.Primitives+import           Database.DSH.VL.Vector++--------------------------------------------------------------------------------+-- Construction of not-lifted primitives++zip ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+zip (VShape q1 lyt1) (VShape q2 lyt2) = do+    q' <- vlZip q1 q2+    return $ VShape q' $ zipLayout lyt1 lyt2+zip _ _ = $impossible++cartProduct :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+cartProduct (VShape q1 lyt1) (VShape q2 lyt2) = do+    (q', p1, p2) <- vlCartProduct q1 q2+    lyt1'        <- chainReorder p1 lyt1+    lyt2'        <- chainReorder p2 lyt2+    return $ VShape q' $ zipLayout lyt1' lyt2'+cartProduct _ _ = $impossible++nestProduct :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+nestProduct (VShape q1 lyt1) (VShape q2 lyt2) = do+  (q', p1, p2) <- vlNestProduct q1 q2+  lyt1'        <- chainReorder p1 lyt1+  lyt2'        <- chainReorder p2 lyt2+  return $ VShape q1 (LTuple [lyt1, LNest q' (zipLayout lyt1' lyt2')])+nestProduct _ _ = $impossible++thetaJoin :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+thetaJoin joinPred (VShape q1 lyt1) (VShape q2 lyt2) = do+    (q', p1, p2) <- vlThetaJoin joinPred q1 q2+    lyt1'        <- chainReorder p1 lyt1+    lyt2'        <- chainReorder p2 lyt2+    return $ VShape q' $ zipLayout lyt1' lyt2'+thetaJoin _ _ _ = $impossible++nestJoin :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+nestJoin joinPred (VShape q1 lyt1) (VShape q2 lyt2) = do+    (q', p1, p2) <- vlNestJoin joinPred q1 q2+    lyt1'        <- chainReorder p1 lyt1+    lyt2'        <- chainReorder p2 lyt2+    return $ VShape q1 (LTuple [lyt1, LNest q' (zipLayout lyt1' lyt2')])+nestJoin _ _ _ = $impossible++semiJoin :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+semiJoin joinPred (VShape q1 lyt1) (VShape q2 _) = do+    (qj, r) <- vlSemiJoin joinPred q1 q2+    lyt1'   <- chainRenameFilter r lyt1+    return $ VShape qj lyt1'+semiJoin _ _ _ = $impossible++antiJoin :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+antiJoin joinPred (VShape q1 lyt1) (VShape q2 _) = do+    (qj, r) <- vlAntiJoin joinPred q1 q2+    lyt1'   <- chainRenameFilter r lyt1+    return $ VShape qj lyt1'+antiJoin _ _ _ = $impossible++nub ::  Shape VLDVec -> Build VL (Shape VLDVec)+nub (VShape q lyt) = VShape <$> vlUniqueS q <*> pure lyt+nub _ = $impossible++number ::  Shape VLDVec -> Build VL (Shape VLDVec)+number (VShape q lyt) =+    VShape <$> vlNumber q <*> (pure $ zipLayout lyt (LCol 1))+number _ = $impossible++init ::  Shape VLDVec -> Build VL (Shape VLDVec)+init (VShape q lyt) = do+    i          <- vlAggr AggrCount q+    (q', r, _) <- vlSelectPos q (L.SBRelOp L.Lt) i+    lyt'       <- chainRenameFilter r lyt+    return $ VShape q' lyt'+init _ = $impossible++last ::  Shape VLDVec -> Build VL (Shape VLDVec)+last (VShape qs lyt@(LNest _ _)) = do+    i              <- vlAggr AggrCount qs+    (q, r, _)      <- vlSelectPos qs (L.SBRelOp L.Eq) i+    (LNest qr lyt') <- chainRenameFilter r lyt+    re             <- vlUnboxRename q+    renameOuter re $ VShape qr lyt'+last (VShape qs lyt) = do+    i         <- vlAggr AggrCount qs+    (q, r, _) <- vlSelectPos qs (L.SBRelOp L.Eq) i+    lyt'      <- chainRenameFilter r lyt+    return $ SShape q lyt'+last _ = $impossible++index ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+index (VShape qs (LNest qi lyti)) (SShape i _) = do+    one       <- literal intT (VLInt 1)+    i'        <- vlBinExpr (L.SBNumOp L.Add) i one+    -- Use the unboxing rename vector+    (_, _, r) <- vlSelectPos qs (L.SBRelOp L.Eq) i'+    (qu, ri)  <- vlUnboxNested r qi+    lyti'     <- chainRenameFilter ri lyti+    return $ VShape qu lyti'+index (VShape qs lyt) (SShape i _) = do+    one       <- literal intT (VLInt 1)+    i'        <- vlBinExpr (L.SBNumOp L.Add) i one+    (q, r, _) <- vlSelectPos qs (L.SBRelOp L.Eq) i'+    lyt'      <- chainRenameFilter r lyt+    return $ SShape q lyt'+index _ _ = $impossible++append ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+append (VShape q1 lyt1) (VShape q2 lyt2) = do+    -- Append the current vectors+    (v, p1, p2) <- vlAppend q1 q2+    -- Propagate position changes to descriptors of any inner vectors+    lyt1'       <- renameOuterLyt p1 lyt1+    lyt2'       <- renameOuterLyt p2 lyt2+    -- Append the layouts, i.e. actually append all inner vectors+    lyt'        <- appendLayout lyt1' lyt2'+    return $ VShape v lyt'+appendVec _ _ = $impossible++-- FIXME looks fishy, there should be an unboxing join.+the ::  Shape VLDVec -> Build VL (Shape VLDVec)+the (VShape d lyt@(LNest _ _)) = do+    (_, prop, _)   <- vlSelectPos1 d (L.SBRelOp L.Eq) 1+    (LNest q' lyt') <- chainRenameFilter prop lyt+    return $ VShape q' lyt'+the (VShape d lyt) = do+    (q', prop, _) <- vlSelectPos1 d (L.SBRelOp L.Eq) 1+    lyt'          <- chainRenameFilter prop lyt+    return $ SShape q' lyt'+the _ = $impossible++reverse ::  Shape VLDVec -> Build VL (Shape VLDVec)+reverse (VShape d lyt) = do+    (d', p) <- vlReverse d+    lyt'    <- chainReorder p lyt+    return (VShape d' lyt')+reverse _ = $impossible++tail ::  Shape VLDVec -> Build VL (Shape VLDVec)+tail (VShape d lyt) = do+    p          <- literal intT (VLInt 1)+    (q', r, _) <- vlSelectPos d (L.SBRelOp L.Gt) p+    lyt'       <- chainRenameFilter r lyt+    return $ VShape q' lyt'+tail _ = $impossible++sort :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+sort (VShape q1 lyt1) (VShape q2 lyt2) = do+    let leftWidth  = columnsInLayout lyt1+        rightWidth = columnsInLayout lyt2++        sortExprs = map Column [leftWidth+1..leftWidth+rightWidth]++    -- Sort by all columns from the right vector+    (sortedVec, propVec) <- vlSortS sortExprs =<< vlAlign q1 q2++    -- After sorting, discard the sorting criteria columns from the+    -- right vector+    resVec               <- vlProject (map Column [1..leftWidth]) sortedVec+    lyt1'  <- chainReorder propVec lyt1+    return $ VShape resVec lyt1'+sort _e1 _e2 = $impossible++-- | The right input contains the grouping columns.+group ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+group (VShape q1 lyt1) (VShape q2 lyt2) = do+    let leftWidth  = columnsInLayout lyt1+        rightWidth = columnsInLayout lyt2++        groupExprs = map Column [leftWidth+1..leftWidth+rightWidth]++    (outerVec, innerVec, propVec) <- vlGroupS groupExprs =<< vlAlign q1 q2++    -- Discard the grouping columns in the inner vector+    innerVec' <- vlProject (map Column [1..leftWidth]) innerVec++    lyt1'     <- chainReorder propVec lyt1+    return $ VShape outerVec (LTuple [lyt2, LNest innerVec' lyt1'])+group _e1 _e2 = $impossible++length_ ::  Shape VLDVec -> Build VL (Shape VLDVec)+length_ (VShape q _) = do+    v  <- vlAggr AggrCount q+    return $ SShape v (LCol 1)++restrict ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+restrict(VShape q1 lyt) (VShape q2 (LCol 1)) = do+    -- The right input vector has only one boolean column which+    -- defines wether the tuple at the same position in the left input+    -- is preserved.+    let leftWidth = columnsInLayout lyt+        predicate = Column $ leftWidth + 1++    -- Filter the vector according to the boolean column+    (filteredVec, renameVec) <- vlSelect predicate =<< vlAlign q1 q2++    -- After the selection, discard the boolean column from the right+    resVec                   <- vlProject (map Column [1..leftWidth]) filteredVec+    +    -- Filter any inner vectors+    lyt'                     <- chainRenameFilter renameVec lyt+    return $ VShape resVec lyt'+restrict _e1 _e2 = $impossible++combine ::  Shape VLDVec -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+combine (VShape qb (LCol 1)) (VShape q1 lyt1) (VShape q2 lyt2) = do+    (v, p1, p2) <- vlCombine qb q1 q2+    lyt1'       <- renameOuterLyt p1 lyt1+    lyt2'       <- renameOuterLyt p2 lyt2+    lyt'        <- appendLayout lyt1' lyt2'+    return $ VShape v lyt'+combine l1 l2 l3 = trace (show l1 ++ " " ++ show l2 ++ " " ++ show l3) $ $impossible++-- | Distribute a single value in vector 'q2' over an arbitrary shape.+-- FIXME accepting a scalar shape makes no sense here. we can only distribute over a list.+distSingleton :: Shape VLDVec -> VLDVec -> Layout VLDVec -> Build VL (Shape VLDVec)+distSingleton shape1 q2 lyt2 = do+    let (shapeCon, q1, lyt1) = unwrapShape shape1++        leftWidth  = columnsInLayout lyt1+        rightWidth = columnsInLayout lyt2+        proj       = map Column [leftWidth+1..leftWidth+rightWidth]++    (prodVec, _, propVec) <- q1 `vlCartProduct` q2+    resVec                <- vlProject proj prodVec++    lyt'                  <- chainReorder propVec lyt2+    return $ shapeCon resVec lyt'++dist ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+-- Distributing a single value is implemented using a cartesian+-- product. After the product, we discard columns from the vector that+-- we distributed over. Vectors are swapped because CartProduct uses+-- the descriptor of its left input and that is what we want.+dist (SShape q lyt) v = distSingleton v q lyt+dist (VShape q lyt) (VShape qo lyto) = do+    let leftWidth  = columnsInLayout lyto+        rightWidth = columnsInLayout lyt+        innerProj  = map Column [leftWidth+1..leftWidth+rightWidth]++    (prodVec, _, propVec) <- vlNestProduct qo q+    innerVec              <- vlProject innerProj prodVec++    -- The outer vector does not have columns, it only describes the+    -- shape.+    outerVec              <- vlProject [] qo+    +    -- Replicate any inner vectors+    lyt'                  <- chainReorder propVec lyt++    return $ VShape outerVec (LNest innerVec lyt')+dist _ _ = $impossible++aggr :: (Expr -> AggrFun) -> Shape VLDVec -> Build VL (Shape VLDVec)+aggr afun (VShape q (LCol 1)) =+    SShape <$> vlAggr (afun (Column 1)) q <*> (pure $ LCol 1)+aggr _ _ = $impossible++ifList ::  Shape VLDVec -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+ifList (SShape qb lytb) (VShape q1 lyt1) (VShape q2 lyt2) = do+    let leftWidth = columnsInLayout lyt1+        predicate = Column $ leftWidth + 1++    VShape trueSelVec _        <- distSingleton (VShape q1 lyt1) qb lytb+    (trueVec, trueRenameVec)   <- vlSelect predicate +                                  =<< vlAlign q1 trueSelVec+    trueVec'                   <- vlProject (map Column [1..leftWidth]) trueVec++    let predicate' = UnApp (L.SUBoolOp L.Not) predicate++    VShape falseSelVec _       <- distSingleton (VShape q2 lyt2) qb lytb+    (falseVec, falseRenameVec) <- vlSelect predicate' +                                  =<< vlAlign q2 falseSelVec+    falseVec'                  <- vlProject (map Column [1..leftWidth]) falseVec++    lyt1'                      <- renameOuterLyt trueRenameVec lyt1+    lyt2'                      <- renameOuterLyt falseRenameVec lyt2+    lyt'                       <- appendLayout lyt1' lyt2'++    (bothBranches, _, _)       <- vlAppend trueVec' falseVec'++    return $ VShape bothBranches lyt'+ifList qb (SShape q1 lyt1) (SShape q2 lyt2) = do+    (VShape q lyt) <- ifList qb (VShape q1 lyt1) (VShape q2 lyt2)+    return $ SShape q lyt+ifList _ _ _ = $impossible++tuple :: [Shape VLDVec] -> Build VL (Shape VLDVec)+tuple shapes@(_ : _) = do+    (q, lyts) <- tupleVectors shapes+    let lyts' = zipLayouts lyts+    return $ SShape q (LTuple lyts')+tuple _ = $impossible++tupElem :: TupleIndex -> Shape VLDVec -> Build VL (Shape VLDVec)+tupElem i (SShape q (LTuple lyts)) =+    case lyts !! (tupleIndex i - 1) of+        LNest qi lyt -> return $ VShape qi lyt+        lyt          -> do+            let (lyt', cols) = projectFromPos lyt+            proj <- vlProject (map Column cols) q+            return $ SShape proj lyt'+tupElem _ _ = $impossible++transpose :: Shape VLDVec -> Build VL (Shape VLDVec)+transpose (VShape _ (LNest qi lyt)) = do+    (qo', qi') <- vlTranspose qi+    return $ VShape qo' (LNest qi' lyt)+transpose _ = $impossible+++reshape :: Integer -> Shape VLDVec -> Build VL (Shape VLDVec)+reshape n (VShape q lyt) = do+    (qo, qi) <- vlReshape n q+    return $ VShape qo (LNest qi lyt)+reshape _ _ = $impossible++concat :: Shape VLDVec -> Build VL (Shape VLDVec)+concat (VShape _ (LNest q lyt)) = VShape <$> vlUnsegment q <*> pure lyt+concat _e                       = $impossible++--------------------------------------------------------------------------------+-- Construction of lifted primitives++restrictL :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+restrictL (VShape qo (LNest qi lyt)) (VShape _ (LNest qb (LCol 1))) = do+    VShape qi' lyt' <- restrict (VShape qi lyt) (VShape qb (LCol 1))+    return $ VShape qo (LNest qi' lyt')+restrictL l1                              l2                          =+    trace (show l1 ++ " " ++ show l2) $ $impossible++combineL :: Shape VLDVec -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+combineL (VShape qo (LNest qb (LCol 1)))+         (VShape _ (LNest qi1 lyt1))+         (VShape _ (LNest qi2 lyt2)) = do+    VShape qi' lyt' <- combine (VShape qb (LCol 1)) (VShape qi1 lyt1) (VShape qi2 lyt2)+    return $ VShape qo (LNest qi' lyt')+combineL _ _ _ = $impossible++zipL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+zipL (VShape d1 (LNest q1 lyt1)) (VShape _ (LNest q2 lyt2)) = do+    (q', r1, r2) <- vlZipS q1 q2+    lyt1'        <- chainRenameFilter r1 lyt1+    lyt2'        <- chainRenameFilter r2 lyt2+    return $ VShape d1 (LNest q' $ zipLayout lyt1' lyt2')+zipL _ _ = $impossible++cartProductL :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+cartProductL (VShape d1 (LNest q1 lyt1)) (VShape _ (LNest q2 lyt2)) = do+    (q', p1, p2) <- vlCartProductS q1 q2+    lyt1'        <- chainReorder p1 lyt1+    lyt2'        <- chainReorder p2 lyt2+    return $ VShape d1 (LNest q' $ zipLayout lyt1' lyt2')+cartProductL _ _ = $impossible++nestProductL :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+nestProductL (VShape qd1 (LNest qv1 lyt1)) (VShape _qd2 (LNest qv2 lyt2)) = do+    (qj, qp2) <- vlNestProductS qv1 qv2+    lyt2'     <- chainReorder qp2 lyt2+    let lytJ  = zipLayout lyt1 lyt2'+    return $ VShape qd1 (LNest qv1 (LTuple [lyt1, (LNest qj lytJ)]))+nestProductL _ _ = $impossible++thetaJoinL :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+thetaJoinL joinPred (VShape d1 (LNest q1 lyt1)) (VShape _ (LNest q2 lyt2)) = do+    (q', p1, p2) <- vlThetaJoinS joinPred q1 q2+    lyt1'        <- chainReorder p1 lyt1+    lyt2'        <- chainReorder p2 lyt2+    return $ VShape d1 (LNest q' $ zipLayout lyt1' lyt2')+thetaJoinL _ _ _ = $impossible++-- △^L :: [[a]] -> [[b]] -> [[(a, [(a, b)])]]++-- For the unlifted nestjoin, we could segment the left (outer) input+-- and then use the regular thetajoin implementation. This trick does+-- not work here, as the lifted thetajoin joins on the+-- descriptors. Therefore, we have to 'segment' **after** the join,+-- i.e. use the left input positions as descriptors+nestJoinL :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+nestJoinL joinPred (VShape qd1 (LNest qv1 lyt1)) (VShape _qd2 (LNest qv2 lyt2)) = do+    (qj, qp2) <- vlNestJoinS joinPred qv1 qv2+    lyt2'     <- chainReorder qp2 lyt2+    let lytJ  = zipLayout lyt1 lyt2'+    return $ VShape qd1 (LNest qv1 (LTuple [lyt1,(LNest qj lytJ)]))+nestJoinL _ _ _ = $impossible++semiJoinL :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+semiJoinL joinPred (VShape d1 (LNest q1 lyt1)) (VShape _ (LNest q2 _)) = do+    (qj, r) <- vlSemiJoinS joinPred q1 q2+    lyt1'   <- chainRenameFilter r lyt1+    return $ VShape d1 (LNest qj lyt1')+semiJoinL _ _ _ = $impossible++antiJoinL :: L.JoinPredicate L.JoinExpr -> Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+antiJoinL joinPred (VShape d1 (LNest q1 lyt1)) (VShape _ (LNest q2 _)) = do+    (qj, r) <- vlAntiJoinS joinPred q1 q2+    lyt1'   <- chainRenameFilter r lyt1+    return $ VShape d1 (LNest qj lyt1')+antiJoinL _ _ _ = $impossible++++nubL ::  Shape VLDVec -> Build VL (Shape VLDVec)+nubL (VShape d (LNest q lyt)) =  VShape d <$> (LNest <$> vlUniqueS q <*> pure lyt)+nubL _ = $impossible++numberL ::  Shape VLDVec -> Build VL (Shape VLDVec)+numberL (VShape d (LNest q lyt)) =+    VShape d <$> (LNest <$> vlNumberS q+                            <*> (pure $ zipLayout lyt (LCol 1)))+numberL _ = $impossible++initL ::  Shape VLDVec -> Build VL (Shape VLDVec)+initL (VShape qs (LNest q lyt)) = do+    is         <- vlAggrS AggrCount qs q+    (q', r, _) <- vlSelectPosS q (L.SBRelOp L.Lt) is+    lyt'       <- chainRenameFilter r lyt+    return $ VShape qs (LNest q' lyt')+initL _ = $impossible++lastL ::  Shape VLDVec -> Build VL (Shape VLDVec)+lastL (VShape d (LNest qs lyt@(LNest _ _))) = do+    is          <- vlAggrS AggrCount d qs+    (qs', r, _) <- vlSelectPosS qs (L.SBRelOp L.Eq) is+    lyt'        <- chainRenameFilter r lyt+    re          <- vlUnboxRename qs'+    VShape d <$> renameOuterLyt re lyt'+lastL (VShape d (LNest qs lyt)) = do+    is          <- vlAggrS AggrCount d qs+    (qs', r, _) <- vlSelectPosS qs (L.SBRelOp L.Eq) is+    lyt'        <- chainRenameFilter r lyt+    re          <- vlUnboxRename d+    renameOuter re (VShape qs' lyt')+lastL _ = $impossible++indexL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+indexL (VShape d (LNest qs (LNest qi lyti))) (VShape idxs (LCol 1)) = do+    idxs'          <- vlProject [BinApp (L.SBNumOp L.Add) (Column 1) (Constant $ VLInt 1)] idxs+    (_, _, u)      <- vlSelectPosS qs (L.SBRelOp L.Eq) idxs'+    (qu, ri)       <- vlUnboxNested u qi+    lyti'          <- chainRenameFilter ri lyti+    return $ VShape d (LNest qu lyti')+indexL (VShape d (LNest qs lyt)) (VShape idxs (LCol 1)) = do+    idxs'          <- vlProject [BinApp (L.SBNumOp L.Add) (Column 1) (Constant $ VLInt 1)] idxs+    (qs', r, _)    <- vlSelectPosS qs (L.SBRelOp L.Eq) idxs'+    lyt'           <- chainRenameFilter r lyt+    re             <- vlUnboxRename d+    renameOuter re (VShape qs' lyt')+indexL _ _ = $impossible++appendL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+appendL (VShape d lyt1) (VShape _ lyt2) = do+    VShape d <$> appendLayout lyt1 lyt2+appendL _ _ = $impossible++reverseL ::  Shape VLDVec -> Build VL (Shape VLDVec)+reverseL (VShape d (LNest d1 lyt)) = do+    (d1', p) <- vlReverseS d1+    lyt'     <- chainReorder p lyt+    return (VShape d (LNest d1' lyt'))+reverseL _ = $impossible++theL ::  Shape VLDVec -> Build VL (Shape VLDVec)+theL (VShape d (LNest q lyt)) = do+    (v, p2, _) <- vlSelectPos1S q (L.SBRelOp L.Eq) 1+    prop       <- vlUnboxRename d+    lyt'       <- chainRenameFilter p2 lyt+    v'         <- vlPropRename prop v+    return $ VShape v' lyt'+theL _ = $impossible++tailL ::  Shape VLDVec -> Build VL (Shape VLDVec)+tailL (VShape d (LNest q lyt)) = do+    p              <- vlProject [Constant $ VLInt 1] d+    (v, p2, _)     <- vlSelectPosS q (L.SBRelOp L.Gt) p+    lyt'           <- chainRenameFilter p2 lyt+    return $ VShape d (LNest v lyt')+tailL _ = $impossible++sortL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+sortL (VShape _ (LNest v1 lyt1)) (VShape d2 (LNest v2 lyt2)) = do+    VShape innerVec lyt <- sort (VShape v1 lyt1) (VShape v2 lyt2)+    return $ VShape d2 (LNest innerVec lyt)+sortL _ _ = $impossible++groupL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+groupL (VShape _ (LNest v1 lyt1)) (VShape d2 (LNest v2 lyt2)) = do+    let flatRes = group (VShape v1 lyt1) (VShape v2 lyt2)+    (VShape middleVec (LTuple [groupLyt, LNest innerVec innerLyt])) <- flatRes+    return $ VShape d2 (LNest middleVec (LTuple [groupLyt, LNest innerVec innerLyt]))+groupL _ _ = $impossible++concatL ::  Shape VLDVec -> Build VL (Shape VLDVec)+concatL (VShape d (LNest d' vs)) = do+    p   <- vlUnboxRename d'+    vs' <- renameOuterLyt p vs+    return $ VShape d vs'+concatL _ = $impossible++lengthL ::  Shape VLDVec -> Build VL (Shape VLDVec)+lengthL (VShape q (LNest qi _)) = do+    ls  <- vlAggrS AggrCount q qi+    lsu <- vlUnboxScalar q ls+    return $ VShape lsu (LCol 1)+lengthL s = trace (show s) $ $impossible++outer ::  Shape VLDVec -> Build VL VLDVec+outer (SShape _ _)        = $impossible+outer (VShape q _)        = return q++aggrL :: (Expr -> AggrFun) -> Shape VLDVec -> Build VL (Shape VLDVec)+aggrL afun (VShape d (LNest q (LCol 1))) = do+    qr <- vlAggrS (afun (Column 1)) d q+    qu <- vlUnboxScalar d qr+    return $ VShape qu (LCol 1)+aggrL _ _ = $impossible++distL ::  Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+distL (VShape q1 lyt1) (VShape d (LNest q2 lyt2)) = do+    (qa, p)             <- vlDistLift q1 q2+    lyt1'               <- chainReorder p lyt1+    let lyt             = zipLayout lyt1' lyt2+    VShape qf lytf <- tupElemL First $ VShape qa lyt+    return $ VShape d (LNest qf lytf)+distL _e1 _e2 = $impossible++tupleL :: [Shape VLDVec] -> Build VL (Shape VLDVec)+tupleL shapes@(_ : _) = do+    (q, lyts) <- zipVectors shapes+    let lyts' = zipLayouts lyts+    return $ VShape q (LTuple lyts')+tupleL _ = $impossible++tupElemL :: TupleIndex -> Shape VLDVec -> Build VL (Shape VLDVec)+tupElemL i (VShape q (LTuple lyts)) = do+    let (lyt', cols) = projectFromPos $ lyts !! (tupleIndex i - 1)+    proj <- vlProject (map Column cols) q+    return $ VShape proj lyt'+tupElemL i s = trace (show i ++ " " ++ show s) $impossible++transposeL :: Shape VLDVec -> Build VL (Shape VLDVec)+transposeL (VShape qo (LNest qm (LNest qi lyt))) = do+    (qm', qi') <- vlTransposeS qm qi+    return $ VShape qo (LNest qm' (LNest qi' lyt))+transposeL _ = $impossible++reshapeL :: Integer -> Shape VLDVec -> Build VL (Shape VLDVec)+reshapeL n (VShape qo (LNest qi lyt)) = do+    (qm, qi') <- vlReshapeS n qi+    return $ VShape qo (LNest qm (LNest qi' lyt))+reshapeL _ _ = $impossible++-- | Create a projection list that extracts only those columns+-- referenced in the sub-layout passed as argument, and shift column+-- names in the sub-layout to the beginning.+projectFromPos :: Layout VLDVec -> (Layout VLDVec , [DBCol])+projectFromPos = (\(x,y,_) -> (x,y)) . (projectFromPosWork 1)+  where+    projectFromPosWork :: Int -> Layout VLDVec -> (Layout VLDVec, [DBCol], Int)+    projectFromPosWork c (LCol i)      = (LCol c, [i], c + 1)+    projectFromPosWork c (LNest q l)   = (LNest q l, [], c)+    projectFromPosWork c (LTuple lyts) = (LTuple psRes, colsRes, cRes)+      where+        (psRes, colsRes, cRes) = List.foldl' tupleWorker ([], [], c) lyts++    tupleWorker (psAcc, colsAcc, cAcc) lyt = (psAcc ++ [lyt'], colsAcc ++ cols, c')+      where+        (lyt', cols, c') = projectFromPosWork cAcc lyt++singleton :: Shape VLDVec -> Build VL (Shape VLDVec)+singleton (VShape q lyt) = do+    VLDVec d <- vlSingletonDescr+    return $ VShape (VLDVec d) (LNest q lyt)+singleton (SShape q1 lyt) = return $ VShape q1 lyt++singletonL :: Shape VLDVec -> Build VL (Shape VLDVec)+singletonL (VShape q lyt) = do+    innerVec <- vlSegment q+    outerVec <- vlProject [] q+    return $ VShape outerVec (LNest innerVec lyt)+singletonL _ = $impossible++--------------------------------------------------------------------------------+-- Construction of base tables and literal tables++-- | Create a VL reference to a base table.+dbTable ::  String -> [L.Column] -> L.TableHints -> Build VL (Shape VLDVec)+dbTable n cs ks = do+    t <- vlTableRef n (map (mapSnd typeToScalarType) cs) ks+    return $ VShape t (LTuple [LCol i | i <- [1..length cs]])++-- | Create a VL representation of a literal value.+mkLiteral ::  Type -> L.Val -> Build VL (Shape VLDVec)+-- Translate an outer list+mkLiteral t@(ListT _) (L.ListV es) = do+    ((tabTys, tabCols), lyt, _) <- toPlan (mkDescriptor [P.length es]) t 1 es+    let emptinessFlag = case es of+          []    -> L.PossiblyEmpty+          _ : _ -> L.NonEmpty+    litNode <- vlLit emptinessFlag (P.reverse tabTys) $ map P.reverse tabCols+    return $ VShape litNode lyt+-- Translate a non-list value, i.e. scalar or tuple+mkLiteral t e           = do+    -- There is only one element in the outermost vector+    ((tabTys, [tabCols]), layout, _) <- toPlan (mkDescriptor [1]) (ListT t) 1 [e]+    litNode <- vlLit L.NonEmpty (P.reverse tabTys) [(P.reverse tabCols)]+    return $ SShape litNode layout++type Table = ([Type], [[VLVal]])++-- | Add values to a vector. If necessary (i.e. inner lists are+-- encountered), create new inner vectors. 'toPlan' receives a+-- descriptor that has enough space for all elements of the list that+-- are currently encoded.++-- FIXME Check if inner list literals are nonempty and flag VL+-- literals appropriately.  +toPlan ::  Table -> Type -> Int -> [L.Val] -> Build VL (Table, Layout VLDVec, Int)+toPlan (tabTys, tabCols) (ListT t) nextCol es =+    -- Inspect the element type of the list to be encoded+    case t of+        ListT _ -> do+            let vs = map listElems es+                -- Create a vector with one entry for each element of an inner list+                d  = mkDescriptor $ map P.length vs+            -- Add the inner list elements to the vector+            ((innerTabTys, innerTabCols), lyt, _) <- toPlan d t 1 (P.concat vs)+            n <- vlLit L.PossiblyEmpty (P.reverse innerTabTys) (map P.reverse innerTabCols)+            return ((tabTys, tabCols), LNest n lyt, nextCol)++        TupleT elemTys -> do+            -- We add tuple elements column-wise. If the list to be+            -- encoded is empty, create an empty list for each column.+            let colsVals = case es of+                               [] -> map (const []) elemTys+                               _  -> List.transpose $ map tupleElems es+            mkTupleTable (tabTys, tabCols) nextCol [] colsVals elemTys++        _ -> let (hd, vs) = mkColumn t es+             in return ((hd:tabTys, zipWith (:) vs tabCols), (LCol nextCol), nextCol + 1)++toPlan (tabTys, tabCols) t c v =+    let (hd, v') = mkColumn t v+    in return $ ((hd:tabTys, zipWith (:) v' tabCols), (LCol c), c + 1)++-- | Construct the literal table for a list of tuples.+mkTupleTable :: Table                         -- ^ The literal table so far.+   -> Int                                     -- ^ The next available column offset+   -> [Layout VLDVec]                         -- ^ The layouts of the tuple elements constructed so far+   -> [[L.Val]]                               -- ^ Values for the tuple elements+   -> [Type]                                  -- ^ Types for the tuple elements+   -> Build VL (Table, Layout VLDVec, Int)+mkTupleTable tab nextCol lyts (colVals : colsVals) (t : ts) = do+    (tab', lyt, nextCol') <- toPlan tab (ListT t) nextCol colVals+    mkTupleTable tab' nextCol' (lyt : lyts) colsVals ts+mkTupleTable tab nextCol lyts []                   []       = do+    return $ (tab, LTuple $ P.reverse lyts, nextCol)+mkTupleTable _   _       _    _                    _        = $impossible++literal :: Type -> VLVal -> Build VL VLDVec+literal t v = vlLit L.NonEmpty [t] [[VLInt 1, VLInt 1, v]]++listElems :: L.Val -> [L.Val]+listElems (L.ListV es) = es+listElems _            = $impossible++tupleElems :: L.Val -> [L.Val]+tupleElems (L.TupleV es) = es+tupleElems _             = $impossible++mkColumn :: Type -> [L.Val] -> (Type, [VLVal])+mkColumn t vs = (t, [pVal v | v <- vs])++mkDescriptor :: [Int] -> Table+mkDescriptor lengths =+    let header = []+        body   = [ [VLInt $ fromInteger p, VLInt $ fromInteger d]+                 | d <- P.concat [ replicate l p | p <- [1..] | l <- lengths ]  +                 | p <- [1..]+                 ]+    in (header, body)++--------------------------------------------------------------------------------+-- Helper functions for zipping/tuple construction++zipLayout :: Layout VLDVec -> Layout VLDVec -> Layout VLDVec+zipLayout l1 l2 = let offSet = columnsInLayout l1+                      l2' = incrementPositions offSet l2+                   in LTuple [l1, l2']++incrementPositions :: Int -> Layout VLDVec -> Layout VLDVec+incrementPositions i (LCol n)       = LCol $ n + i+incrementPositions _i v@(LNest _ _) = v+incrementPositions i (LTuple lyts)  = LTuple $ map (incrementPositions i) lyts++zipLayouts :: [Layout VLDVec] -> [Layout VLDVec]+zipLayouts layouts = go 0 layouts++  where+    go :: Int -> [Layout VLDVec] -> [Layout VLDVec]+    go 0 (lyt : lyts) = lyt : go (columnsInLayout lyt) lyts+    go o (lyt : lyts) = incrementPositions o lyt : go (o + columnsInLayout lyt) lyts+    go _ []           = []++zipVectors :: [Shape VLDVec] -> Build VL (VLDVec, [Layout VLDVec])+zipVectors (VShape q1 lyt1 : [])     = return (q1, [lyt1])+zipVectors (VShape q1 lyt1 : shapes) = do+    (q, lyts) <- zipVectors shapes+    qz' <- vlAlign q1 q+    return (qz', lyt1 : lyts)+zipVectors _ = $impossible++tupleVectors :: [Shape VLDVec] -> Build VL (VLDVec, [Layout VLDVec])+tupleVectors (SShape q1 lyt1 : [])     = return (q1, [lyt1])+tupleVectors (VShape q1 lyt1 : [])     = do+    qo <- vlSingletonDescr+    qi <- vlUnsegment q1+    return (qo, [LNest qi lyt1])+tupleVectors (SShape q1 lyt1 : shapes) = do+    (q, lyts) <- tupleVectors shapes+    qz'       <- vlAlign q1 q+    return (qz', lyt1 : lyts)+tupleVectors (VShape q1 lyt1 : shapes) = do+    (q, lyts) <- tupleVectors shapes+    q1'       <- vlUnsegment q1+    return (q, LNest q1' lyt1 : lyts)+tupleVectors s = error $ show s++--------------------------------------------------------------------------------+-- Compile-time operations that implement higher-lifted primitives.++-- | Remove the 'n' outer layers of nesting from a nested list+-- (Prins/Palmer: 'extract').+forget :: Nat -> Shape VLDVec -> Shape VLDVec+forget Zero _                               = $impossible+forget (Succ Zero) (VShape _ (LNest q lyt)) = VShape q lyt+forget (Succ n)    (VShape _ lyt)           = extractInnerVec n lyt+forget _           _                        = $impossible++extractInnerVec :: Nat -> Layout VLDVec -> Shape VLDVec+extractInnerVec (Succ Zero) (LNest _ (LNest q lyt)) = VShape q lyt+extractInnerVec (Succ n)    (LNest _ lyt)           = extractInnerVec n lyt+extractInnerVec n           l                       = trace (show n ++ " " ++ show l) $impossible++-- | Prepend the 'n' outer layers of nesting from the first input to+-- the second input (Prins/Palmer: 'insert').+imprint :: Nat -> Shape VLDVec -> Shape VLDVec -> Shape VLDVec+imprint (Succ Zero) (VShape d _) (VShape vi lyti) =+    VShape d (LNest vi lyti)+imprint (Succ n) (VShape d lyt) (VShape vi lyti)  =+    VShape d (implantInnerVec n lyt vi lyti)+imprint _          _                   _          =+    $impossible++implantInnerVec :: Nat -> Layout VLDVec -> VLDVec -> Layout VLDVec -> Layout VLDVec+implantInnerVec (Succ Zero) (LNest d _)   vi lyti   =+    LNest d $ LNest vi lyti+implantInnerVec (Succ n)      (LNest d lyt) vi lyti =+    LNest d $ implantInnerVec n lyt vi lyti+implantInnerVec _          _            _  _        =+    $impossible++--------------------------------------------------------------------------------+-- Vectorization Helper Functions++-- | Take a shape apart by extracting the vector, the layout and the+-- shape constructor itself.+unwrapShape :: Shape VLDVec -> (VLDVec -> Layout VLDVec -> Shape VLDVec, VLDVec, Layout VLDVec)+unwrapShape (VShape q lyt) = (VShape, q, lyt)+unwrapShape (SShape q lyt) = (SShape, q, lyt)++fromLayout :: Layout VLDVec -> [DBCol]+fromLayout (LCol i)      = [i]+fromLayout (LNest _ _)   = []+fromLayout (LTuple lyts) = concatMap fromLayout lyts++-- | chainRenameFilter renames and filters a vector according to a rename vector+-- and propagates these changes to all inner vectors. No reordering is applied,+-- that is the propagation vector must not change the order of tuples.+chainRenameFilter :: RVec -> Layout VLDVec -> Build VL (Layout VLDVec)+chainRenameFilter _ l@(LCol _) = return l+chainRenameFilter r (LNest q lyt) = do+    (q', r') <- vlPropFilter r q+    lyt'     <- chainRenameFilter r' lyt+    return $ LNest q' lyt'+chainRenameFilter r (LTuple lyts) =+    LTuple <$> mapM (chainRenameFilter r) lyts++-- | chainReorder renames and filters a vector according to a propagation vector+-- and propagates these changes to all inner vectors. The propagation vector+-- may change the order of tuples.+chainReorder :: PVec -> Layout VLDVec -> Build VL (Layout VLDVec)+chainReorder _ l@(LCol _) = return l+chainReorder p (LNest q lyt) = do+    (q', p') <- vlPropReorder p q+    lyt'     <- chainReorder p' lyt+    return $ LNest q' lyt'+chainReorder p (LTuple lyts) =+    LTuple <$> mapM (chainReorder p) lyts++-- | renameOuter renames and filters a vector according to a rename+-- vector. Changes are not propagated to inner vectors.+renameOuter :: RVec -> Shape VLDVec -> Build VL (Shape VLDVec)+renameOuter p (VShape q lyt) = flip VShape lyt <$> vlPropRename p q+renameOuter _ _ = error "renameOuter: Not possible"++renameOuterLyt :: RVec -> Layout VLDVec -> Build VL (Layout VLDVec)+renameOuterLyt _ l@(LCol _)    = return l+renameOuterLyt r (LNest q lyt) = flip LNest lyt <$> vlPropRename r q+renameOuterLyt r (LTuple lyts) = LTuple <$> mapM (renameOuterLyt r) lyts++-- | Append two inner vectors (segment-wise).+appendInnerVec :: Shape VLDVec -> Shape VLDVec -> Build VL (Shape VLDVec)+appendInnerVec (VShape q1 lyt1) (VShape q2 lyt2) = do+    -- Append the current vectors+    (v, p1, p2) <- vlAppendS q1 q2+    -- Propagate position changes to descriptors of any inner vectors+    lyt1'       <- renameOuterLyt p1 lyt1+    lyt2'       <- renameOuterLyt p2 lyt2+    -- Append the layouts, i.e. actually append all inner vectors+    lyt'        <- appendLayout lyt1' lyt2'+    return $ VShape v lyt'+appendInnerVec _ _ = $impossible++-- | Traverse a layout and append all nested vectors that are+-- encountered.+appendLayout :: Layout VLDVec -> Layout VLDVec -> Build VL (Layout VLDVec)+appendLayout (LCol i1) (LCol i2)+    | i1 == i2  = return $ LCol i1+    | otherwise = error "appendR': Incompatible vectors"+-- Append two nested vectors+appendLayout (LNest q1 lyt1) (LNest q2 lyt2) = do+    a <- appendInnerVec (VShape q1 lyt1) (VShape q2 lyt2)+    case a of+        VShape q lyt -> return $ LNest q lyt+        _            -> $impossible+appendLayout (LTuple lyts1) (LTuple lyts2) =+    LTuple <$> (sequence $ zipWith appendLayout lyts1 lyts2)+appendLayout _ _ = $impossible
+ tests/CombinatorTests.hs view
@@ -0,0 +1,1241 @@+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ViewPatterns          #-}++module CombinatorTests +    ( tests_types+    , tests_boolean+    , tests_tuples+    , tests_numerics+    , tests_maybe+    , tests_either+    , tests_lists+    , tests_lifted+    , tests_combinators_hunit+    ) where++import           Common++import qualified Database.DSH as Q++import           Test.QuickCheck+import           Test.HUnit(Assertion)+import           Test.Framework (Test, testGroup)+import           Test.Framework.Providers.QuickCheck2 (testProperty)+import           Test.Framework.Providers.HUnit+-- import           Data.DeriveTH++import           Data.Char+import           Data.Text (Text)+import qualified Data.Text as Text++import           Data.List+import           Data.Maybe+import           Data.Either+import           GHC.Exts++{-+data D0 = C01 deriving (Eq,Ord,Show)++derive makeArbitrary ''D0+Q.deriveDSH ''D0++data D1 a = C11 a deriving (Eq,Ord,Show)++derive makeArbitrary ''D1+Q.deriveDSH ''D1++data D2 a b = C21 a b b a deriving (Eq,Ord,Show)++derive makeArbitrary ''D2+Q.deriveDSH ''D2++data D3 = C31 +        | C32 +        deriving (Eq,Ord,Show)++derive makeArbitrary ''D3+Q.deriveDSH ''D3++data D4 a = C41 a +          | C42 +          deriving (Eq,Ord,Show)++derive makeArbitrary ''D4+Q.deriveDSH ''D4++data D5 a = C51 a +          | C52 +          | C53 a a +          | C54 a a a +          deriving (Eq,Ord,Show)++derive makeArbitrary ''D5+Q.deriveDSH ''D5++data D6 a b c d e = C61 { c611 :: a, c612 :: (a,b,c,d) } +                  | C62 +                  | C63 a b +                  | C64 (a,b,c) +                  | C65 a b c d e +                  deriving (Eq,Ord,Show)++derive makeArbitrary ''D6+Q.deriveDSH ''D6++-}++tests_types :: Test+tests_types = testGroup "Supported Types"+  [ testProperty "()" $ prop_unit+  , testProperty "Bool" $ prop_bool+  , testProperty "Char" $ prop_char+  , testProperty "Text" $ prop_text+  , testProperty "Integer" $ prop_integer+  , testProperty "Double" $ prop_double+  , testProperty "[Integer]" $ prop_list_integer_1+  , testProperty "[[Integer]]" $ prop_list_integer_2+  , testProperty "[[[Integer]]]" $ prop_list_integer_3+  , testProperty "[(Integer, Integer)]" $ prop_list_tuple_integer+  , testProperty "([], [])" $ prop_tuple_list_integer+  , testProperty "(,[])" $ prop_tuple_integer_list+  , testProperty "(,[],)" $ prop_tuple_integer_list_integer+  , testProperty "Maybe Integer" $ prop_maybe_integer+  , testProperty "Either Integer Integer" $ prop_either_integer+  , testProperty "(Int, Int, Int, Int)" $ prop_tuple4+  , testProperty "(Int, Int, Int, Int, Int)" $ prop_tuple5+{-+  , testProperty "D0" $ prop_d0+  , testProperty "D1" $ prop_d1+  , testProperty "D2" $ prop_d2+  , testProperty "D3" $ prop_d3+  , testProperty "D4" $ prop_d4+  , testProperty "D5" $ prop_d5+  , testProperty "D6" $ prop_d6+-}+  ]++tests_boolean :: Test+tests_boolean = testGroup "Equality, Boolean Logic and Ordering"+  [ testProperty "&&" $ prop_infix_and+  , testProperty "||" $ prop_infix_or+  , testProperty "not" $ prop_not+  , testProperty "eq" $ prop_eq+  , testProperty "neq" $ prop_neq+  , testProperty "cond" $ prop_cond+  , testProperty "cond tuples" $ prop_cond_tuples+  , testProperty "cond ([[Integer]], [[Integer]])" $ prop_cond_list_tuples+  , testProperty "lt" $ prop_lt+  , testProperty "lte" $ prop_lte+  , testProperty "gt" $ prop_gt+  , testProperty "gte" $ prop_gte+  , testProperty "min_integer" $ prop_min_integer+  , testProperty "min_double" $ prop_min_double+  , testProperty "max_integer" $ prop_max_integer+  , testProperty "max_double" $ prop_max_double+  ]++tests_tuples :: Test+tests_tuples = testGroup "Tuples"+  [ testProperty "fst" $ prop_fst+  , testProperty "snd" $ prop_snd+  , testProperty "fst ([], [])" prop_fst_nested+  , testProperty "snd ([], [])" prop_snd_nested+  , testProperty "tup3_1" prop_tup3_1+  , testProperty "tup3_2" prop_tup3_2+  , testProperty "tup3_3" prop_tup3_3+  , testProperty "tup4_2" prop_tup4_2+  , testProperty "tup4_4" prop_tup4_4+  , testProperty "tup3_nested" prop_tup3_nested+  , testProperty "tup4_tup3" prop_tup4_tup3+  ]++tests_numerics :: Test+tests_numerics = testGroup "Numerics"+  [ testProperty "add_integer" $ prop_add_integer+  , testProperty "add_double" $ prop_add_double+  , testProperty "mul_integer" $ prop_mul_integer+  , testProperty "mul_double" $ prop_mul_double+  , testProperty "div_double" $ prop_div_double+  , testProperty "integer_to_double" $ prop_integer_to_double+  , testProperty "integer_to_double_+" $ prop_integer_to_double_arith+  , testProperty "abs_integer" $ prop_abs_integer+  , testProperty "abs_double" $ prop_abs_double+  , testProperty "signum_integer" $ prop_signum_integer+  , testProperty "signum_double" $ prop_signum_double+  , testProperty "negate_integer" $ prop_negate_integer+  , testProperty "negate_double" $ prop_negate_double+  , testProperty "trig_sin" $ prop_trig_sin+  , testProperty "trig_cos" $ prop_trig_cos+  , testProperty "trig_tan" $ prop_trig_tan+  , testProperty "trig_asin" $ prop_trig_asin+  , testProperty "trig_acos" $ prop_trig_acos+  , testProperty "trig_atan" $ prop_trig_atan+  , testProperty "sqrt" $ prop_sqrt+  , testProperty "log" $ prop_log+  , testProperty "exp" $ prop_exp+  ]++tests_maybe :: Test+tests_maybe = testGroup "Maybe"+        [ testProperty "maybe" $ prop_maybe+        , testProperty "just" $ prop_just+        , testProperty "isJust" $ prop_isJust+        , testProperty "isNothing" $ prop_isNothing+        , testProperty "fromJust" $ prop_fromJust+        , testProperty "fromMaybe" $ prop_fromMaybe+        , testProperty "listToMaybe" $ prop_listToMaybe+        , testProperty "maybeToList" $ prop_maybeToList+        , testProperty "catMaybes" $ prop_catMaybes+        , testProperty "mapMaybe" $ prop_mapMaybe+        ]++tests_either :: Test+tests_either = testGroup "Either"+        [ testProperty "left" $ prop_left+        , testProperty "right" $ prop_right+        , testProperty "isLeft" $ prop_isLeft+        , testProperty "isRight" $ prop_isRight+        , testProperty "either" $ prop_either+        , testProperty "lefts" $ prop_lefts+        , testProperty "rights" $ prop_rights+        , testProperty "partitionEithers" $ prop_partitionEithers+        ]++tests_lists :: Test+tests_lists = testGroup "Lists"+        [ testProperty "singleton" prop_singleton+        , testProperty "head" $ prop_head+        , testProperty "tail" $ prop_tail+        , testProperty "cons" $ prop_cons+        , testProperty "snoc" $ prop_snoc+        , testProperty "take" $ prop_take+        , testProperty "drop" $ prop_drop+        , testProperty "take ++ drop" $ prop_takedrop+        , testProperty "map" $ prop_map+        , testProperty "filter" $ prop_filter+        , testProperty "filter > 42" $ prop_filter_gt+        , testProperty "filter > 42 (,[])" $ prop_filter_gt_nested+        , testProperty "the" $ prop_the+        , testProperty "last" $ prop_last+        , testProperty "init" $ prop_init+        , testProperty "null" $ prop_null+        , testProperty "length" $ prop_length+        , testProperty "length tuple list" $ prop_length_tuple+        , testProperty "index [Integer]" $ prop_index+        , testProperty "index [(Integer, [Integer])]" $ prop_index_pair+        , testProperty "index [[]]" $ prop_index_nest+        , testProperty "reverse" $ prop_reverse+        , testProperty "reverse [[]]" $ prop_reverse_nest+        , testProperty "append" $ prop_append+        , testProperty "append nest" $ prop_append_nest+        , testProperty "groupWith" $ prop_groupWith+        , testProperty "groupWithKey" $ prop_groupWithKey+        , testProperty "groupWith length" $ prop_groupWith_length+        , testProperty "groupWithKey length" $ prop_groupWithKey_length+        , testProperty "sortWith" $ prop_sortWith+        , testProperty "sortWith [(,)]" $ prop_sortWith_pair+        , testProperty "sortWith [(,[])]" $ prop_sortWith_nest+        , testProperty "and" $ prop_and+        , testProperty "or" $ prop_or+        , testProperty "any_zero" $ prop_any_zero+        , testProperty "all_zero" $ prop_all_zero+        , testProperty "sum_integer" $ prop_sum_integer+        , testProperty "sum_double" $ prop_sum_double+        , testProperty "avg_integer" $ prop_avg_integer+        , testProperty "avg_double" $ prop_avg_double+        , testProperty "concat" $ prop_concat+        , testProperty "concatMap" $ prop_concatMap+        , testProperty "maximum" $ prop_maximum+        , testProperty "minimum" $ prop_minimum+        , testProperty "splitAt" $ prop_splitAt+        , testProperty "takeWhile" $ prop_takeWhile+        , testProperty "dropWhile" $ prop_dropWhile+        , testProperty "span" $ prop_span+        , testProperty "break" $ prop_break+        , testProperty "elem" $ prop_elem+        , testProperty "notElem" $ prop_notElem+        , testProperty "lookup" $ prop_lookup+        , testProperty "zip" $ prop_zip+        , testProperty "zip3" $ prop_zip3+        , testProperty "zipWith" $ prop_zipWith+        , testProperty "zipWith3" $ prop_zipWith3+        , testProperty "unzip" $ prop_unzip+        , testProperty "unzip3" $ prop_unzip3+        , testProperty "nub" $ prop_nub+        , testProperty "number" $ prop_number+        , testProperty "reshape" $ prop_reshape+        , testProperty "reshape2" $ prop_reshape2+        , testProperty "transpose" $ prop_transpose+        ]++tests_lifted :: Test+tests_lifted = testGroup "Lifted operations"+        [ testProperty "Lifted &&" $ prop_infix_map_and+        , testProperty "Lifted ||" $ prop_infix_map_or+        , testProperty "Lifted not" $ prop_map_not+        , testProperty "Lifted eq" $ prop_map_eq+        , testProperty "Lifted neq" $ prop_map_neq+        , testProperty "Lifted cond" $ prop_map_cond+        , testProperty "Lifted cond tuples" $ prop_map_cond_tuples+        , testProperty "Lifted cond + concat" $ prop_concatmapcond+        , testProperty "Lifted lt" $ prop_map_lt+        , testProperty "Lifted lte" $ prop_map_lte+        , testProperty "Lifted gt" $ prop_map_gt+        , testProperty "Lifted gte" $ prop_map_gte+        , testProperty "Lifted cons" $ prop_map_cons+        , testProperty "Lifted concat" $ prop_map_concat+        , testProperty "Lifted fst" $ prop_map_fst+        , testProperty "Lifted snd" $ prop_map_snd+        , testProperty "Lifted the" $ prop_map_the+        --, testProperty "Lifed and" $ prop_map_and+        , testProperty "map (map (*2))" $ prop_map_map_mul+        , testProperty "map (map (map (*2)))" $ prop_map_map_map_mul+        , testProperty "map (\\x -> map (\\y -> x + y) ..) .." $ prop_map_map_add+        , testProperty "Lifted groupWith" $ prop_map_groupWith+        , testProperty "Lifted groupWithKey" $ prop_map_groupWithKey+        , testProperty "Lifted sortWith" $ prop_map_sortWith+        , testProperty "Lifted sortWith [(,)]" $ prop_map_sortWith_pair+        , testProperty "Lifted sortWith [(,[])]" $ prop_map_sortWith_nest+        , testProperty "Lifted sortWith length" $ prop_map_sortWith_length+        , testProperty "Lifted groupWithKey length" $ prop_map_groupWithKey_length+        , testProperty "Lifted length" $ prop_map_length+        , testProperty "Lifted length on [[(a,b)]]" $ prop_map_length_tuple+        , testProperty "Sortwith length nested" $ prop_sortWith_length_nest+        , testProperty "GroupWithKey length nested" $ prop_groupWithKey_length_nest+        , testProperty "Lift minimum" $ prop_map_minimum+        , testProperty "map (map minimum)" $ prop_map_map_minimum+        , testProperty "Lift maximum" $ prop_map_maximum+        , testProperty "map (map maximum)" $ prop_map_map_maximum+        , testProperty "map integer_to_double" $ prop_map_integer_to_double+        , testProperty "map tail" $ prop_map_tail+        , testProperty "map unzip" $ prop_map_unzip+        , testProperty "map reverse" $ prop_map_reverse+        , testProperty "map reverse [[]]" $ prop_map_reverse_nest+        , testProperty "map and" $ prop_map_and+        , testProperty "map (map and)" $ prop_map_map_and+        , testProperty "map sum" $ prop_map_sum+        , testProperty "map avg" $ prop_map_avg+        , testProperty "map (map sum)" $ prop_map_map_sum+        , testProperty "map or" $ prop_map_or+        , testProperty "map (map or)" $ prop_map_map_or+        , testProperty "map any zero" $ prop_map_any_zero+        , testProperty "map all zero" $ prop_map_all_zero+        , testProperty "map filter" $ prop_map_filter+        , testProperty "map filter > 42" $ prop_map_filter_gt+        , testProperty "map filter > 42 (,[])" $ prop_map_filter_gt_nested+        , testProperty "map append" $ prop_map_append+        , testProperty "map index" $ prop_map_index+        , testProperty "map index [[]]" $ prop_map_index_nest+        , testProperty "map init" $ prop_map_init+        , testProperty "map last" $ prop_map_last+        , testProperty "map null" $ prop_map_null+        , testProperty "map nub" $ prop_map_nub+        , testProperty "map snoc" $ prop_map_snoc+        , testProperty "map take" $ prop_map_take+        , testProperty "map drop" $ prop_map_drop+        , testProperty "map zip" $ prop_map_zip+        , testProperty "map takeWhile" $ prop_map_takeWhile+        , testProperty "map dropWhile" $ prop_map_dropWhile+        , testProperty "map span" $ prop_map_span+        , testProperty "map break" $ prop_map_break+        , testProperty "map number" $ prop_map_number+        , testProperty "map reshape" $ prop_map_reshape+        , testProperty "map reshape2" $ prop_map_reshape2+        -- , testProperty "map transpose" $ prop_map_transpose+        , testProperty "map sin" $ prop_map_trig_sin+        , testProperty "map cos" $ prop_map_trig_cos+        , testProperty "map tan" $ prop_map_trig_tan+        , testProperty "map asin" $ prop_map_trig_asin+        , testProperty "map acos" $ prop_map_trig_acos+        , testProperty "map atan" $ prop_map_trig_atan+        , testProperty "map log" $ prop_map_log+        , testProperty "map exp" $ prop_map_exp+        , testProperty "map sqrt" $ prop_map_sqrt+        ]++tests_combinators_hunit :: Test+tests_combinators_hunit = testGroup "HUnit combinators"+    [ testCase "hnegative_sum" hnegative_sum+    , testCase "hnegative_map_sum" hnegative_map_sum+    , testCase "hmap_transpose" hmap_transpose+    ]++-- * Supported Types++prop_unit :: () -> Property+prop_unit = makeProp id id++prop_bool :: Bool -> Property+prop_bool = makeProp id id++prop_integer :: Integer -> Property+prop_integer = makeProp id id++prop_double :: Double -> Property+prop_double = makePropDouble id id++prop_char :: Char -> Property+prop_char c = isPrint c ==> makeProp id id c++prop_text :: Text -> Property+prop_text t = Text.all isPrint t ==> makeProp id id t++prop_list_integer_1 :: [Integer] -> Property+prop_list_integer_1 = makeProp id id++prop_list_integer_2 :: [[Integer]] -> Property+prop_list_integer_2 = makeProp id id++prop_list_integer_3 :: [[[Integer]]] -> Property+prop_list_integer_3 = makeProp id id++prop_list_tuple_integer :: [(Integer, Integer)] -> Property+prop_list_tuple_integer = makeProp id id++prop_maybe_integer :: Maybe Integer -> Property+prop_maybe_integer = makeProp id id++prop_tuple_list_integer :: ([Integer], [Integer]) -> Property+prop_tuple_list_integer = makeProp id id++prop_tuple_integer_list :: (Integer, [Integer]) -> Property+prop_tuple_integer_list = makeProp id id++prop_tuple_integer_list_integer :: (Integer, [Integer], Integer) -> Property+prop_tuple_integer_list_integer = makeProp id id++prop_either_integer :: Either Integer Integer -> Property+prop_either_integer = makeProp id id++prop_tuple4 :: [(Integer, Integer, Integer, Integer)] -> Property+prop_tuple4 = makeProp (Q.map (\(Q.view -> (a, b, c, d)) -> Q.tup4 (a + c) (b - d) b d))+                       (map (\(a, b, c, d) -> (a + c, b - d, b, d)))++prop_tuple5 :: [(Integer, Integer, Integer, Integer, Integer)] -> Property+prop_tuple5 = makeProp (Q.map (\(Q.view -> (a, _, c, _, e)) -> Q.tup3 a c e))+                       (map (\(a, _, c, _, e) -> (a, c, e)))++{-++prop_d0 :: D0 -> Property+prop_d0 = makeProp id id++prop_d1 :: D1 Integer -> Property+prop_d1 = makeProp id id++prop_d2 :: D2 Integer Integer -> Property+prop_d2 = makeProp id id++prop_d3 :: D3 -> Property+prop_d3 = makeProp id id++prop_d4 :: D4 Integer -> Property+prop_d4 = makeProp id id++prop_d5 :: D5 Integer -> Property+prop_d5 = makeProp id id++prop_d6 :: D6 Integer Integer Integer Integer Integer -> Property+prop_d6 = makeProp id id++-}++-- * Equality, Boolean Logic and Ordering++prop_infix_and :: (Bool,Bool) -> Property+prop_infix_and = makeProp (uncurryQ (Q.&&)) (uncurry (&&))++prop_infix_map_and :: (Bool, [Bool]) -> Property+prop_infix_map_and = makeProp (\x -> Q.map ((Q.fst x) Q.&&) $ Q.snd x) (\(x,xs) -> map (x &&) xs)++prop_infix_or :: (Bool,Bool) -> Property+prop_infix_or = makeProp (uncurryQ (Q.||)) (uncurry (||))++prop_infix_map_or :: (Bool, [Bool]) -> Property+prop_infix_map_or = makeProp (\x -> Q.map ((Q.fst x) Q.||) $ Q.snd x) (\(x,xs) -> map (x ||) xs)++prop_not :: Bool -> Property+prop_not = makeProp Q.not not++prop_map_not :: [Bool] -> Property+prop_map_not = makeProp (Q.map Q.not) (map not)++prop_eq :: (Integer,Integer) -> Property+prop_eq = makeProp (uncurryQ (Q.==)) (uncurry (==))++prop_map_eq :: (Integer, [Integer]) -> Property+prop_map_eq = makeProp (\x -> Q.map ((Q.fst x) Q.==) $ Q.snd x) (\(x,xs) -> map (x ==) xs)++prop_neq :: (Integer,Integer) -> Property+prop_neq = makeProp (uncurryQ (Q./=)) (uncurry (/=))++prop_map_neq :: (Integer, [Integer]) -> Property+prop_map_neq = makeProp (\x -> Q.map ((Q.fst x) Q./=) $ Q.snd x) (\(x,xs) -> map (x /=) xs)++prop_cond :: Bool -> Property+prop_cond = makeProp (\b -> Q.cond b 0 1) (\b -> if b then (0 :: Integer) else 1)++prop_cond_tuples :: (Bool, (Integer, Integer)) -> Property+prop_cond_tuples = makeProp (\b -> Q.cond (Q.fst b) +                                          (Q.pair (Q.fst $ Q.snd b) (Q.fst $ Q.snd b)) +                                          (Q.pair (Q.snd $ Q.snd b) (Q.snd $ Q.snd b))) +                            (\b -> if fst b +                                   then (fst $ snd b, fst $ snd b) +                                   else (snd $ snd b, snd $ snd b))++prop_cond_list_tuples :: (Bool, ([[Integer]], [[Integer]])) -> Property+prop_cond_list_tuples = makeProp (\b -> Q.cond (Q.fst b) +                                               (Q.pair (Q.fst $ Q.snd b) (Q.fst $ Q.snd b)) +                                               (Q.pair (Q.snd $ Q.snd b) (Q.snd $ Q.snd b))) +                                 (\b -> if fst b +                                        then (fst $ snd b, fst $ snd b) +                                        else (snd $ snd b, snd $ snd b))++prop_map_cond :: [Bool] -> Property+prop_map_cond = makeProp (Q.map (\b -> Q.cond b (0 :: Q.Q Integer) 1)) +                         (map (\b -> if b then 0 else 1))++prop_map_cond_tuples :: [Bool] -> Property+prop_map_cond_tuples = makeProp (Q.map (\b -> Q.cond b +                                                     (Q.toQ (0, 10) :: Q.Q (Integer, Integer)) +                                                     (Q.toQ (1, 11)))) +                                (map (\b -> if b +                                            then (0, 10) +                                            else (1, 11)))++prop_concatmapcond :: [Integer] -> Property+prop_concatmapcond l1 =+        -- FIXME remove precondition as soon as X100 is fixed+    (not $ null l1)+    ==>+    makeProp q n l1+        where q l = Q.concatMap (\x -> Q.cond ((Q.>) x (Q.toQ 0)) (x Q.<| el) el) l+              n l = concatMap (\x -> if x > 0 then [x] else []) l+              el = Q.toQ []++prop_lt :: (Integer, Integer) -> Property+prop_lt = makeProp (uncurryQ (Q.<)) (uncurry (<))++prop_map_lt :: (Integer, [Integer]) -> Property+prop_map_lt = makeProp (\x -> Q.map ((Q.fst x) Q.<) $ Q.snd x) (\(x,xs) -> map (x <) xs)++prop_lte :: (Integer, Integer) -> Property+prop_lte = makeProp (uncurryQ (Q.<=)) (uncurry (<=))++prop_map_lte :: (Integer, [Integer]) -> Property+prop_map_lte = makeProp (\x -> Q.map ((Q.fst x) Q.<=) $ Q.snd x) (\(x,xs) -> map (x <=) xs)++prop_gt :: (Integer, Integer) -> Property+prop_gt = makeProp (uncurryQ (Q.>)) (uncurry (>))++prop_map_gt :: (Integer, [Integer]) -> Property+prop_map_gt = makeProp (\x -> Q.map ((Q.fst x) Q.>) $ Q.snd x) (\(x,xs) -> map (x >) xs)++prop_gte :: (Integer, Integer) -> Property+prop_gte = makeProp (uncurryQ (Q.>=)) (uncurry (>=))++prop_map_gte :: (Integer, [Integer]) -> Property+prop_map_gte = makeProp (\x -> Q.map ((Q.fst x) Q.>=) $ Q.snd x) (\(x,xs) -> map (x >=) xs)++prop_min_integer :: (Integer,Integer) -> Property+prop_min_integer = makeProp (uncurryQ Q.min) (uncurry min)++prop_max_integer :: (Integer,Integer) -> Property+prop_max_integer = makeProp (uncurryQ Q.max) (uncurry max)++prop_min_double :: (Double,Double) -> Property+prop_min_double = makePropDouble (uncurryQ Q.min) (uncurry min)++prop_max_double :: (Double,Double) -> Property+prop_max_double = makePropDouble (uncurryQ Q.max) (uncurry max)++-- * Maybe++prop_maybe :: (Integer, Maybe Integer) -> Property+prop_maybe =  makeProp (\a -> Q.maybe (Q.fst a) id (Q.snd a)) (\(i,mi) -> maybe i id mi)++prop_just :: Integer -> Property+prop_just = makeProp Q.just Just++prop_isJust :: Maybe Integer -> Property+prop_isJust = makeProp Q.isJust isJust++prop_isNothing :: Maybe Integer -> Property+prop_isNothing = makeProp Q.isNothing isNothing++prop_fromJust :: Maybe Integer -> Property+prop_fromJust mi = isJust mi ==> makeProp Q.fromJust fromJust mi++prop_fromMaybe :: (Integer,Maybe Integer) -> Property+prop_fromMaybe = makeProp (uncurryQ Q.fromMaybe) (uncurry fromMaybe)++prop_listToMaybe :: [Integer] -> Property+prop_listToMaybe = makeProp Q.listToMaybe listToMaybe++prop_maybeToList :: Maybe Integer -> Property+prop_maybeToList = makeProp Q.maybeToList maybeToList++prop_catMaybes :: [Maybe Integer] -> Property+prop_catMaybes = makeProp Q.catMaybes catMaybes++prop_mapMaybe :: [Maybe Integer] -> Property+prop_mapMaybe = makeProp (Q.mapMaybe id) (mapMaybe id)++-- * Either++prop_left :: Integer -> Property+prop_left = makeProp (Q.left :: Q.Q Integer -> Q.Q (Either Integer Integer)) Left++prop_right :: Integer -> Property+prop_right = makeProp (Q.right :: Q.Q Integer -> Q.Q (Either Integer Integer)) Right++prop_isLeft :: Either Integer Integer -> Property+prop_isLeft = makeProp Q.isLeft (\e -> case e of {Left _ -> True; Right _ -> False;})++prop_isRight :: Either Integer Integer -> Property+prop_isRight = makeProp Q.isRight (\e -> case e of {Left _ -> False; Right _ -> True;})++prop_either :: Either Integer Integer -> Property+prop_either =  makeProp (Q.either id id) (either id id)++prop_lefts :: [Either Integer Integer] -> Property+prop_lefts =  makeProp Q.lefts lefts++prop_rights :: [Either Integer Integer] -> Property+prop_rights =  makeProp Q.rights rights++prop_partitionEithers :: [Either Integer Integer] -> Property+prop_partitionEithers =  makeProp Q.partitionEithers partitionEithers++-- * Lists++prop_cons :: (Integer, [Integer]) -> Property+prop_cons = makeProp (uncurryQ (Q.<|)) (uncurry (:))++prop_map_cons :: (Integer, [[Integer]]) -> Property+prop_map_cons = makeProp (\x -> Q.map ((Q.fst x) Q.<|) $ Q.snd x) +                         (\(x,xs) -> map (x:) xs)++prop_snoc :: ([Integer], Integer) -> Property+prop_snoc = makeProp (uncurryQ (Q.|>)) (\(a,b) -> a ++ [b])++prop_map_snoc :: ([Integer], [Integer]) -> Property+prop_map_snoc = makeProp (\z -> Q.map ((Q.fst z) Q.|>) (Q.snd z)) (\(a,b) -> map (\z -> a ++ [z]) b)++prop_singleton :: Integer -> Property+prop_singleton = makeProp Q.singleton (: [])++prop_head  :: [Integer] -> Property+prop_head  = makePropNotNull Q.head head++prop_tail  :: [Integer] -> Property+prop_tail  = makePropNotNull Q.tail tail++prop_last  :: [Integer] -> Property+prop_last  = makePropNotNull Q.last last++prop_map_last :: [[Integer]] -> Property+prop_map_last ps = and (map ((>0) . length) ps) ==> makeProp (Q.map Q.last) (map last) ps++prop_init  :: [Integer] -> Property+prop_init  = makePropNotNull Q.init init++prop_map_init  :: [[Integer]] -> Property+prop_map_init  ps = and (map ((>0) . length) ps)+    ==>+     makeProp (Q.map Q.init) (map init) ps++prop_the   :: (Int, Integer) -> Property+prop_the (n, i) =+  n > 0+  ==>+  let l = replicate n i in makeProp Q.head the l++prop_map_the :: [(Int, Integer)] -> Property+prop_map_the ps =+  let ps' = filter ((>0) . fst) ps in+  (length ps') > 0+  ==>+  let xss = map (\(n, i) -> replicate n i) ps' in+  makeProp (Q.map Q.head) (map the) xss++prop_map_tail :: [[Integer]] -> Property+prop_map_tail ps =+    and [length p > 0 | p <- ps]+    ==>+    makeProp (Q.map Q.tail) (map tail) ps++prop_index :: ([Integer], Integer)  -> Property+prop_index (l, i) =+        i > 0 && i < fromIntegral (length l)+    ==> makeProp (uncurryQ (Q.!!))+                 (\(a,b) -> a !! fromIntegral b)+                 (l, i)++prop_index_pair :: ([(Integer, [Integer])], Integer) -> Property+prop_index_pair (l, i) =+        i > 0 && i < fromIntegral (length l)               +    ==> makeProp (uncurryQ (Q.!!))+                 (\(a,b) -> a !! fromIntegral b)+                 (l, i)++prop_index_nest :: ([[Integer]], Integer)  -> Property+prop_index_nest (l, i) =+     i > 0 && i < fromIntegral (length l)+ ==> makeProp (uncurryQ (Q.!!))+              (\(a,b) -> a !! fromIntegral b)+              (l, i)++prop_map_index :: ([Integer], [Integer])  -> Property+prop_map_index (l, is) =+     and [i >= 0 && i < 2 * fromIntegral (length l) | i <-  is]+ ==> makeProp (\z -> Q.map (((Q.fst z) Q.++ (Q.fst z) Q.++ (Q.fst z)) Q.!!) (Q.snd z))+              (\(a,b) -> map ((a ++ a ++ a) !!) (map fromIntegral b))+              (l, is)++prop_map_index_nest :: ([[Integer]], [Integer])  -> Property+prop_map_index_nest (l, is) =+     and [i >= 0 && i < 3 * fromIntegral (length l) | i <-  is]+ ==> makeProp (\z -> Q.map (((Q.fst z) Q.++ (Q.fst z) Q.++ (Q.fst z)) Q.!!) (Q.snd z))+            (\(a,b) -> map ((a ++ a ++ a) !!) (map fromIntegral b))+            (l, is)++prop_take :: (Integer, [Integer]) -> Property+prop_take = makeProp (uncurryQ Q.take) (\(n,l) -> take (fromIntegral n) l)++prop_map_take :: (Integer, [[Integer]]) -> Property+prop_map_take = makeProp (\z -> Q.map (Q.take $ Q.fst z) $ Q.snd z) (\(n,l) -> map (take (fromIntegral n)) l)++prop_drop :: (Integer, [Integer]) -> Property+prop_drop = makeProp (uncurryQ Q.drop) (\(n,l) -> drop (fromIntegral n) l)++prop_map_drop :: (Integer, [[Integer]]) -> Property+prop_map_drop = makeProp (\z -> Q.map (Q.drop $ Q.fst z) $ Q.snd z) (\(n,l) -> map (drop (fromIntegral n)) l)++prop_takedrop :: (Integer, [Integer]) -> Property+prop_takedrop = makeProp takedrop_q takedrop+  where takedrop_q = \p -> Q.append ((Q.take (Q.fst p)) (Q.snd p)) ((Q.drop (Q.fst p)) (Q.snd p))+        takedrop (n, l) = (take (fromIntegral n) l) ++ (drop (fromIntegral n) l)++prop_map :: [Integer] -> Property+prop_map = makeProp (Q.map id) (map id)++prop_map_map_mul :: [[Integer]] -> Property+prop_map_map_mul = makeProp (Q.map (Q.map (*2))) (map (map (*2)))++prop_map_map_add :: ([Integer], [Integer]) -> Property+prop_map_map_add = makeProp (\z -> Q.map (\x -> (Q.map (\y -> x + y) $ Q.snd z)) $ Q.fst z) (\(l,r) -> map (\x -> map (\y -> x + y) r) l)++prop_map_map_map_mul :: [[[Integer]]] -> Property+prop_map_map_map_mul = makeProp (Q.map (Q.map (Q.map (*2)))) (map (map (map (*2))))++prop_append :: ([Integer], [Integer]) -> Property+prop_append = makeProp (uncurryQ (Q.++)) (uncurry (++))++prop_append_nest :: ([[Integer]], [[Integer]]) -> Property+prop_append_nest = makeProp (uncurryQ (Q.append)) (\(a,b) -> a ++ b)++prop_map_append :: ([Integer], [[Integer]]) -> Property+prop_map_append = makeProp (\z -> Q.map (Q.fst z Q.++) (Q.snd z)) (\(a,b) -> map (a ++) b)++prop_filter :: [Integer] -> Property+prop_filter = makeProp (Q.filter (const $ Q.toQ True)) (filter $ const True)++prop_filter_gt :: [Integer] -> Property+prop_filter_gt = makeProp (Q.filter (Q.> 42)) (filter (> 42))++prop_filter_gt_nested :: [(Integer, [Integer])] -> Property+prop_filter_gt_nested = makeProp (Q.filter ((Q.> 42) . Q.fst)) (filter ((> 42) . fst))++prop_map_filter :: [[Integer]] -> Property+prop_map_filter = makeProp (Q.map (Q.filter (const $ Q.toQ True))) (map (filter $ const True))++prop_map_filter_gt :: [[Integer]] -> Property+prop_map_filter_gt = makeProp (Q.map (Q.filter (Q.> 42))) (map (filter (> 42)))++prop_map_filter_gt_nested :: [[(Integer, [Integer])]] -> Property+prop_map_filter_gt_nested = makeProp (Q.map (Q.filter ((Q.> 42) . Q.fst))) (map (filter ((> 42) . fst)))++prop_groupWith :: [Integer] -> Property+prop_groupWith = makeProp (Q.groupWith id) (groupWith id)++groupWithKey :: Ord b => (a -> b) -> [a] -> [(b, [a])]+groupWithKey p as = map (\g -> (the $ map p g, g)) $ groupWith p as++prop_groupWithKey :: [Integer] -> Property+prop_groupWithKey = makeProp (Q.groupWithKey id) (groupWithKey id)++prop_map_groupWith :: [[Integer]] -> Property+prop_map_groupWith = makeProp (Q.map (Q.groupWith id)) (map (groupWith id))++prop_map_groupWithKey :: [[Integer]] -> Property+prop_map_groupWithKey = makeProp (Q.map (Q.groupWithKey id)) (map (groupWithKey id))++prop_groupWith_length :: [[Integer]] -> Property+prop_groupWith_length = makeProp (Q.groupWith Q.length) (groupWith length)++prop_groupWithKey_length :: [[Integer]] -> Property+prop_groupWithKey_length = makeProp (Q.groupWithKey Q.length) (groupWithKey (fromIntegral . length))++prop_sortWith  :: [Integer] -> Property+prop_sortWith = makeProp (Q.sortWith id) (sortWith id)++prop_sortWith_pair :: [(Integer, Integer)] -> Property+prop_sortWith_pair = makeProp (Q.sortWith Q.fst) (sortWith fst)++prop_sortWith_nest  :: [(Integer, [Integer])] -> Property+prop_sortWith_nest = makeProp (Q.sortWith Q.fst) (sortWith fst)++prop_map_sortWith :: [[Integer]] -> Property+prop_map_sortWith = makeProp (Q.map (Q.sortWith id)) (map (sortWith id))++prop_map_sortWith_pair :: [[(Integer, Integer)]] -> Property+prop_map_sortWith_pair = makeProp (Q.map (Q.sortWith Q.fst)) (map (sortWith fst))++prop_map_sortWith_nest :: [[(Integer, [Integer])]] -> Property+prop_map_sortWith_nest = makeProp (Q.map (Q.sortWith Q.fst)) (map (sortWith fst))++prop_map_sortWith_length :: [[[Integer]]] -> Property+prop_map_sortWith_length = makeProp (Q.map (Q.sortWith Q.length)) (map (sortWith length))++prop_map_groupWith_length :: [[[Integer]]] -> Property+prop_map_groupWith_length = makeProp (Q.map (Q.groupWith Q.length)) (map (groupWith length))++prop_map_groupWithKey_length :: [[[Integer]]] -> Property+prop_map_groupWithKey_length = makeProp (Q.map (Q.groupWithKey Q.length)) (map (groupWithKey (fromIntegral . length)))++prop_sortWith_length_nest  :: [[[Integer]]] -> Property+prop_sortWith_length_nest = makeProp (Q.sortWith Q.length) (sortWith length)++prop_groupWith_length_nest :: [[[Integer]]] -> Property+prop_groupWith_length_nest = makeProp (Q.groupWith Q.length) (groupWith length)++prop_groupWithKey_length_nest :: [[[Integer]]] -> Property+prop_groupWithKey_length_nest = makeProp (Q.groupWithKey Q.length) (groupWithKey (fromIntegral . length))++prop_null :: [Integer] -> Property+prop_null = makeProp Q.null null++prop_map_null :: [[Integer]] -> Property+prop_map_null = makeProp (Q.map Q.null) (map null)++prop_length :: [Integer] -> Property+prop_length = makeProp Q.length ((fromIntegral :: Int -> Integer) . length)++prop_length_tuple :: [(Integer, Integer)] -> Property+prop_length_tuple = makeProp Q.length (fromIntegral . length)++prop_map_length :: [[Integer]] -> Property+prop_map_length = makeProp (Q.map Q.length) (map (fromIntegral . length))++prop_map_minimum :: [[Integer]] -> Property+prop_map_minimum ps = and (map (\p -> length p > 0) ps)+        ==>+    makeProp (Q.map Q.minimum) (map (fromIntegral . minimum)) ps++prop_map_maximum :: [[Integer]] -> Property+prop_map_maximum ps = and (map (\p -> length p > 0) ps)+        ==>+    makeProp (Q.map Q.maximum) (map (fromIntegral . maximum)) ps++prop_map_map_minimum :: [[[Integer]]] -> Property+prop_map_map_minimum ps = and (map (and . map (\p -> length p > 0)) ps)+        ==>+    makeProp (Q.map (Q.map Q.minimum)) (map (map(fromIntegral . minimum))) ps++prop_map_map_maximum :: [[[Integer]]] -> Property+prop_map_map_maximum ps = and (map (and . map (\p -> length p > 0)) ps)+        ==>+    makeProp (Q.map (Q.map Q.maximum)) (map (map(fromIntegral . maximum))) ps+++prop_map_length_tuple :: [[(Integer, Integer)]] -> Property+prop_map_length_tuple = makeProp (Q.map Q.length) (map (fromIntegral . length))++prop_reverse :: [Integer] -> Property+prop_reverse = makeProp Q.reverse reverse++prop_reverse_nest :: [[Integer]] -> Property+prop_reverse_nest = makeProp Q.reverse reverse++prop_map_reverse :: [[Integer]] -> Property+prop_map_reverse = makeProp (Q.map Q.reverse) (map reverse)++prop_map_reverse_nest :: [[[Integer]]] -> Property+prop_map_reverse_nest = makeProp (Q.map Q.reverse) (map reverse)++prop_and :: [Bool] -> Property+prop_and = makeProp Q.and and++prop_map_and :: [[Bool]] -> Property+prop_map_and = makeProp (Q.map Q.and) (map and)++prop_map_map_and :: [[[Bool]]] -> Property+prop_map_map_and = makeProp (Q.map (Q.map Q.and)) (map (map and))++prop_or :: [Bool] -> Property+prop_or = makeProp Q.or or++prop_map_or :: [[Bool]] -> Property+prop_map_or = makeProp (Q.map Q.or) (map or)++prop_map_map_or :: [[[Bool]]] -> Property+prop_map_map_or = makeProp (Q.map (Q.map Q.or)) (map (map or))++prop_any_zero :: [Integer] -> Property+prop_any_zero = makeProp (Q.any (Q.== 0)) (any (== 0))++prop_map_any_zero :: [[Integer]] -> Property+prop_map_any_zero = makeProp (Q.map (Q.any (Q.== 0))) (map (any (== 0)))++prop_all_zero :: [Integer] -> Property+prop_all_zero = makeProp (Q.all (Q.== 0)) (all (== 0))++prop_map_all_zero :: [[Integer]] -> Property+prop_map_all_zero = makeProp (Q.map (Q.all (Q.== 0))) (map (all (== 0)))++prop_sum_integer :: [Integer] -> Property+prop_sum_integer = makeProp Q.sum sum+                 +avgInt :: [Integer] -> Double+avgInt is = (realToFrac $ sum is) / (fromIntegral $ length is)++prop_avg_integer :: [Integer] -> Property+prop_avg_integer is = (not $ null is) ==> makeProp Q.avg avgInt is++prop_map_sum :: [[Integer]] -> Property+prop_map_sum = makeProp (Q.map Q.sum) (map sum)++prop_map_avg :: [[Integer]] -> Property+prop_map_avg is = (not $ any null is) ==> makeProp (Q.map Q.avg) (map avgInt) is++prop_map_map_sum :: [[[Integer]]] -> Property+prop_map_map_sum = makeProp (Q.map (Q.map Q.sum)) (map (map sum))++prop_map_map_avg :: [[[Integer]]] -> Property+prop_map_map_avg is = (not $ any (any null) is) ==> makeProp (Q.map (Q.map Q.avg)) (map (map avgInt))++prop_sum_double :: [Double] -> Property+prop_sum_double = makePropDouble Q.sum sum++avgDouble :: [Double] -> Double+avgDouble ds = sum ds / (fromIntegral $ length ds)++prop_avg_double :: [Double] -> Property+prop_avg_double ds = (not $ null ds) ==> makePropDouble Q.avg avgDouble ds++prop_concat :: [[Integer]] -> Property+prop_concat = makeProp Q.concat concat++prop_map_concat :: [[[Integer]]] -> Property+prop_map_concat = makeProp (Q.map Q.concat) (map concat)++prop_concatMap :: [Integer] -> Property+prop_concatMap = makeProp (Q.concatMap Q.singleton) (concatMap (: []))++prop_maximum :: [Integer] -> Property+prop_maximum = makePropNotNull Q.maximum maximum++prop_minimum :: [Integer] -> Property+prop_minimum = makePropNotNull Q.minimum minimum++prop_splitAt :: (Integer, [Integer]) -> Property+prop_splitAt = makeProp (uncurryQ Q.splitAt) (\(a,b) -> splitAt (fromIntegral a) b)++prop_takeWhile :: (Integer, [Integer]) -> Property+prop_takeWhile = makeProp (uncurryQ $ Q.takeWhile . (Q.==))+                          (uncurry  $   takeWhile . (==))++prop_dropWhile :: (Integer, [Integer]) -> Property+prop_dropWhile = makeProp (uncurryQ $ Q.dropWhile . (Q.==))+                          (uncurry  $   dropWhile . (==))++prop_map_takeWhile :: (Integer, [[Integer]]) -> Property+prop_map_takeWhile = makeProp (\z -> Q.map (Q.takeWhile (Q.fst z Q.==)) (Q.snd z))+                              (\z -> map (takeWhile (fst z ==)) (snd z))++prop_map_dropWhile :: (Integer, [[Integer]]) -> Property+prop_map_dropWhile = makeProp (\z -> Q.map (Q.dropWhile (Q.fst z Q.==)) (Q.snd z))+                              (\z -> map (dropWhile (fst z ==)) (snd z))++prop_span :: (Integer, [Integer]) -> Property+prop_span = makeProp (uncurryQ $ Q.span . (Q.==))+                     (uncurry   $   span . (==) . fromIntegral)++prop_map_span :: (Integer, [[Integer]]) -> Property+prop_map_span = makeProp (\z -> Q.map (Q.span ((Q.fst z) Q.==)) (Q.snd z))+                         (\z -> map (span (fst z ==)) (snd z))++prop_break :: (Integer, [Integer]) -> Property+prop_break = makeProp (uncurryQ $ Q.break . (Q.==))+                      (uncurry   $   break . (==) . fromIntegral)++prop_map_break :: (Integer, [[Integer]]) -> Property+prop_map_break = makeProp (\z -> Q.map (Q.break ((Q.fst z) Q.==)) (Q.snd z))+                          (\z -> map (break (fst z ==)) (snd z))++prop_elem :: (Integer, [Integer]) -> Property+prop_elem = makeProp (uncurryQ Q.elem)+                     (uncurry    elem)++prop_notElem :: (Integer, [Integer]) -> Property+prop_notElem = makeProp (uncurryQ Q.notElem)+                        (uncurry    notElem)++prop_lookup :: (Integer, [(Integer,Integer)]) -> Property+prop_lookup = makeProp (uncurryQ Q.lookup)+                       (uncurry    lookup)++prop_zip :: ([Integer], [Integer]) -> Property+prop_zip = makeProp (uncurryQ Q.zip) (uncurry zip)++prop_map_zip :: ([Integer], [[Integer]]) -> Property+prop_map_zip = makeProp (\z -> Q.map (Q.zip $ Q.fst z) $ Q.snd z) (\(x, y) -> map (zip x) y)++prop_zipWith :: ([Integer], [Integer]) -> Property+prop_zipWith = makeProp (uncurryQ $ Q.zipWith (+)) (uncurry $ zipWith (+))++prop_unzip :: [(Integer, Integer)] -> Property+prop_unzip = makeProp Q.unzip unzip++prop_map_unzip :: [[(Integer, Integer)]] -> Property+prop_map_unzip = makeProp (Q.map Q.unzip) (map unzip)++prop_zip3 :: ([Integer], [Integer],[Integer]) -> Property+prop_zip3 = makeProp (\q -> (case Q.view q of (as,bs,cs) -> Q.zip3 as bs cs))+                     (\(as,bs,cs) -> zip3 as bs cs)++prop_zipWith3 :: ([Integer], [Integer],[Integer]) -> Property+prop_zipWith3 = makeProp (\q -> (case Q.view q of (as,bs,cs) -> Q.zipWith3 (\a b c -> a + b + c) as bs cs))+                         (\(as,bs,cs) -> zipWith3 (\a b c -> a + b + c) as bs cs)++prop_unzip3 :: [(Integer, Integer, Integer)] -> Property+prop_unzip3 = makeProp Q.unzip3 unzip3++prop_nub :: [Integer] -> Property+prop_nub = makeProp Q.nub nub++prop_map_nub :: [[(Integer, Integer)]] -> Property+prop_map_nub = makeProp (Q.map Q.nub) (map nub)++-- * Tuples++prop_fst :: (Integer, Integer) -> Property+prop_fst = makeProp Q.fst fst++prop_fst_nested :: ([Integer], [Integer]) -> Property+prop_fst_nested = makeProp Q.fst fst++prop_map_fst :: [(Integer, Integer)] -> Property+prop_map_fst = makeProp (Q.map Q.fst) (map fst)++prop_snd :: (Integer, Integer) -> Property+prop_snd = makeProp Q.snd snd++prop_map_snd :: [(Integer, Integer)] -> Property+prop_map_snd = makeProp (Q.map Q.snd) (map snd)++prop_snd_nested :: ([Integer], [Integer]) -> Property+prop_snd_nested = makeProp Q.snd snd++prop_tup3_1 :: (Integer, Integer, Integer) -> Property+prop_tup3_1 = makeProp (\q -> case Q.view q of (a, _, _) -> a) (\(a, _, _) -> a)++prop_tup3_2 :: (Integer, Integer, Integer) -> Property+prop_tup3_2 = makeProp (\q -> case Q.view q of (_, b, _) -> b) (\(_, b, _) -> b)++prop_tup3_3 :: (Integer, Integer, Integer) -> Property+prop_tup3_3 = makeProp (\q -> case Q.view q of (_, _, c) -> c) (\(_, _, c) -> c)++prop_tup4_2 :: (Integer, Integer, Integer, Integer) -> Property+prop_tup4_2 = makeProp (\q -> case Q.view q of (_, b, _, _) -> b) (\(_, b, _, _) -> b)++prop_tup4_4 :: (Integer, Integer, Integer, Integer) -> Property+prop_tup4_4 = makeProp (\q -> case Q.view q of (_, _, _, d) -> d) (\(_, _, _, d) -> d)++prop_tup3_nested :: (Integer, [Integer], Integer) -> Property+prop_tup3_nested = makeProp (\q -> case Q.view q of (_, b, _) -> b) (\(_, b, _) -> b)++prop_tup4_tup3 :: (Integer, Integer, Integer, Integer) -> Property+prop_tup4_tup3 = makeProp (\q -> case Q.view q of (a, b, _, d) -> Q.tup3 a b d) +                          (\(a, b, _, d) -> (a, b, d))++-- * Numerics++prop_add_integer :: (Integer,Integer) -> Property+prop_add_integer = makeProp (uncurryQ (+)) (uncurry (+))++prop_add_double :: (Double,Double) -> Property+prop_add_double = makePropDouble (uncurryQ (+)) (uncurry (+))++prop_mul_integer :: (Integer,Integer) -> Property+prop_mul_integer = makeProp (uncurryQ (*)) (uncurry (*))++prop_mul_double :: (Double,Double) -> Property+prop_mul_double = makePropDouble (uncurryQ (*)) (uncurry (*))++prop_div_double :: (Double,Double) -> Property+prop_div_double (x,y) =+      y /= 0+  ==> makePropDouble (uncurryQ (/)) (uncurry (/)) (x,y)++prop_integer_to_double :: Integer -> Property+prop_integer_to_double = makePropDouble Q.integerToDouble fromInteger++prop_integer_to_double_arith :: (Integer, Double) -> Property+prop_integer_to_double_arith = makePropDouble (\x -> (Q.integerToDouble (Q.fst x)) + (Q.snd x))+                                              (\(i, d) -> fromInteger i + d)++prop_map_integer_to_double :: [Integer] -> Property+prop_map_integer_to_double = makePropListDouble (Q.map Q.integerToDouble) (map fromInteger)++prop_abs_integer :: Integer -> Property+prop_abs_integer = makeProp Q.abs abs++prop_abs_double :: Double -> Property+prop_abs_double = makePropDouble Q.abs abs++prop_signum_integer :: Integer -> Property+prop_signum_integer = makeProp Q.signum signum++prop_signum_double :: Double -> Property+prop_signum_double = makePropDouble Q.signum signum++prop_negate_integer :: Integer -> Property+prop_negate_integer = makeProp Q.negate negate++prop_negate_double :: Double -> Property+prop_negate_double = makePropDouble Q.negate negate++prop_trig_sin :: Double -> Property+prop_trig_sin = makePropDouble Q.sin sin++prop_trig_cos :: Double -> Property+prop_trig_cos = makePropDouble Q.cos cos++prop_trig_tan :: Double -> Property+prop_trig_tan = makePropDouble Q.tan tan++prop_exp :: Double -> Property+prop_exp = makePropDouble Q.exp exp++prop_log :: Double -> Property+prop_log d = d > 0 ==> makePropDouble Q.log log d++prop_sqrt :: Double -> Property+prop_sqrt d = d > 0 ==> makePropDouble Q.sqrt sqrt d++arc :: Double -> Bool+arc d = d >= -1 && d <= 1++prop_trig_asin :: Double -> Property+prop_trig_asin d = arc d ==>  makePropDouble Q.asin asin d++prop_trig_acos :: Double -> Property+prop_trig_acos d = arc d ==> makePropDouble Q.acos acos d++prop_trig_atan :: Double -> Property+prop_trig_atan = makePropDouble Q.atan atan++prop_number :: [Integer] -> Property+prop_number = makeProp (Q.map Q.snd . Q.number) (\xs -> map snd $ zip xs [1..])++prop_map_number :: [[Integer]] -> Property+prop_map_number = makeProp (Q.map (Q.map Q.snd . Q.number))+                            (map (\xs -> map snd $ zip xs [1..]))++prop_transpose :: [[Integer]] -> Property+prop_transpose = makeProp Q.transpose transpose++{-+prop_map_transpose :: [[[Integer]]] -> Property+prop_map_transpose xss = +    (all (not . null) (xss :: [[[Integer]]])+    &&+    and (map (all (not . null)) xss))+    ==> makeProp (Q.map Q.transpose) (map transpose)+-}++reshape :: Int -> [a] -> [[a]]+reshape _ [] = []+reshape i xs = take i xs : reshape i (drop i xs)++prop_reshape :: [Integer] -> Property+prop_reshape = makeProp (Q.reshape 5) (reshape 5)++prop_reshape2 :: [Integer] -> Property+prop_reshape2 = makeProp (Q.reshape 2) (reshape 2)+             +prop_map_reshape :: [[Integer]] -> Property+prop_map_reshape = makeProp (Q.map (Q.reshape 8)) (map (reshape 8))++prop_map_reshape2 :: [[Integer]] -> Property+prop_map_reshape2 = makeProp (Q.map (Q.reshape 2)) (map (reshape 2))++prop_map_trig_sin :: [Double] -> Property+prop_map_trig_sin = makePropListDouble (Q.map Q.sin) (map sin)++prop_map_trig_cos :: [Double] -> Property+prop_map_trig_cos = makePropListDouble (Q.map Q.cos) (map cos)++prop_map_trig_tan :: [Double] -> Property+prop_map_trig_tan = makePropListDouble (Q.map Q.tan) (map tan)++prop_map_trig_asin :: [Double] -> Property+prop_map_trig_asin ds = all arc ds ==> makePropListDouble (Q.map Q.asin) (map asin) ds++prop_map_trig_acos :: [Double] -> Property+prop_map_trig_acos ds = all arc ds ==> makePropListDouble (Q.map Q.acos) (map acos) ds++prop_map_trig_atan :: [Double] -> Property+prop_map_trig_atan = makePropListDouble (Q.map Q.atan) (map atan)++prop_map_exp :: [Double] -> Property+prop_map_exp = makePropListDouble (Q.map Q.exp) (map exp)++prop_map_log :: [Double] -> Property+prop_map_log ds = all (> 0) ds ==> makePropListDouble (Q.map Q.log) (map log) ds++prop_map_sqrt :: [Double] -> Property+prop_map_sqrt ds = all (> 0) ds ==> makePropListDouble (Q.map Q.sqrt) (map sqrt) ds+                   ++hnegative_sum :: Assertion+hnegative_sum = makeEqAssertion "hnegative_sum" (Q.sum (Q.toQ xs)) (sum xs)+  where+    xs :: [Integer]+    xs = [-1, -4, -5, 2]++hnegative_map_sum :: Assertion+hnegative_map_sum = makeEqAssertion "hnegative_map_sum" +                                    (Q.map Q.sum (Q.toQ xss)) +                                    (map sum xss)+  where+    xss :: [[Integer]]+    xss = [[10, 20, 30], [-10, -20, -30], [], [0]]++hmap_transpose :: Assertion+hmap_transpose = makeEqAssertion "hmap_transpose" (Q.map Q.transpose (Q.toQ xss)) res+  where+    xss :: [[[Integer]]]+    xss = [ [ [10, 20, 30]+            , [40, 50, 60]]+          , [ [100, 200]+            , [300, 400]+            , [500, 600]]+          ]++    res :: [[[Integer]]]+    res = [ [ [10, 40]+            , [20, 50]+            , [30, 60]+            ]+          , [ [100, 300, 500]+            , [200, 400, 600]+            ]+          ]
+ tests/ComprehensionTests.hs view
@@ -0,0 +1,526 @@+module ComprehensionTests where++import           Common+import qualified DSHComprehensions                    as C++import           Test.Framework                       (Test, testGroup)+import           Test.Framework.Providers.HUnit+import           Test.Framework.Providers.QuickCheck2 (testProperty)+import           Test.HUnit                           (Assertion)+import           Test.QuickCheck++tests_comprehensions :: Test+tests_comprehensions = testGroup "Comprehensions"+    [ testProperty "cartprod" prop_cartprod+    , testProperty "eqjoin" prop_eqjoin+    , testProperty "eqjoinproj" prop_eqjoinproj+    , testProperty "eqjoinpred" prop_eqjoinpred+    , testProperty "eqjointuples" prop_eqjointuples+    , testProperty "thetajoin_eq" prop_thetajoin_eq+    , testProperty "thetajoin_neq" prop_thetajoin_neq+    , testProperty "eqjoin3" prop_eqjoin3+    , testProperty "eqjoin_nested_left" prop_eqjoin_nested_left+    , testProperty "eqjoin_nested_right" prop_eqjoin_nested_right+    , testProperty "eqjoin_nested_both" prop_eqjoin_nested_both+    , testProperty "nestjoin" prop_nestjoin+    , testProperty "nestjoin3" prop_nestjoin3+    , testProperty "antijoin class12" prop_aj_class12+    , testProperty "antijoin class15" prop_aj_class15+    , testProperty "antijoin class16" prop_aj_class16+    , testProperty "backdep1" prop_backdep+    , testProperty "backdep_filter" prop_backdep_filter+    , testProperty "backdep2" prop_backdep2+    , testProperty "backdep3" prop_backdep3+    , testProperty "backdep4" prop_backdep4+    , testProperty "backdep5" prop_backdep5+    , testProperty "deep" prop_deep_iter+    ]++tests_join_hunit :: Test+tests_join_hunit = testGroup "HUnit joins"+    [ testCase "heqjoin_nested1" heqjoin_nested1+    , testCase "hsemijoin" hsemijoin+    , testCase "hsemijoin_range" hsemijoin_range+    , testCase "hsemijoin_quant" hsemijoin_quant+    , testCase "hsemijoin_not_null" hsemijoin_not_null+    , testCase "hantijoin" hantijoin+    , testCase "hantijoin_range" hantijoin_range+    , testCase "hantijoin_null" hantijoin_null+    , testCase "hantijoin_class12" hantijoin_class12+    , testCase "hantijoin_class15" hantijoin_class15+    , testCase "hantijoin_class16" hantijoin_class16+    , testCase "hfrontguard" hfrontguard+    ]++tests_nest_head_hunit :: Test+tests_nest_head_hunit = testGroup "HUnit head nesting"+    [ testCase "hnj1" hnj1+    , testCase "hnj2" hnj2+    , testCase "hnj3" hnj3+    , testCase "hnj4" hnj4+    , testCase "hnj5" hnj5+    , testCase "hnj6" hnj6+    , testCase "hnj7" hnj7+    , testCase "hnj8" hnj8+    , testCase "hnj9" hnj9+    , testCase "hnj10" hnj10+    , testCase "hnj11" hnj11+    , testCase "hnj12" hnj12+    , testCase "hnp1" hnp1+    , testCase "hnp2" hnp2+    , testCase "hnp3" hnp3+    , testCase "hnp4" hnp4+    ]++tests_nest_guard_hunit :: Test+tests_nest_guard_hunit = testGroup "HUnit guard nesting"+    [ testCase "hnjg1" hnjg1+    , testCase "hnjg2" hnjg2+    , testCase "hnjg3" hnjg3+    , testCase "hnjg4" hnjg4+    , testCase "hnjg5" hnjg5+    ]++---------------------------------------------------------------------------------+-- QuickCheck properties for comprehensions++prop_cartprod :: ([Integer], [Integer]) -> Property+prop_cartprod = makeProp C.cartprod cartprod_native+  where+    cartprod_native (xs, ys) = [ (x, y) | x <- xs, y <- ys]++prop_eqjoin :: ([Integer], [Integer]) -> Property+prop_eqjoin = makeProp C.eqjoin eqjoin_native+  where+    eqjoin_native (xs, ys) = [ (x, y) | x <- xs , y <- ys , x == y ]++prop_eqjoinproj :: ([Integer], [Integer]) -> Property+prop_eqjoinproj = makeProp C.eqjoinproj eqjoinproj_native+  where+    eqjoinproj_native (xs, ys) = [ (x, y) | x <- xs , y <- ys , (2 * x) == y ]++prop_eqjoinpred :: (Integer, [Integer], [Integer]) -> Property+prop_eqjoinpred = makeProp C.eqjoinpred eqjoinpred_native+  where+    eqjoinpred_native (x', xs, ys) = [ (x, y) | x <- xs , y <- ys , x == y , x > x']++prop_eqjointuples :: ([(Integer, Integer)], [(Integer, Integer)]) -> Property+prop_eqjointuples = makeProp C.eqjointuples eqjointuples_native+  where+    eqjointuples_native (xs, ys) = [ (x1 * x2, y1, y2)+                                   | (x1, x2) <- xs+                                   , (y1, y2) <- ys+                                   , x1 == y2+                                   ]++prop_thetajoin_eq :: ([(Integer, Integer)], [(Integer, Integer)]) -> Property+prop_thetajoin_eq = makeProp C.thetajoin_eq thetajoin_eq_native+  where+    thetajoin_eq_native (xs, ys) = [ (x1 * x2, y1, y2)+                                   | (x1, x2) <- xs+                                   , (y1, y2) <- ys+                                   , x1 == y2+                                   , y1 == x2+                                   ]++prop_thetajoin_neq :: ([(Integer, Integer)], [(Integer, Integer)]) -> Property+prop_thetajoin_neq = makeProp C.thetajoin_neq thetajoin_neq_native+  where+    thetajoin_neq_native (xs, ys) = [ (x1 * x2, y1, y2)+                                    | (x1, x2) <- xs+                                    , (y1, y2) <- ys+                                    , x1 == y2+                                    , y1 /= x2+                                    ]+++prop_eqjoin3 :: ([Integer], [Integer], [Integer]) -> Property+prop_eqjoin3 = makeProp C.eqjoin3 eqjoin3_native+  where+    eqjoin3_native (xs, ys, zs) = [ (x, y, z) | x <- xs , y <- ys , z <- zs , x == y , y == z]++prop_eqjoin_nested_left :: ([(Integer, [Integer])], [Integer]) -> Property+prop_eqjoin_nested_left = makeProp C.eqjoin_nested_left eqjoin_nested_left_native+  where+    eqjoin_nested_left_native (xs, ys) = [ (x, y) | x <- xs , y <- ys , fst x == y]++prop_eqjoin_nested_right :: ([Integer], [(Integer, [Integer])]) -> Property+prop_eqjoin_nested_right = makeProp C.eqjoin_nested_right eqjoin_nested_right_native+  where+    eqjoin_nested_right_native (xs, ys) = [ (x, y) | x <- xs , y <- ys , x == fst y]++prop_eqjoin_nested_both :: ([(Integer, [Integer])], [(Integer, [Integer])]) -> Property+prop_eqjoin_nested_both = makeProp C.eqjoin_nested_both eqjoin_nested_both_native+  where+    eqjoin_nested_both_native (xs, ys) = [ (x, y) | x <- xs , y <- ys , fst x == fst y]++prop_nestjoin :: ([Integer], [Integer]) -> Property+prop_nestjoin = makeProp C.nestjoin nestjoin_native+  where+    nestjoin_native (xs, ys) = [ (x, [ y | y <- ys, x == y ]) | x <- xs]++prop_nestjoin3 :: ([Integer], [Integer], [Integer]) -> Property+prop_nestjoin3 = makeProp C.nestjoin3 nestjoin3_native+  where+    nestjoin3_native (njxs, njys, njzs) = +        [ [ [ (x,y,z) | z <- njzs, y == z ]+          | y <- njys+          , x == y+          ]+        | x <- njxs+        ]++prop_aj_class12 :: ([Integer], [Integer]) -> Property+prop_aj_class12 = makeProp C.aj_class12 aj_class12_native+  where+    aj_class12_native (ajxs, ajys) = [ x +                                     | x <- ajxs+                                     , and [ x == y | y <- ajys, y > 10 ]+                                     ]++prop_aj_class15 :: ([Integer], [Integer]) -> Property+prop_aj_class15 = makeProp C.aj_class15 aj_class15_native+  where+    aj_class15_native (ajxs, ajys) = [ x +                                     | x <- ajxs+                                     , and [ y `mod` 4 == 0 | y <- ajys, x < y ]+                                     ]++prop_aj_class16 :: ([Integer], [Integer]) -> Property+prop_aj_class16 = makeProp C.aj_class16 aj_class16_native+  where+    aj_class16_native (ajxs, ajys) = [ x +                                     | x <- ajxs+                                     , and [ y <= 2 * x | y <- ajys, x < y ]+                                     ]++prop_backdep :: [[Integer]] -> Property+prop_backdep = makeProp C.backdep backdep_native+  where+    backdep_native xss = [x | xs <- xss, x <- xs]++prop_backdep_filter :: [[Integer]] -> Property+prop_backdep_filter = makeProp C.backdep_filter backdep_filter_native+  where+    backdep_filter_native xss = [x | xs <- xss, x <- xs, fromIntegral (length xs) > x]++prop_backdep2 :: [[Integer]] -> Property+prop_backdep2 = makeProp C.backdep2 backdep2+  where+    backdep2 xss = [ [ x * 42 | x <- xs ] | xs <- xss ]++prop_backdep3 :: [[Integer]] -> Property+prop_backdep3 = makeProp C.backdep3 backdep3+  where+    backdep3 xss = [ [ x + fromIntegral (length xs) | x <- xs ] | xs <- xss ]++prop_backdep4 :: [[[Integer]]] -> Property+prop_backdep4 = makeProp C.backdep4 backdep4+  where+    backdep4 xsss = [ [ [ x + fromIntegral (length xs) + fromIntegral (length xss)+                        | x <- xs+                        ]+                      | xs <- xss+                      ]+                    | xss <- xsss+                    ]++prop_backdep5 :: [[Integer]] -> Property+prop_backdep5 = makeProp C.backdep5 backdep5+  where+    backdep5 xss = [ [ x + fromIntegral (length xs) +                     | x <- take (length xs - 3) xs ] +                   | xs <- xss ]++++-----------------------------------------------------------------------+-- HUnit tests for comprehensions++heqjoin_nested1 :: Assertion+heqjoin_nested1 = makeEqAssertion "heqjoin_nested" C.eqjoin_nested1 res+  where+    res = [ ((20, ['b']), 20)+          , ((30, ['c', 'd']), 30)+          , ((30, ['c', 'd']), 30)+          , ((40, []), 40)+          ]++hsemijoin :: Assertion+hsemijoin = makeEqAssertion "hsemijoin" C.semijoin res+  where+    res = [2, 4, 6, 7]++hsemijoin_range :: Assertion+hsemijoin_range = makeEqAssertion "hsemijoin_range" C.semijoin_range res+  where+    res = [2, 4]++hsemijoin_not_null :: Assertion+hsemijoin_not_null = makeEqAssertion "hsemijoin_range" C.semijoin_not_null res+  where+    res = [2, 4, 6, 7]++hsemijoin_quant :: Assertion+hsemijoin_quant = makeEqAssertion "hsemijoin_quant" C.semijoin_quant res+  where+    res = [6,7]++hantijoin :: Assertion+hantijoin = makeEqAssertion "hantijoin" C.antijoin res+  where+    res = [1, 3, 5]++hantijoin_range :: Assertion+hantijoin_range = makeEqAssertion "hantijoin_range" C.antijoin_range res+  where+    res = [1, 3, 5, 6, 7]++hantijoin_null :: Assertion+hantijoin_null = makeEqAssertion "hantijoin_range" C.antijoin_null res+  where+    res = [1, 3, 5]++hantijoin_class12 :: Assertion+hantijoin_class12 = makeEqAssertion "hantijoin_class12" C.antijoin_class12 res+  where+    res = [6,7,8,9,10]++hantijoin_class15 :: Assertion+hantijoin_class15 = makeEqAssertion "hantijoin_class15" C.antijoin_class15 res+  where+    res = [5,6,7,8]++hantijoin_class16 :: Assertion+hantijoin_class16 = makeEqAssertion "hantijoin_class16" C.antijoin_class16 res+  where+    res = [4,5,6]++hfrontguard :: Assertion+hfrontguard = makeEqAssertion "hfrontguard" C.frontguard res+  where+    res = [[],[1,2],[1,2]] ++-----------------------------------------------------------------------+-- HUnit tests for nestjoin/nestproduct++njxs1 :: [Integer]+njxs1 = [1,2,3,4,5,6]++njys1 :: [Integer]+njys1 = [3,4,5,6,3,6,4,1,1,1]++hnj1 :: Assertion+hnj1 = makeEqAssertion "hnj1" (C.nj1 njxs1 njys1) (nj1 njxs1 njys1)++hnj2 :: Assertion+hnj2 = makeEqAssertion "hnj2" (C.nj2 njxs1 njys1) (nj2 njxs1 njys1)++hnj3 :: Assertion+hnj3 = makeEqAssertion "hnj3" (C.nj3 njxs1 njys1) (nj3 njxs1 njys1)++hnj4 :: Assertion+hnj4 = makeEqAssertion "hnj4" (C.nj4 njxs1 njys1) (nj4 njxs1 njys1)++hnj5 :: Assertion+hnj5 = makeEqAssertion "hnj5" (C.nj5 njxs1 njys1) (nj5 njxs1 njys1)++hnj6 :: Assertion+hnj6 = makeEqAssertion "hnj6" (C.nj6 njxs1 njys1) (nj6 njxs1 njys1)++hnj7 :: Assertion+hnj7 = makeEqAssertion "hnj7" (C.nj7 njxs1 njys1) (nj7 njxs1 njys1)++hnj8 :: Assertion+hnj8 = makeEqAssertion "hnj8" (C.nj8 njxs1 njys1) (nj8 njxs1 njys1)++hnj9 :: Assertion+hnj9 = makeEqAssertion "hnj9" (C.nj9 njxs1 njys1) (nj9 njxs1 njys1)++hnj10 :: Assertion+hnj10 = makeEqAssertion "hnj10" (C.nj10 njxs1 njys1) (nj10 njxs1 njys1)++hnj11 :: Assertion+hnj11 = makeEqAssertion "hnj11" (C.nj11 njxs1 njys1) (nj11 njxs1 njys1)++-- Test data for testcase hnj12+njxs2, njys2, njzs2 :: [Integer]+njxs2 = [1,2,3,4,5,5,2]+njys2 = [2,1,0,5,4,4,4]+njzs2 = [6,1,1,3,2,5]++hnj12 :: Assertion+hnj12 = makeEqAssertion "hnj12" (C.nj12 njxs2 njys2 njzs2) (nj12 njxs2 njys2 njzs2)++hnp1 :: Assertion+hnp1 = makeEqAssertion "hnp1" (C.np1 njxs1 njys1) (np1 njxs1 njys1)++hnp2 :: Assertion+hnp2 = makeEqAssertion "hnp2" (C.np2 njxs1 njys1) (np2 njxs1 njys1)++hnp3 :: Assertion+hnp3 = makeEqAssertion "hnp3" (C.np3 njxs1 njys1) (np3 njxs1 njys1)++hnp4 :: Assertion+hnp4 = makeEqAssertion "hnp4" (C.np4 njxs1 njys1) (np4 njxs1 njys1)++hnjg1 :: Assertion+hnjg1 = makeEqAssertion "hnjg1" (C.njg1 njgxs1 njgzs1) (njg1 njgxs1 njgzs1)++hnjg2 :: Assertion+hnjg2 = makeEqAssertion "hnjg2" (C.njg2 njgxs1 njgys1) (njg2 njgxs1 njgys1)++hnjg3 :: Assertion+hnjg3 = makeEqAssertion "hnjg3" (C.njg3 njgxs1 njgys1 njgzs1) (njg3 njgxs1 njgys1 njgzs1)++hnjg4 :: Assertion+hnjg4 = makeEqAssertion "hnjg4" (C.njg4 njgxs1 njgys1 njgzs1) (njg4 njgxs1 njgys1 njgzs1)++hnjg5 :: Assertion+hnjg5 = makeEqAssertion "hnjg5" (C.njg5 njgxs1 njgys1) (njg5 njgxs1 njgys1)++pair :: a -> b -> (a, b)+pair = (,)++-- Head/NestJoin+nj1 :: [Integer] -> [Integer] -> [[Integer]]+nj1 njxs njys =+    [ [ y | y <- njys, x == y ]+    | x <- njxs+    ]++nj2 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+nj2 njxs njys =+    [ pair x [ y | y <- njys, x == y ]+    | x <- njxs+    ]++nj3 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+nj3 njxs njys =+    [ pair x ([ y | y <- njys, x == y ] ++ ([100, 200, 300]))+    | x <- njxs+    ]++nj4 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+nj4 njxs njys =+      [ pair x ([ y | y <- njys, x == y ] ++ [ z | z <- njys, x == z ])+      | x <- njxs+      ]++nj5 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+nj5 njxs njys =+      [ pair x [ y | y <- njys, x + y > 15 ]+      | x <- njxs+      ]++nj6 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+nj6 njxs njys =+      [ pair x [ y | y <- njys, x + y > 10, y < 7 ]+      | x <- njxs+      ]++nj7 :: [Integer] -> [Integer] -> [[Integer]]+nj7 njxs njys =+    [ [ x + y | y <- njys, x + 2 == y ] | x <- njxs ]++nj8 :: [Integer] -> [Integer] -> [[Integer]]+nj8 njxs njys = [ [ x + y | y <- njys, x == y, y < 5 ] | x <- njxs, x > 3 ]++nj9 :: [Integer] -> [Integer] -> [[Integer]]+nj9 njxs njys = [ [ x + y | y <- njys, x + 1 == y, y > 2, x < 6 ] | x <- njxs ]++nj10 :: [Integer] -> [Integer] -> [Integer]+nj10 njxs njys = [ x + sum [ x * y | y <- njys, x == y ] | x <- njxs ]++nj11 :: [Integer] -> [Integer] -> [[Integer]]+nj11 njxs njys = [ [ x + y | y <- njys, x > y, x < y * 2 ] | x <- njxs ]++nj12 :: [Integer] -> [Integer] -> [Integer] -> [[[(Integer, Integer, Integer)]]]+nj12 njxs njys njzs =+    [ [ [ (x,y,z) | z <- njzs, y == z ]+      | y <- njys+      , x == y+      ]+    | x <- njxs+    ]++-- Head/NestProduct+np1 :: [Integer] -> [Integer] -> [[Integer]]+np1 njxs njys = [ [ x * y * 2 | y <- njys ] | x <- njxs ]++np2 :: [Integer] -> [Integer] -> [(Integer, [Integer])]+np2 njxs njys = [ pair x [ y * 2 | y <- njys ] | x <- njxs ]++np3 :: [Integer] -> [Integer] -> [[Integer]]+np3 njxs njys = [ [ x + y | y <- njys ] | x <- njxs ]++np4 :: [Integer] -> [Integer] -> [[Integer]]+np4 njxs njys = [ [ y | y <- njys, x > y ] | x <- njxs ]++-- Guard/NestJoin++njgxs1 :: [Integer]+njgxs1 = [1,2,3,4,5,6,7,8,12]++njgys1 :: [Integer]+njgys1 = [2,3,2,4,5,5,9,12,2,2,13]++njgzs1 :: [(Integer, Integer)]+njgzs1 = [(2, 20), (5, 60), (3, 30), (3, 80), (4, 40), (5, 10), (5, 30), (12, 120)]++njg1 :: [Integer] -> [(Integer, Integer)] -> [Integer]+njg1 njgxs njgzs =+  [ x+  | x <- njgxs+  , x < 8+  , sum [ snd z | z <- njgzs, fst z == x ] > 100+  ]++njg2 :: [Integer] -> [Integer] -> [Integer]+njg2 njgxs njgys =+  [ x+  | x <- njgxs+  , and [ y > 1 | y <- njgys, x == y ]+  , x < 8+  ]++njg3 :: [Integer] -> [Integer] -> [(Integer, Integer)] -> [(Integer, Integer)]+njg3 njgxs njgys njgzs =+  [ pair x y+  | x <- njgxs+  , y <- njgys+  , length [ () | z <- njgzs, fst z == x ] > 2+  ]++njg4 :: [Integer] -> [Integer] -> [(Integer, Integer)] -> [Integer]+njg4 njgxs njgys njgzs =+  [ x+  | x <- njgxs+  , length [ () | y <- njgys, x == y ]+    > length [ () | z <- njgzs, fst z == x ]+  ]++njg5 :: [Integer] -> [Integer] -> [Integer]+njg5 njgxs njgys =+  [ x+  | x <- njgxs+  , sum [ y | y <- njgys, x < y, y > 5 ] < 10+  ]++--------------------------------------------------------------------------------+--++prop_deep_iter :: ([Integer], [Integer], [Integer], [Integer], [Integer]) -> Property+prop_deep_iter = makeProp C.deep_iter deep_iter_native+  where+    deep_iter_native (ws1, ws2, xs, ys, zs) = +      [ [ [ [ w1 * 23 - y | w1 <- ws1 ]+            +++            [ w2 + 42 - y | w2 <- ws2 ]+          | z <- zs+          , z > x+          ]+        | y <- ys+        ]+      | x <- xs+      ]
+ tests/DSHComprehensions.hs view
@@ -0,0 +1,384 @@+{-# LANGUAGE RebindableSyntax    #-}+{-# LANGUAGE ViewPatterns        #-}+{-# LANGUAGE MonadComprehensions #-}+    +-- | This module contains testcases for monad comprehensions. We store them in a+-- separate module because they rely on RebindableSyntax and hidden Prelude.+   +module DSHComprehensions where++import qualified Prelude as P+import Database.DSH+       +---------------------------------------------------------------+-- Comprehensions for quickcheck tests++cartprod :: Q ([Integer], [Integer]) -> Q [(Integer, Integer)]+cartprod (view -> (xs, ys)) =+  [ tup2 x y+  | x <- xs+  , y <- ys+  ]++eqjoin :: Q ([Integer], [Integer]) -> Q [(Integer, Integer)]+eqjoin (view -> (xs, ys)) = +  [ tup2 x y+  | x <- xs+  , y <- ys+  , x == y+  ]++  +eqjoinproj :: Q ([Integer], [Integer]) -> Q [(Integer, Integer)]+eqjoinproj (view -> (xs, ys)) = +  [ tup2 x y+  | x <- xs+  , y <- ys+  , (2 * x) == y+  ]++eqjoinpred :: Q (Integer, [Integer], [Integer]) -> Q [(Integer, Integer)]+eqjoinpred (view -> (x', xs, ys)) = +  [ tup2 x y+  | x <- xs+  , y <- ys+  , x == y+  , x > x'+  ]++eqjointuples :: Q ([(Integer, Integer)], [(Integer, Integer)]) -> Q [(Integer, Integer, Integer)]+eqjointuples (view -> (xs, ys)) =+  [ tup3 (x1 * x2) y1 y2+  | (view -> (x1, x2)) <- xs+  , (view -> (y1, y2)) <- ys+  , x1 == y2+  ]++thetajoin_eq :: Q ([(Integer, Integer)], [(Integer, Integer)]) -> Q [(Integer, Integer, Integer)]+thetajoin_eq (view -> (xs, ys)) =+  [ tup3 (x1 * x2) y1 y2+  | (view -> (x1, x2)) <- xs+  , (view -> (y1, y2)) <- ys+  , x1 == y2+  , y1 == x2+  ]++thetajoin_neq :: Q ([(Integer, Integer)], [(Integer, Integer)]) -> Q [(Integer, Integer, Integer)]+thetajoin_neq (view -> (xs, ys)) =+  [ tup3 (x1 * x2) y1 y2+  | (view -> (x1, x2)) <- xs+  , (view -> (y1, y2)) <- ys+  , x1 == y2+  , y1 /= x2+  ]++eqjoin3 :: Q ([Integer], [Integer], [Integer]) -> Q [(Integer, Integer, Integer)]+eqjoin3 (view -> (xs, ys, zs)) = +  [ tup3 x y z+  | x <- xs+  , y <- ys+  , z <- zs+  , x == y+  , y == z+  ]+  +eqjoin_nested_left :: Q ([(Integer, [Integer])], [Integer]) -> Q [((Integer, [Integer]), Integer)]+eqjoin_nested_left args =+  [ pair x y+  | x <- fst args+  , y <- snd args+  , fst x == y+  ]++eqjoin_nested_right :: Q ([Integer], [(Integer, [Integer])]) -> Q [(Integer, (Integer, [Integer]))]+eqjoin_nested_right args =+  [ pair x y+  | x <- fst args+  , y <- snd args+  , x == fst y+  ]++eqjoin_nested_both :: Q ([(Integer, [Integer])], [(Integer, [Integer])]) +                   -> Q [((Integer, [Integer]), (Integer, [Integer]))]+eqjoin_nested_both args =+  [ pair x y+  | x <- fst args+  , y <- snd args+  , fst x == fst y+  ]++nestjoin :: Q ([Integer], [Integer]) -> Q [(Integer, [Integer])]+nestjoin (view -> (xs, ys)) =+  [ tup2 x [ y | y <- ys, x == y]+  | x <- xs+  ]++nestjoin3 :: Q ([Integer], [Integer], [Integer]) -> Q [[[(Integer, Integer, Integer)]]]+nestjoin3 (view -> (xs, ys, zs)) =+    [ [ [ tup3 x y z | z <- zs, y == z ]+      | y <- ys+      , x == y+      ]+    | x <- xs+    ]+  +--------------------------------------------------------------+-- Comprehensions for HUnit tests++eqjoin_nested1 :: Q [((Integer, [Char]), Integer)]+eqjoin_nested1 =+    [ pair x y+    | x <- (toQ ([(10, ['a']), (20, ['b']), (30, ['c', 'd']), (40, [])] :: [(Integer, [Char])]))+    , y <- (toQ [20, 30, 30, 40, 50])+    , fst x == y+    ]++semijoin :: Q [Integer]+semijoin = +    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs , x `elem` ys ]++semijoin_range :: Q [Integer]+semijoin_range = +    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6] :: Q [Integer])+    in [ x | x <- xs , x `elem` [ y | y <- ys, y < 6 ] ]++semijoin_quant :: Q [Integer]+semijoin_quant = +    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs, or [ y > 5 | y <- ys, x == y ] ]++semijoin_not_null :: Q [Integer]+semijoin_not_null =+    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs, not $ null [ y | y <- ys, x == y] ]+    ++antijoin :: Q [Integer]+antijoin =+    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs , not $ x `elem` ys ]++antijoin_null :: Q [Integer]+antijoin_null =+    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs, null [ y | y <- ys, x == y] ]++antijoin_range :: Q [Integer]+antijoin_range =+    let xs = (toQ [1, 2, 3, 4, 5, 6, 7] :: Q [Integer])+        ys = (toQ [2, 4, 6, 7] :: Q [Integer])+    in [ x | x <- xs , not $ x `elem` [ y | y <- ys, y < 5 ] ]++antijoin_class12 :: Q [Integer]+antijoin_class12 =+    let xs = toQ ([6,7,8,9,10,12] :: [Integer])+        ys = toQ ([8,9,12,13,15,16] :: [Integer])+    in [ x | x <- xs, and [ x < y | y <- ys, y > 10 ]]++antijoin_class15 :: Q [Integer]+antijoin_class15 =+    let xs = toQ ([3,4,5,6,7,8] :: [Integer])+        ys = toQ ([4,5,8,16] :: [Integer])+    in [ x | x <- xs, and [ y `mod` 4 == 0 | y <- ys, x < y ]]++antijoin_class16 :: Q [Integer]+antijoin_class16 =+    let xs = toQ ([3,4,5,6] :: [Integer])+        ys = toQ ([1,2,3,4,5,6,7,8] :: [Integer])+    in [ x | x <- xs, and [ y <= 2 * x | y <- ys, x < y ]]++frontguard :: Q [[Integer]]+frontguard =+    [ [ y | x > 13, y <- toQ ([1,2,3,4] :: [Integer]), y < 3 ]+    | x <- toQ ([10, 20, 30] :: [Integer])+    ]++----------------------------------------------------------------------+-- Comprehensions for HUnit NestJoin/NestProduct tests++nj1 :: [Integer] -> [Integer] -> Q [[Integer]]+nj1 njxs njys = +    [ [ y | y <- toQ njys, x == y ]+    | x <- toQ njxs+    ]++nj2 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj2 njxs njys = +    [ pair x [ y | y <- toQ njys, x == y ]+    | x <- toQ njxs+    ]++nj3 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj3 njxs njys = +    [ pair x ([ y | y <- toQ njys, x == y ] ++ (toQ [100, 200, 300]))+    | x <- toQ njxs+    ]++nj4 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj4 njxs njys = +      [ pair x ([ y | y <- toQ njys, x == y ] ++ [ z | z <- toQ njys, x == z ])+      | x <- toQ njxs+      ]++-- Code incurs DistSeg for the literal 15.+nj5 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj5 njxs njys = +      [ pair x [ y | y <- toQ njys, x + y > 15 ]+      | x <- toQ njxs+      ]++nj6 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj6 njxs njys = +      [ pair x [ y | y <- toQ njys, x + y > 10, y < 7 ]+      | x <- toQ njxs+      ]++nj7 :: [Integer] -> [Integer] -> Q [[Integer]]+nj7 njxs njys = +    [ [ x + y | y <- toQ njys, x + 2 == y ] | x <- toQ njxs ]++nj8 :: [Integer] -> [Integer] -> Q [[Integer]]+nj8 njxs njys = [ [ x + y | y <- toQ njys, x == y, y < 5 ] | x <- toQ njxs, x > 3 ]++nj9 :: [Integer] -> [Integer] -> Q [[Integer]]+nj9 njxs njys = [ [ x + y | y <- toQ njys, x + 1 == y, y > 2, x < 6 ] | x <- toQ njxs ]++nj10 :: [Integer] -> [Integer] -> Q [Integer]+nj10 njxs njys = [ x + sum [ x * y | y <- toQ njys, x == y ] | x <- toQ njxs ]++nj11 :: [Integer] -> [Integer] -> Q [[Integer]]+nj11 njxs njys = [ [ x + y | y <- toQ njys, x > y, x < y * 2 ] | x <- toQ njxs ]++nj12 :: [Integer] -> [Integer] -> [Integer] -> Q [[[(Integer, Integer, Integer)]]]+nj12 njxs njys njzs =+    [ [ [ tup3 x y z | z <- toQ njzs, y == z ]+      | y <- toQ njys+      , x == y+      ]+    | x <- toQ njxs+    ]++np1 :: [Integer] -> [Integer] -> Q [[Integer]]+np1 njxs njys = [ [ x * y * 2 | y <- toQ njys ] | x <- toQ njxs ]+	++np2 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+np2 njxs njys = [ pair x [ y * 2 | y <- toQ njys ] | x <- toQ njxs ]++np3 :: [Integer] -> [Integer] -> Q [[Integer]]+np3 njxs njys = [ [ x + y | y <- toQ njys ] | x <- toQ njxs ]++np4 :: [Integer] -> [Integer] -> Q [[Integer]]+np4 njxs njys = [ [ y | y <- toQ njys, x > y ] | x <- toQ njxs ]++njg1 :: [Integer] -> [(Integer, Integer)] -> Q [Integer]+njg1 njgxs njgzs =+  [ x+  | x <- toQ njgxs+  , x < 8+  , sum [ snd z | z <- toQ njgzs, fst z == x ] > 100+  ]++njg2 :: [Integer] -> [Integer] -> Q [Integer]+njg2 njgxs njgys =+  [ x+  | x <- toQ njgxs+  , and [ y > 1 | y <- toQ njgys, x == y ]+  , x < 8+  ]++njg3 :: [Integer] -> [Integer] -> [(Integer, Integer)] -> Q [(Integer, Integer)]+njg3 njgxs njgys njgzs =+  [ pair x y+  | x <- toQ njgxs+  , y <- toQ njgys+  , length [ toQ () | z <- toQ njgzs, fst z == x ] > 2+  ]++njg4 :: [Integer] -> [Integer] -> [(Integer, Integer)] -> Q [Integer]+njg4 njgxs njgys njgzs =+  [ x+  | x <- toQ njgxs+  , length [ toQ () | y <- toQ njgys, x == y ] +    > length [ toQ () | z <- toQ njgzs, fst z == x ]+  ]++njg5 :: [Integer] -> [Integer] -> Q [Integer]+njg5 njgxs njgys =+  [ x+  | x <- toQ njgxs+  , sum [ y | y <- toQ njgys, x < y, y > 5 ] < 10+  ]++--------------------------------------------------------------------------------+-- Comprehensions for QuickCheck antijoin/semijoin tests++aj_class12 :: Q ([Integer], [Integer]) -> Q [Integer]+aj_class12 (view -> (xs, ys)) = +  [ x +  | x <- xs+  , and [ x == y | y <- ys, y > 10 ]+  ]++aj_class15 :: Q ([Integer], [Integer]) -> Q [Integer]+aj_class15 (view -> (xs, ys)) = +  [ x +  | x <- xs+  , and [ y `mod` 4 == 0 | y <- ys, x < y ]+  ]++aj_class16 :: Q ([Integer], [Integer]) -> Q [Integer]+aj_class16 (view -> (xs, ys)) = +  [ x +  | x <- xs+  , and [ y <= 2 * x | y <- ys, x < y ]+  ]++++--------------------------------------------------------------------------------+-- Comprehensions for ++backdep :: Q [[Integer]] -> Q [Integer]+backdep xss = [ x | xs <- xss, x <- xs ]++backdep_filter :: Q [[Integer]] -> Q [Integer]+backdep_filter xss = [ x | xs <- xss, x <- xs, length xs > x ]++backdep2 :: Q [[Integer]] -> Q [[Integer]]+backdep2 xss = [ [ x * 42 | x <- xs ] | xs <- xss ]++backdep3 :: Q [[Integer]] -> Q [[Integer]]+backdep3 xss = [ [ x + length xs | x <- xs ] | xs <- xss ]++backdep4 :: Q [[[Integer]]] -> Q [[[Integer]]]+backdep4 xsss = [ [ [ x + length xs + length xss+                    | x <- xs+                    ]+                  | xs <- xss+                  ]+                | xss <- xsss+                ]++backdep5 :: Q [[Integer]] -> Q [[Integer]]+backdep5 xss = [ [ x + length xs | x <- take (length xs - 3) xs ] | xs <- xss ]++deep_iter :: Q ([Integer], [Integer], [Integer], [Integer], [Integer]) -> Q [[[[Integer]]]]+deep_iter (view -> (ws1, ws2, xs, ys, zs)) = +  [ [ [ [ w1 * 23 - y | w1 <- ws1 ]+        +++        [ w2 + 42 - y | w2 <- ws2 ]+      | z <- zs+      , z > x+      ]+    | y <- ys+    ]+  | x <- xs+  ]
tests/Main.hs view
@@ -1,694 +1,60 @@-{-# LANGUAGE TemplateHaskell, GADTs, TypeFamilies, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses #-}-{-# OPTIONS_GHC -Wall -O3 -fno-warn-orphans #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-} +{-# OPTIONS_GHC -Wall -O3 -fno-warn-orphans -fno-warn-overlapping-patterns #-} module Main where+       +import           ComprehensionTests+import           CombinatorTests -import qualified Database.DSH as Q-import Database.DSH (Q, QA)+#ifdef TESTSQL+import           Database.HDBC.PostgreSQL+#endif --- import Database.DSH.Interpreter (fromQ)-import Database.DSH.Compiler (fromQ)+import           System.Environment+import           Test.Framework (Test, defaultMainWithArgs)+import           Test.QuickCheck -import qualified Database.HDBC as HDBC-import Database.HDBC.PostgreSQL+import           Data.List -import Test.QuickCheck-import Test.QuickCheck.Monadic -import Data.DeriveTH--import Data.List-import Data.Maybe-import Data.Either-import GHC.Exts--import Data.Text (Text)-import qualified Data.Text as Text--import Data.Char--instance Arbitrary Text where-  arbitrary = fmap Text.pack arbitrary--data D0 = C01 deriving (Eq,Ord,Show)-derive makeArbitrary ''D0-Q.deriveDSH ''D0--data D1 a = C11 a deriving (Eq,Ord,Show)-derive makeArbitrary ''D1-Q.deriveDSH ''D1--data D2 a b = C21 a b b a deriving (Eq,Ord,Show)-derive makeArbitrary ''D2-Q.deriveDSH ''D2--data D3 = C31 | C32 deriving (Eq,Ord,Show)-derive makeArbitrary ''D3-Q.deriveDSH ''D3--data D4 a = C41 a | C42 deriving (Eq,Ord,Show)-derive makeArbitrary ''D4-Q.deriveDSH ''D4--data D5 a = C51 a | C52 | C53 a a | C54 a a a deriving (Eq,Ord,Show)-derive makeArbitrary ''D5-Q.deriveDSH ''D5--data D6 a b c d e = C61 { c611 :: a, c612 :: (a,b,c,d) } | C62 | C63 a b | C64 (a,b,c) | C65 a b c d e deriving (Eq,Ord,Show)-derive makeArbitrary ''D6-Q.deriveDSH ''D6-+#ifdef TESTSQL getConn :: IO Connection-getConn = connectPostgreSQL "user = 'giorgidz' password = '' host = 'localhost' dbname = 'giorgidz'"+getConn = connectPostgreSQL "user = 'au' password = 'foobar' host = 'localhost' dbname = 'test'"+#endif -qc:: Testable prop => prop -> IO ()+qc :: Testable prop => prop -> IO () qc = quickCheckWith stdArgs{maxSuccess = 100, maxSize = 5}  putStrPad :: String -> IO () putStrPad s = putStr (s ++ replicate (32 - length s) ' ' ) + main :: IO () main = do-    putStrLn "Supprted Types"-    putStrLn "--------------"-    putStrPad "()"-    qc prop_unit-    putStrPad "Bool"-    qc prop_bool-    putStrPad "Char"-    qc prop_char-    putStrPad "Text"-    qc prop_text-    putStrPad "Integer"-    qc prop_integer-    putStrPad "Double"-    qc prop_double-    putStrPad "[Integer]"-    qc prop_list_integer_1-    putStrPad "[[Integer]]"-    qc prop_list_integer_2-    putStrPad "[[[Integer]]]"-    qc prop_list_integer_3-    putStrPad "Maybe Integer"-    qc prop_maybe_integer-    putStrPad "Either Integer Integer: "-    qc prop_either_integer-    putStrPad "D0: "-    qc prop_d0-    putStrPad "D1: "-    qc prop_d1-    putStrPad "D2: "-    qc prop_d2-    putStrPad "D3: "-    qc prop_d3-    putStrPad "D4: "-    qc prop_d4-    putStrPad "D5: "-    qc prop_d5-    putStrPad "D6: "-    qc prop_d6--    putStrLn ""-    putStrLn "Equality, Boolean Logic and Ordering"-    putStrLn "------------------------------------"-    putStrPad "&&"-    qc prop_infix_and-    putStrPad "||"-    qc prop_infix_or-    putStrPad "not"-    qc prop_not-    putStrPad "eq"-    qc prop_eq-    putStrPad "neq"-    qc prop_neq-    putStrPad "cond"-    qc prop_cond-    putStrPad "lt"-    qc prop_lt-    putStrPad "lte"-    qc prop_lte-    putStrPad "gt"-    qc prop_gt-    putStrPad "gte"-    qc prop_gte-    putStrPad "min_integer"-    qc prop_min_integer-    putStrPad "min_double"-    qc prop_min_double-    putStrPad "max_integer"-    qc prop_max_integer-    putStrPad "max_double"-    qc prop_max_double-    -    putStrLn ""-    putStrLn "Tuples"-    putStrLn "------"-    putStrPad "fst"-    qc prop_fst-    putStrPad "snd"-    qc prop_snd--    putStrLn ""-    putStrLn "Numerics:"-    putStrLn "-----------"-    putStrPad "add_integer"-    qc prop_add_integer-    putStrPad "add_double"-    qc prop_add_double-    putStrPad "mul_integer"-    qc prop_mul_integer-    putStrPad "mul_double"-    qc prop_mul_double-    putStrPad "div_double"-    qc prop_div_double-    putStrPad "integer_to_double: "-    qc prop_integer_to_double    -    putStrPad "abs_integer"-    qc prop_abs_integer-    putStrPad "abs_double"-    qc prop_abs_double-    putStrPad "signum_integer: "-    qc prop_signum_integer-    putStrPad "signum_double"-    qc prop_signum_double-    putStrPad "negate_integer: "-    qc prop_negate_integer-    putStrPad "negate_double"-    qc prop_negate_double--    putStrLn ""-    putStrLn "Maybe"-    putStrLn "-----"-    putStrPad "maybe"-    qc prop_maybe-    putStrPad "just"-    qc prop_just-    putStrPad "isJust"-    qc prop_isJust-    putStrPad "isNothing"-    qc prop_isNothing-    putStrPad "fromJust"-    qc prop_fromJust-    putStrPad "fromMaybe"-    qc prop_fromMaybe-    putStrPad "listToMaybe"-    qc prop_listToMaybe-    putStrPad "maybeToList"-    qc prop_maybeToList-    putStrPad "catMaybes"-    qc prop_catMaybes-    putStrPad "mapMaybe"-    qc prop_mapMaybe-    -    putStrLn ""-    putStrLn "Either"-    putStrLn "-----"-    putStrPad "left"-    qc prop_left-    putStrPad "right"-    qc prop_right-    putStrPad "isLeft"-    qc prop_isLeft-    putStrPad "isRight"-    qc prop_isRight-    putStrPad "either"-    qc prop_either-    putStrPad "lefts"-    qc prop_lefts-    putStrPad "rights"-    qc prop_rights-    putStrPad "partitionEithers"-    qc prop_partitionEithers--    putStrLn ""-    putStrLn "Lists"-    putStrLn "-----"-    putStrPad "head"-    qc prop_head-    putStrPad "tail"-    qc prop_tail-    putStrPad "cons"-    qc prop_cons-    putStrPad "snoc"-    qc prop_snoc-    putStrPad "take"-    qc prop_take-    putStrPad "drop"-    qc prop_drop-    putStrPad "map"-    qc prop_map-    putStrPad "filter"-    qc prop_filter-    putStrPad "last"-    qc prop_last-    putStrPad "init"-    qc prop_init-    putStrPad "null"-    qc prop_null-    putStrPad "length"-    qc prop_length-    putStrPad "index"-    qc prop_index-    putStrPad "reverse"-    qc prop_reverse-    putStrPad "append"-    qc prop_append-    putStrPad "groupWith"-    qc prop_groupWith-    putStrPad "sortWith"-    qc prop_sortWith-    putStrPad "and"-    qc prop_and-    putStrPad "or"-    qc prop_or-    putStrPad "any_zero"-    qc prop_any_zero-    putStrPad "all_zero"-    qc prop_all_zero-    putStrPad "sum_integer"-    qc prop_sum_integer-    putStrPad "sum_double"-    qc prop_sum_double-    putStrPad "concat"-    qc prop_concat-    putStrPad "concatMap"-    qc prop_concatMap-    putStrPad "maximum"-    qc prop_maximum-    putStrPad "minimum"-    qc prop_minimum-    putStrPad "splitAt"-    qc prop_splitAt-    putStrPad "takeWhile"-    qc prop_takeWhile-    putStrPad "dropWhile"-    qc prop_dropWhile-    putStrPad "span"-    qc prop_span-    putStrPad "break"-    qc prop_break-    putStrPad "elem"-    qc prop_elem-    putStrPad "notElem"-    qc prop_notElem-    putStrPad "lookup"-    qc prop_lookup-    putStrPad "zip"-    qc prop_zip-    putStrPad "zip3"-    qc prop_zip3-    putStrPad "zipWith"-    qc prop_zipWith-    putStrPad "zipWith3"-    qc prop_zipWith3-    putStrPad "unzip"-    qc prop_unzip-    putStrPad "unzip3"-    qc prop_unzip3-    putStrPad "nub"-    qc prop_nub--makeProp :: (Eq b, QA a, QA b, Show a, Show b)-            => (Q a -> Q b)-            -> (a -> b)-            -> a-            -> Property-makeProp f1 f2 arg = monadicIO $ do-    c  <- run getConn-    db <- run $ fromQ c $ f1 (Q.toQ arg)-    run (HDBC.disconnect c)-    let hs = f2 arg-    assert (db == hs)--makePropNotNull ::  (Eq b, QA a, QA b, Show a, Show b)-                    => (Q [a] -> Q b)-                    -> ([a] -> b)-                    -> [a]-                    -> Property-makePropNotNull q f arg = not (null arg) ==> makeProp q f arg--makePropDouble :: (QA a, Show a)-                  => (Q a -> Q Double)-                  -> (a -> Double)-                  -> a-                  -> Property-makePropDouble f1 f2 arg = monadicIO $ do-    c  <- run getConn-    db <- run $ fromQ c $ f1 (Q.toQ arg)-    run $ HDBC.disconnect c-    let hs = f2 arg-    let eps = 1.0E-8 :: Double;    -    assert (abs (db - hs) < eps)--uncurryQ :: (QA a, QA b) => (Q a -> Q b -> Q c) -> Q (a,b) -> Q c-uncurryQ f = uncurry f . Q.view---- * Supported Types--prop_unit :: () -> Property-prop_unit = makeProp id id--prop_bool :: Bool -> Property-prop_bool = makeProp id id--prop_integer :: Integer -> Property-prop_integer = makeProp id id--prop_double :: Double -> Property-prop_double = makePropDouble id id--prop_char :: Char -> Property-prop_char c = isPrint c ==> makeProp id id c--prop_text :: Text -> Property-prop_text t = Text.all isPrint t ==> makeProp id id t--prop_list_integer_1 :: [Integer] -> Property-prop_list_integer_1 = makeProp id id--prop_list_integer_2 :: [[Integer]] -> Property-prop_list_integer_2 = makeProp id id--prop_list_integer_3 :: [[[Integer]]] -> Property-prop_list_integer_3 = makeProp id id--prop_maybe_integer :: Maybe Integer -> Property-prop_maybe_integer = makeProp id id--prop_either_integer :: Either Integer Integer -> Property-prop_either_integer = makeProp id id--prop_d0 :: D0 -> Property-prop_d0 = makeProp id id--prop_d1 :: D1 Integer -> Property-prop_d1 = makeProp id id--prop_d2 :: D2 Integer Integer -> Property-prop_d2 = makeProp id id--prop_d3 :: D3 -> Property-prop_d3 = makeProp id id--prop_d4 :: D4 Integer -> Property-prop_d4 = makeProp id id--prop_d5 :: D5 Integer -> Property-prop_d5 = makeProp id id--prop_d6 :: D6 Integer Integer Integer Integer Integer -> Property-prop_d6 = makeProp id id---- * Equality, Boolean Logic and Ordering--prop_infix_and :: (Bool,Bool) -> Property-prop_infix_and = makeProp (uncurryQ (Q.&&)) (uncurry (&&))--prop_infix_or :: (Bool,Bool) -> Property-prop_infix_or = makeProp (uncurryQ (Q.||)) (uncurry (||))--prop_not :: Bool -> Property-prop_not = makeProp Q.not not--prop_eq :: (Integer,Integer) -> Property-prop_eq = makeProp (uncurryQ (Q.==)) (uncurry (==))--prop_neq :: (Integer,Integer) -> Property-prop_neq = makeProp (uncurryQ (Q./=)) (uncurry (/=))--prop_cond :: Bool -> Property-prop_cond = makeProp (\b -> Q.cond b 0 1) (\b -> if b then (0 :: Integer) else 1)--prop_lt :: (Integer, Integer) -> Property-prop_lt = makeProp (uncurryQ (Q.<)) (uncurry (<))--prop_lte :: (Integer, Integer) -> Property-prop_lte = makeProp (uncurryQ (Q.<=)) (uncurry (<=))--prop_gt :: (Integer, Integer) -> Property-prop_gt = makeProp (uncurryQ (Q.>)) (uncurry (>))--prop_gte :: (Integer, Integer) -> Property-prop_gte = makeProp (uncurryQ (Q.>=)) (uncurry (>=))--prop_min_integer :: (Integer,Integer) -> Property-prop_min_integer = makeProp (uncurryQ Q.min) (uncurry min)--prop_max_integer :: (Integer,Integer) -> Property-prop_max_integer = makeProp (uncurryQ Q.max) (uncurry max)--prop_min_double :: (Double,Double) -> Property-prop_min_double = makePropDouble (uncurryQ Q.min) (uncurry min)--prop_max_double :: (Double,Double) -> Property-prop_max_double = makePropDouble (uncurryQ Q.max) (uncurry max)---- * Maybe--prop_maybe :: (Integer, Maybe Integer) -> Property-prop_maybe =  makeProp (\a -> Q.maybe (Q.fst a) id (Q.snd a)) (\(i,mi) -> maybe i id mi)--prop_just :: Integer -> Property-prop_just = makeProp Q.just Just--prop_isJust :: Maybe Integer -> Property-prop_isJust = makeProp Q.isJust isJust--prop_isNothing :: Maybe Integer -> Property-prop_isNothing = makeProp Q.isNothing isNothing--prop_fromJust :: Maybe Integer -> Property-prop_fromJust mi = isJust mi ==> makeProp Q.fromJust fromJust mi--prop_fromMaybe :: (Integer,Maybe Integer) -> Property-prop_fromMaybe = makeProp (uncurryQ Q.fromMaybe) (uncurry fromMaybe)--prop_listToMaybe :: [Integer] -> Property-prop_listToMaybe = makeProp Q.listToMaybe listToMaybe--prop_maybeToList :: Maybe Integer -> Property-prop_maybeToList = makeProp Q.maybeToList maybeToList--prop_catMaybes :: [Maybe Integer] -> Property-prop_catMaybes = makeProp Q.catMaybes catMaybes--prop_mapMaybe :: [Maybe Integer] -> Property-prop_mapMaybe = makeProp (Q.mapMaybe id) (mapMaybe id)---- * Either--prop_left :: Integer -> Property-prop_left = makeProp (Q.left :: Q Integer -> Q (Either Integer Integer)) Left--prop_right :: Integer -> Property-prop_right = makeProp (Q.right :: Q Integer -> Q (Either Integer Integer)) Right--prop_isLeft :: Either Integer Integer -> Property-prop_isLeft = makeProp Q.isLeft (\e -> case e of {Left _ -> True; Right _ -> False;})--prop_isRight :: Either Integer Integer -> Property-prop_isRight = makeProp Q.isRight (\e -> case e of {Left _ -> False; Right _ -> True;})--prop_either :: Either Integer Integer -> Property-prop_either =  makeProp (Q.either id id) (either id id)--prop_lefts :: [Either Integer Integer] -> Property-prop_lefts =  makeProp Q.lefts lefts--prop_rights :: [Either Integer Integer] -> Property-prop_rights =  makeProp Q.rights rights--prop_partitionEithers :: [Either Integer Integer] -> Property-prop_partitionEithers =  makeProp Q.partitionEithers partitionEithers---- * Lists--prop_cons :: (Integer, [Integer]) -> Property-prop_cons = makeProp (uncurryQ (Q.<|)) (uncurry (:))--prop_snoc :: ([Integer], Integer) -> Property-prop_snoc = makeProp (uncurryQ (Q.|>)) (\(a,b) -> a ++ [b])--prop_singleton :: Integer -> Property-prop_singleton = makeProp Q.singleton (: [])--prop_head  :: [Integer] -> Property-prop_head  = makePropNotNull Q.head head--prop_tail  :: [Integer] -> Property-prop_tail  = makePropNotNull Q.tail tail--prop_last  :: [Integer] -> Property-prop_last  = makePropNotNull Q.last last--prop_init  :: [Integer] -> Property-prop_init  = makePropNotNull Q.init init--prop_index :: ([Integer], Integer)  -> Property-prop_index (l, i) =-        i > 0 && i < fromIntegral (length l)-    ==> makeProp (uncurryQ (Q.!!))-                 (\(a,b) -> a !! fromIntegral b)-                 (l, i)--prop_take :: (Integer, [Integer]) -> Property-prop_take = makeProp (uncurryQ Q.take) (\(n,l) -> take (fromIntegral n) l)--prop_drop :: (Integer, [Integer]) -> Property-prop_drop = makeProp (uncurryQ Q.drop) (\(n,l) -> drop (fromIntegral n) l)--prop_map :: [Integer] -> Property-prop_map = makeProp (Q.map id) (map id)--prop_append :: ([Integer], [Integer]) -> Property-prop_append = makeProp (uncurryQ (Q.++)) (uncurry (++))--prop_filter :: [Integer] -> Property-prop_filter = makeProp (Q.filter (const $ Q.toQ True)) (filter $ const True)--prop_groupWith :: [Integer] -> Property-prop_groupWith = makeProp (Q.groupWith id) (groupWith id)--prop_sortWith  :: [Integer] -> Property-prop_sortWith = makeProp (Q.sortWith id) (sortWith id)--prop_null :: [Integer] -> Property-prop_null = makeProp Q.null null--prop_length :: [Integer] -> Property-prop_length = makeProp Q.length ((fromIntegral :: Int -> Integer) . length)--prop_reverse :: [Integer] -> Property-prop_reverse = makeProp Q.reverse reverse--prop_and :: [Bool] -> Property-prop_and = makeProp Q.and and--prop_or :: [Bool] -> Property-prop_or = makeProp Q.or or--prop_any_zero :: [Integer] -> Property-prop_any_zero = makeProp (Q.any (Q.== 0)) (any (== 0))--prop_all_zero :: [Integer] -> Property-prop_all_zero = makeProp (Q.all (Q.== 0)) (all (== 0))--prop_sum_integer :: [Integer] -> Property-prop_sum_integer = makeProp Q.sum sum--prop_sum_double :: [Double] -> Property-prop_sum_double = makePropDouble Q.sum sum--prop_concat :: [[Integer]] -> Property-prop_concat = makeProp Q.concat concat--prop_concatMap :: [Integer] -> Property-prop_concatMap = makeProp (Q.concatMap Q.singleton) (concatMap (: []))--prop_maximum :: [Integer] -> Property-prop_maximum = makePropNotNull Q.maximum maximum--prop_minimum :: [Integer] -> Property-prop_minimum = makePropNotNull Q.minimum minimum--prop_splitAt :: (Integer, [Integer]) -> Property-prop_splitAt = makeProp (uncurryQ Q.splitAt) (\(a,b) -> splitAt (fromIntegral a) b)--prop_takeWhile :: (Integer, [Integer]) -> Property-prop_takeWhile = makeProp (uncurryQ $ Q.takeWhile . (Q.==))-                         (uncurry   $   takeWhile . (==))--prop_dropWhile :: (Integer, [Integer]) -> Property-prop_dropWhile = makeProp (uncurryQ $ Q.dropWhile . (Q.==))-                         (uncurry   $   dropWhile . (==))--prop_span :: (Integer, [Integer]) -> Property-prop_span = makeProp (uncurryQ $ Q.span . (Q.==))-                     (uncurry   $   span . (==) . fromIntegral)--prop_break :: (Integer, [Integer]) -> Property-prop_break = makeProp (uncurryQ $ Q.break . (Q.==))-                      (uncurry   $   break . (==) . fromIntegral)--prop_elem :: (Integer, [Integer]) -> Property-prop_elem = makeProp (uncurryQ Q.elem)-                     (uncurry    elem)--prop_notElem :: (Integer, [Integer]) -> Property-prop_notElem = makeProp (uncurryQ Q.notElem)-                        (uncurry    notElem)--prop_lookup :: (Integer, [(Integer,Integer)]) -> Property-prop_lookup = makeProp (uncurryQ Q.lookup)-                       (uncurry    lookup)--prop_zip :: ([Integer], [Integer]) -> Property-prop_zip = makeProp (uncurryQ Q.zip) (uncurry zip)--prop_zipWith :: ([Integer], [Integer]) -> Property-prop_zipWith = makeProp (uncurryQ $ Q.zipWith (+)) (uncurry $ zipWith (+))--prop_unzip :: [(Integer, Integer)] -> Property-prop_unzip = makeProp Q.unzip unzip--prop_zip3 :: ([Integer], [Integer],[Integer]) -> Property-prop_zip3 = makeProp (\q -> (case Q.view q of (as,bs,cs) -> Q.zip3 as bs cs))-                     (\(as,bs,cs) -> zip3 as bs cs)--prop_zipWith3 :: ([Integer], [Integer],[Integer]) -> Property-prop_zipWith3 = makeProp (\q -> (case Q.view q of (as,bs,cs) -> Q.zipWith3 (\a b c -> a + b + c) as bs cs))-                         (\(as,bs,cs) -> zipWith3 (\a b c -> a + b + c) as bs cs)--prop_unzip3 :: [(Integer, Integer, Integer)] -> Property-prop_unzip3 = makeProp Q.unzip3 unzip3--prop_nub :: [Integer] -> Property-prop_nub = makeProp Q.nub nub---- * Tuples--prop_fst :: (Integer, Integer) -> Property-prop_fst = makeProp Q.fst fst--prop_snd :: (Integer, Integer) -> Property-prop_snd = makeProp Q.snd snd---- * Numerics--prop_add_integer :: (Integer,Integer) -> Property-prop_add_integer = makeProp (uncurryQ (+)) (uncurry (+))--prop_add_double :: (Double,Double) -> Property-prop_add_double = makePropDouble (uncurryQ (+)) (uncurry (+))--prop_mul_integer :: (Integer,Integer) -> Property-prop_mul_integer = makeProp (uncurryQ (*)) (uncurry (*))--prop_mul_double :: (Double,Double) -> Property-prop_mul_double = makePropDouble (uncurryQ (*)) (uncurry (*))--prop_div_double :: (Double,Double) -> Property-prop_div_double (x,y) =-      y /= 0-  ==> makePropDouble (uncurryQ (/)) (uncurry (/)) (x,y)--prop_integer_to_double :: Integer -> Property-prop_integer_to_double = makePropDouble Q.integerToDouble fromInteger--prop_abs_integer :: Integer -> Property-prop_abs_integer = makeProp Q.abs abs--prop_abs_double :: Double -> Property-prop_abs_double = makePropDouble Q.abs abs--prop_signum_integer :: Integer -> Property-prop_signum_integer = makeProp Q.signum signum--prop_signum_double :: Double -> Property-prop_signum_double = makePropDouble Q.signum signum--prop_negate_integer :: Integer -> Property-prop_negate_integer = makeProp Q.negate negate+            args <- getArgs+            let args' = if or $ map (isPrefixOf "-s") args+                         then args+                         else "-s5":args+            defaultMainWithArgs tests args' -prop_negate_double :: Double -> Property-prop_negate_double = makePropDouble Q.negate negate+tests :: [Test]+tests =+    [ tests_types+    , tests_tuples+    , tests_join_hunit+    , tests_nest_head_hunit+    , tests_nest_guard_hunit+    , tests_combinators_hunit+    , tests_comprehensions+    , tests_boolean+    , tests_numerics+    , tests_maybe+    , tests_either+    , tests_lists+    , tests_lifted+    ]
− tests/Makefile
@@ -1,12 +0,0 @@-all: cabal-		ghc --make Main.hs -o Main-		./Main--cabal: clean-		cabal install quickcheck-		cabal install hdbc-postgresql-		cabal install derive-		cd ..; cabal install; cd tests;--clean:-		rm -rf tmp .hpc *.html *.tix *.o *.hi Main
+ tests/Manual.hs view
@@ -0,0 +1,364 @@+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MonadComprehensions   #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RebindableSyntax      #-}+{-# LANGUAGE TemplateHaskell       #-}+{-# LANGUAGE TypeFamilies          #-}+{-# LANGUAGE UndecidableInstances  #-}+{-# LANGUAGE ViewPatterns          #-}++module Main where+++import qualified Prelude as P+import Database.DSH+import Database.DSH.Compiler++import Database.HDBC.PostgreSQL++import qualified Data.Text as T++import TPCH++data Foo = Foo { foo1 :: Integer, foo2 :: Text, foo3 :: Integer }++deriveDSH ''Foo+deriveTA ''Foo+generateTableSelectors ''Foo++getConn :: IO Connection+getConn = connectPostgreSQL "user = 'au' password = 'foobar' host = 'localhost' port = '5432' dbname = 'tpch'"++xs :: Q [(Integer, Integer)]+xs = toQ [(3,5),(4,6),(5,7),(6,9)]++ys :: Q [Integer]+ys = toQ [1,2,3,4]++bar :: Q [(Integer, Integer, Integer)]+bar = [ triple a c 42 | (view -> (a, b, c)) <- toQ ([(1,2,3), (4,5,6), (7,8,9)] :: [(Integer, Integer, Integer)]) ]++{-+li :: Q [(Integer, Text, Double)]+li = [ tup3 (l_linenumberQ l) (l_returnflagQ l) (l_discountQ l)+     | l <- lineitems+     , l_taxQ l > 5.0+     ]+-}++data Range = Range { start :: Integer, end :: Integer }++deriveDSH ''Range++avgBalance :: Q [Customer] -> Q Double+avgBalance cs =+  avg [ c_acctbalQ c | c <- cs, c_acctbalQ c > 0.0 ]++ordersOf :: Q Customer -> Q [Order]+ordersOf c =+  [ o | o <- orders, o_custkeyQ o == c_custkeyQ c ]++potentialCustomers :: Q [Customer] -> Q [Customer]+potentialCustomers cs =+  [ c | c <- cs,+        c_acctbalQ c > avgBalance cs, length (ordersOf c) == 0 ]++countryCodeOf :: Q Customer -> Q Text+countryCodeOf c = subString 1 2 (c_phoneQ c)++livesIn :: Q Customer -> [Text] -> Q Bool+livesIn c countries = countryCodeOf c `elem` toQ countries++q22 :: [Text] -> Q [(Text, Integer, Double)]+q22 countries =+  sortWith (\(view -> (country, _, _)) -> country)+    [ tup3 country (length custs) (sum (map c_acctbalQ custs)) |+      (view -> (country, custs)) <- groupWithKey countryCodeOf pots ]+  where+    pots = potentialCustomers [ c | c <- customers,+                                    c `livesIn` countries ]++minSupplyCost :: Q Integer -> Q Double+minSupplyCost partkey = +  minimum $ +  [ ps_supplycostQ ps+  | ps <- partsupps+  , s  <- suppliers+  , n  <- nations+  , r  <- regions+  , partkey == ps_partkeyQ ps+  , s_suppkeyQ s == ps_suppkeyQ ps+  , s_nationkeyQ s == n_nationkeyQ n+  , n_regionkeyQ n == r_regionkeyQ r+  , r_nameQ r == (toQ "EUROPE")+  ]++sortingCriteria+  :: Q (Double, Text, Text, Integer, Text, Text, Text, Text)+  -> Q (Double, Text, Text, Integer)+sortingCriteria (view -> (b, sn, nn, pk, _, _, _, _)) =+  tup4 (b * (toQ $ -1.0)) nn sn pk++q2 :: Q [(Double, Text, Text, Integer, Text, Text, Text, Text)]+q2 = +  sortWith sortingCriteria $+  [ tup8 (s_acctbalQ s)+           (s_nameQ s)+	   (n_nameQ n)+	   (p_partkeyQ p)+	   (p_mfgrQ p)+	   (s_addressQ s)+	   (s_phoneQ s)+	   (s_commentQ s)+  | p  <- parts+  , ps <- partsupps+  , s  <- suppliers+  , n  <- nations+  , r  <- regions+  , p_partkeyQ p == ps_partkeyQ ps+  , s_suppkeyQ s == ps_suppkeyQ ps+  , p_sizeQ p == (toQ 15)+  , p_typeQ p `like` (toQ "%BRASS")+  , s_nationkeyQ s == n_nationkeyQ n+  , n_regionkeyQ n == r_regionkeyQ r+  , r_nameQ r == (toQ "EUROPE")+  , ps_supplycostQ ps == minSupplyCost (p_partkeyQ p)+  ]++orderQuantity :: Q [LineItem] -> Q Double+orderQuantity lis = sum $ map l_quantityQ lis++jan_q7a :: Q [LineItem]+jan_q7a = snd $ head $ sortWith (orderQuantity . snd) $ groupWithKey l_orderkeyQ lineitems++--------------------------------------------------------------------------------+-- Query written from a database viewpoint++-- List the lineitems of the order with the most parts.+sumPerOrder :: Q [(Integer, Double)]+sumPerOrder = map (\(view -> (ok, lis)) -> pair ok (sum $ map l_quantityQ lis)) +	      $ groupWithKey l_orderkeyQ lineitems++jan_q7b :: Q [LineItem]+jan_q7b = +    [ l+    | l <- lineitems+    , (view -> (ok, nrItems)) <- sumPerOrder+    , l_orderkeyQ l == ok+    , nrItems == maximum(map snd sumPerOrder)+    ]++q :: Q [[Integer]]+q = map init (toQ ([] :: [[Integer]]))++data Trade = Trade+    { t_price     :: Double+    , t_tid       :: Integer+    , t_timestamp :: Integer+    , t_tradeDate :: Integer+    }++deriveDSH ''Trade+deriveTA ''Trade+generateTableSelectors ''Trade++data Portfolio = Portfolio+    { po_pid         :: Integer+    , po_tid         :: Integer+    , po_tradedSince :: Integer+    }++deriveDSH ''Portfolio+deriveTA ''Portfolio+generateTableSelectors ''Portfolio++trades :: Q [Trade]+trades = table "trades" $ TableHints [ Key ["t_tid", "t_timestamp"] ] NonEmpty++portfolios :: Q [Portfolio]+portfolios = table "portfolio" $ TableHints [Key ["po_pid"] ] NonEmpty++--------------------------------------------------------------------------------+-- For a given date and stock, compute the best profit obtained by+-- buying the stock and selling it later.++-- | For each list element, compute the minimum of all elements up to+-- the current one.+mins :: (Ord a, QA a, TA a) => Q [a] -> Q [a]+mins as = [ minimum [ a' | (view -> (a', i')) <- nas, i' <= i ]+          | let nas = number as+	  , (view -> (a, i)) <- nas+	  ]   ++{-++Being able to write the query using a parallel comprehension would be+nice:++maximum [ t_priceQ t - minPrice+        | t        <- trades'+        | minPrice <- mins $ map t_priceQ trades'+        ]+++-}++++bestProfit :: Integer -> Integer -> Q Double+bestProfit stock date = +    maximum [ t_priceQ t - minPrice+            | (view -> (t, minPrice)) <- zip trades' (mins $ map t_priceQ trades')+            ]+  where+    trades' = filter (\t -> t_tidQ t == toQ stock && t_tradeDateQ t == toQ date)+              $ sortWith t_timestampQ trades++hasNationality :: Q Customer -> Text -> Q Bool+hasNationality c nn = +    or [ n_nameQ n == toQ nn && n_nationkeyQ n == c_nationkeyQ c+       | n <- nations+       ]++ordersWithStatus :: Text -> Q Customer -> Q [Order]+ordersWithStatus status c =+    [ o | o <- ordersOf c, o_orderstatusQ o == toQ status ]++revenue :: Q Order -> Q Double+revenue o = sum [ l_extendedpriceQ l * (1 - l_discountQ l)+                | l <- lineitems+                , l_orderkeyQ l == o_orderkeyQ o+                ]++expectedRevenueFor :: Text -> Q [(Text, [(Integer, Double)])]+expectedRevenueFor nation =+    [ pair (c_nameQ c) [ pair (o_orderdateQ o) (revenue o)+                       | o <- ordersWithStatus "P" c ]+    | c <- customers+    , c `hasNationality` nation+    ]++foobar = take 10 $ sortWith id $ map revenue orders++njg3 :: [Integer] -> [Integer] -> [(Integer, Integer)] -> Q [(Integer, Integer)]+njg3 njgxs njgys njgzs =+  [ pair x y+  | x <- toQ njgxs+  , y <- toQ njgys+  , length [ toQ () | z <- toQ njgzs, fst z == x ] > 2+  ]++njgxs1 :: [Integer]+njgxs1 = [1,2]++njgys1 :: [Integer]+njgys1 = [2,3]++njgzs1 :: [(Integer, Integer)]+njgzs1 = [(2, 20), (5, 60), (3, 30)]++backdep5 :: Q [[Integer]]+backdep5 = [ [ x + length xs | x <- take (length xs - 3) xs ] | xs <- toQ ([[1,2,3], [], [4,5,6]] :: [[Integer]]) ]++foo42 :: Q [Integer]+foo42 = filter (const $ toQ True) (toQ ([1,2,3,45] :: [Integer]))++revenue2 :: Integer -> Q [(Integer, Double)]+revenue2 intervalFrom =+    [ pair supplier_no (sum [ ep * (1 - discount)+                            | (view -> (_, ep, discount)) <- g+			    ])+    | (view -> (supplier_no, g)) <- groupWithKey (\(view -> (a, b, c)) -> a) intervalItems+    ]++  where+    intervalItems = [ tup3 (l_suppkeyQ l)+    			   (l_extendedpriceQ l)+			   (l_discountQ l)+		    | l <- lineitems+		    , l_shipdateQ l >= toQ intervalFrom+		    , l_shipdateQ l <= (toQ intervalFrom) + 23+		    ]++q15 :: Integer -> Q [(Integer, (Text, Text, Text, Double))]+q15 intervalFrom = +    sortWith fst+    [ pair (s_suppkeyQ s)+           (tup4 (s_nameQ s)+	         (s_addressQ s)+	         (s_phoneQ s)+	         total_rev)+    | s <- suppliers+    , (view -> (supplier_no, total_rev)) <- revenue2 intervalFrom+    , s_suppkeyQ s == supplier_no+    , total_rev == (maximum $ map snd $ revenue2 intervalFrom)+    ]++cartprod :: Q ([Integer], [Integer]) -> Q [(Integer, Integer)]+cartprod (view -> (xs, ys)) =+  [ tup2 x y+  | x <- xs+  , y <- ys+  , x == y+  ]++tup :: Q [(Integer, Integer, Integer, Integer)]+tup = map (\(view -> (a, b, c, d)) -> tup4 (a + c) (b - d) b d) (toQ ([(0,0,0,0)] :: [(Integer, Integer, Integer, Integer)]))++frontguard :: Q [[Integer]]+frontguard =+    [ [ y | x > 13, y <- toQ ([1,2,3,4] :: [Integer]), y < 3 ]+    | x <- toQ ([10, 20, 30] :: [Integer])+    ]++njxs1 :: [Integer]+njxs1 = [1,2,3,4,5,6]++njys1 :: [Integer]+njys1 = [3,4,5,6,3,6,4,1,1,1]++nj6 :: [Integer] -> [Integer] -> Q [(Integer, [Integer])]+nj6 njxs njys = +      [ pair x [ y | y <- toQ njys, x + y > 10, y < 7 ]+      | x <- toQ njxs+      ]++nj9 :: [Integer] -> [Integer] -> Q [[Integer]]+nj9 njxs njys = [ [ x + y | y <- toQ njys, x + 1 == y, y > 2, x < 6 ] | x <- toQ njxs ]++backdep3 :: Q [[Integer]] -> Q [[Integer]]+backdep3 xss = [ [ x + length xs | x <- xs ] | xs <- xss ]++backdep4 :: Q [[[Integer]]] -> Q [[[Integer]]]+backdep4 xsss = [ [ [ x + length xs + length xss+                    | x <- xs+                    ]+                  | xs <- xss+                  ]+                | xss <- xsss+                ]++q23 :: [[[Integer]]] -> Q [[(Integer, [[Integer]])]]+q23 xsss = map (groupWithKey length) (toQ xsss)++-- Test data for testcase hnj12+njxs2, njys2, njzs2 :: [Integer]+njxs2 = [1,2,3,4,5,5,2]+njys2 = [2,1,0,5,4,4,4]+njzs2 = [6,1,1,3,2,5]++nj12 :: [Integer] -> [Integer] -> [Integer] -> Q [[[(Integer, Integer, Integer)]]]+nj12 njxs njys njzs =+    [ [ [ tup3 x y z | z <- toQ njzs, y == z ]+      | y <- toQ njys+      , x == y+      ]+    | x <- toQ njxs+    ]++main :: IO ()+main = getConn P.>>= \c -> debugQ "q" c (nj12 njxs2 njys2 njzs2)  P.>>= \r -> putStrLn (show r)+-- main = runQX100 x100Conn q P.>>= \r -> putStrLn $ show r+--main = debugQX100 "q" x100Conn q