project-m36-0.9.4: src/lib/ProjectM36/Shortcuts.hs
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ExtendedDefaultRules #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE UndecidableInstances #-}
module ProjectM36.Shortcuts where
-- users need OverloadedLabels, OverloadedLists, and default(Int,Text) to use these shortcuts.
import Data.Text hiding (foldl, map)
import ProjectM36.Base
import ProjectM36.Relation
import ProjectM36.Atomable
import Prelude hiding ((!!))
import Data.Proxy
import GHC.OverloadedLabels
import GHC.TypeLits hiding (Text)
import qualified Data.Text as T
import qualified Data.Map as M
import qualified Data.Set as S
import GHC.Exts (IsList(..))
import Data.Convertible
default (Text)
instance IsList (AttributeNamesBase ()) where
type Item (AttributeNamesBase ()) = AttributeName
fromList = AttributeNames . S.fromList
toList (AttributeNames ns) = S.toList ns
toList _ = error "needs AttributeNames"
instance IsList (TupleExprsBase ()) where
type Item TupleExprs = TupleExpr
fromList = TupleExprs ()
toList (TupleExprs _ ts) = ts
instance IsList TupleExpr where
type Item TupleExpr = (AttributeName, AtomExpr)
fromList attributeValues = TupleExpr (M.fromList attributeValues)
toList (TupleExpr attributeValues) = M.toList attributeValues
-- #xxx :: Text
instance KnownSymbol x => IsLabel x Text where
fromLabel = T.pack $ symbolVal @x Proxy
-- #relvarName :: RelationalExpr
instance KnownSymbol x => IsLabel x RelationalExpr where
fromLabel = RelationVariable (T.pack $ symbolVal @x Proxy) ()
-- *Main> #a Int :: AttributeExpr
-- NakedAttributeExpr (Attribute "a" IntAtomType)
-- *Main> #a (Attr @[Int]) :: AttributeExpr
-- NakedAttributeExpr (Attribute "a" (ConstructedAtomType "List" (fromList [("a",IntAtomType)])))
-- can't offer a Relation atomtype -- don't know how to express a Relation type in haskell type. Maybe something a HList of (Text, a) ?
--
-- ps. I don't understand the usage of "AttributeAndTypeNameExpr AttributeName TypeConstructor a"
instance (KnownSymbol x, Atomable a)=> IsLabel x (HaskAtomType a -> AttributeExpr) where
fromLabel = (NakedAttributeExpr . Attribute name) . toAtomType''
where name = T.pack $ symbolVal @x Proxy
-- (#a 1) :: ExtendTupleExpr
-- no need for :=
instance (Convertible a AtomExpr, KnownSymbol x) => IsLabel x (a -> ExtendTupleExpr) where
fromLabel x = AttributeExtendTupleExpr name (convert x)
where name = T.pack $ symbolVal @x Proxy
-- #name AtomExpr
-- ex. tuple [ #name 3 ]
-- default(Text) is needed in client code to avoid `no Atomable Char`
instance (Convertible a AtomExpr, KnownSymbol x) => IsLabel x (a -> (AttributeName, AtomExpr)) where
fromLabel = \x -> (name, convert x)
where name = T.pack $ symbolVal @x Proxy
-- *Main> #a [1] :: AtomExpr
-- FunctionAtomExpr "a" [NakedAtomExpr (IntegerAtom 1)] ()
--
-- This usage is not working in RestrictionPredicateExpr and AttributeExtendTupleExpr. Use f "a" [1] instead.
instance (KnownSymbol x, Convertible a AtomExpr) => IsLabel x ([a] -> AtomExpr) where
fromLabel = \as' -> FunctionAtomExpr name (map convert as') ()
where name = T.pack $ symbolVal @x Proxy
instance (KnownSymbol x) => IsLabel x AtomExpr where
fromLabel = AttributeAtomExpr name
where name = T.pack $ symbolVal @x Proxy
data HaskAtomType a where
Int :: HaskAtomType Int
Integer :: HaskAtomType Integer
Double :: HaskAtomType Double
Text :: HaskAtomType Text
-- Day :: HaskAtomType Day
-- DateTime :: HaskAtomType DateTime
-- ByteString :: HaskAtomType ByteString
Bool :: HaskAtomType Bool
Attr :: Atomable a => HaskAtomType a -- a Proxy-like value for type application.
toAtomType'' :: Atomable a => HaskAtomType a -> AtomType
toAtomType'' (_ :: HaskAtomType a) = toAtomType (Proxy @a)
-- usage: relation [tuple [#a 1, #b "b"], tuple [#a 2, #b "b"]]
relation :: [TupleExpr] -> RelationalExpr
relation ts = MakeRelationFromExprs Nothing (TupleExprs () ts)
relation' :: [AttributeExprBase ()] -> [TupleExpr] -> RelationalExpr
relation' as' ts = MakeRelationFromExprs (Just as') (TupleExprs () ts)
-- usage: tuple [#name "Mike",#age 6]
tuple :: [(AttributeName, AtomExpr)] -> TupleExprBase ()
tuple as' = TupleExpr (M.fromList as')
-- #a rename [#b `as` #c]
rename :: RelationalExpr -> [(AttributeName,AttributeName)] -> RelationalExpr
rename relExpr renameList = case renameList of
[] -> Restrict TruePredicate relExpr
renames ->
foldl (\acc (old,new) -> Rename old new acc) relExpr renames
--project !!
-- #a !! [#b,#c]
infix 9 !!
(!!) :: RelationalExpr -> AttributeNames -> RelationalExpr
relExpr !! xs = Project xs relExpr
--join ><
-- #a >< #b
(><) :: RelationalExpr -> RelationalExpr -> RelationalExpr
a >< b = Join a b
allBut :: AttributeNames -> AttributeNames
allBut (AttributeNames ns) = InvertedAttributeNames ns
allBut _ = error "give allBut something other than attribute names."
allFrom :: RelationalExpr -> AttributeNames
allFrom = RelationalExprAttributeNames
as :: AttributeNames -> AttributeName -> (AttributeNames, AttributeName)
as = (,)
-- #a `group` ([#b,#c] `as` #d)
group :: RelationalExpr -> (AttributeNames, AttributeName) -> RelationalExpr
group relExpr (aNames, aName) = Group aNames aName relExpr
-- #a `ungroup` #b
ungroup :: RelationalExpr -> AttributeName -> RelationalExpr
ungroup relExpr aName = Ungroup aName relExpr
-- *Main> #a #:= true #: ( #b (f "count" [1,2]))
-- Assign "a" (Extend (AttributeExtendTupleExpr "b" (FunctionAtomExpr "count" [NakedAtomExpr (IntegerAtom 1),NakedAtomExpr (IntegerAtom 2)] ())) (ExistingRelation (Relation attributesFromList [] (RelationTupleSet {asList = [RelationTuple attributesFromList [] []]}))))
(#:) :: RelationalExpr -> ExtendTupleExpr -> RelationalExpr
a #: b = Extend b a
infix 8 #:
instance Convertible AtomExpr AtomExpr where
safeConvert = Right
instance Convertible RelVarName AtomExpr where
safeConvert n = Right $ RelationAtomExpr (RelationVariable n ())
instance Convertible RelationalExpr AtomExpr where
safeConvert relExpr = Right $ RelationAtomExpr relExpr
instance Convertible RelVarName RelationalExpr where
safeConvert n = Right $ RelationVariable n ()
-- @ in tutd
-- (@@) "aaa"
(@@) :: AttributeName -> AtomExpr
(@@) = AttributeAtomExpr
-- works in RestrictedPredicateExpr and AttributeExtendTupleExpr
-- usage: f "gte" [1]
f :: Convertible a AtomExpr => FunctionName -> [a] -> AtomExpr
f n as' = FunctionAtomExpr n (map convert as') ()
-- DatabaseContextExpr
-- define
(#::) :: RelVarName -> [AttributeExpr] -> DatabaseContextExpr
s #:: xs = Define s xs
infix 5 #::
-- assign
(#:=) :: RelVarName -> RelationalExpr -> DatabaseContextExpr
s #:= r = Assign s r
infix 5 #:=
class Boolean a b where
(&&&) :: a -> b -> RestrictionPredicateExpr
infixl 6 &&&
(|||) :: a -> b -> RestrictionPredicateExpr
infixl 5 |||
-- where: @~ mimics the restriction symbol in algebra
-- usage: true #: (#a 1) @~ #a ?= 1 &&& not' false ||| (f "gte" [1])
(@~) :: Convertible a RestrictionPredicateExpr => RelationalExpr -> a -> RelationalExpr
(@~) relExpr resPreExpr = Restrict (convert resPreExpr) relExpr
infix 4 @~
true :: RelationalExpr
true = ExistingRelation relationTrue
false :: RelationalExpr
false = ExistingRelation relationFalse
trueP :: RestrictionPredicateExprBase a
trueP = TruePredicate
falseP :: RestrictionPredicateExprBase a
falseP = NotPredicate TruePredicate
(?=) :: Convertible a AtomExpr => AttributeName -> a -> RestrictionPredicateExpr
(?=) name a = AttributeEqualityPredicate name (convert a)
infix 9 ?=
not' :: Convertible a RestrictionPredicateExpr => a -> RestrictionPredicateExpr
not' = NotPredicate . convert
instance (Convertible a RestrictionPredicateExpr, Convertible b RestrictionPredicateExpr) => Boolean a b where
a &&& b = AndPredicate (convert a) (convert b)
a ||| b = OrPredicate (convert a) (convert b)
instance {-# Incoherent #-} Atomable a => Convertible a RestrictionPredicateExpr where
safeConvert n = Right $ AtomExprPredicate $ toAtomExpr . toAtom $ n
instance {-# Incoherent #-} Convertible RelationalExpr RestrictionPredicateExpr where
safeConvert a = Right $ RelationalExprPredicate a
instance {-# Incoherent #-} Convertible AtomExpr RestrictionPredicateExpr where
safeConvert a = Right $ AtomExprPredicate a
instance {-# Incoherent #-} Convertible RestrictionPredicateExpr RestrictionPredicateExpr where
safeConvert = Right
instance {-# Incoherent #-} Atomable a => Convertible a AtomExpr where
safeConvert n = Right $ toAtomExpr . toAtom $ n
toAtomExpr :: Atom -> AtomExpr
toAtomExpr (ConstructedAtom n _ xs) = ConstructedAtomExpr n (toAtomExpr <$> xs) ()
toAtomExpr a = NakedAtomExpr a