multifocal-0.0.1: src/Data/Transform/TwoLevel.hs
-----------------------------------------------------------------------------
-- |
-- Module : Data.Transform.TwoLevel
-- Copyright : (c) 2011 University of Minho
-- License : BSD3
--
-- Maintainer : hpacheco@di.uminho.pt
-- Stability : experimental
-- Portability : non-portable
--
-- Multifocal:
-- Bidirectional Two-level Transformation of XML Schemas
--
-- Combinators for Two-Level Data Transformation.
--
-----------------------------------------------------------------------------
module Data.Transform.TwoLevel where
import Data.Type
import Data.Pf
import Data.Equal
import Data.Eval
import Data.Spine
import Data.Lens
import Data.Default
import Generics.Pointless.Lenses
import Generics.Pointless.Combinators
import Generics.Pointless.Functors hiding (rep)
import Transform.Examples.Company
import Transform.Rewriting (reducePf,reduceIO)
import Transform.Rules.XPath
import Transform.Rules.PF
import Transform.Rules.Lenses
import Prelude hiding (all,Functor,until)
import Control.Monad.State as ST hiding (Functor,lift,when)
import Data.List hiding (all)
import Data.Char
import Data.Map as Map hiding (map)
import Data.Maybe
import Unsafe.Coerce
-- * Utils
maybeRead :: Read a => String -> Maybe a
maybeRead = maybe Nothing (Just . fst) . listToMaybe . reads
safeTail :: [a] -> [a]
safeTail [] = []
safeTail (x:xs) = xs
-- * Data rewriting monad: with a log of applied rules and an index of type names
type NewDatas = Map String DynFctr
type Log = [(String,String)]
type RuleTMonad m a = StateT (Log,NewDatas) m a
type RuleT = forall a . Type a -> RuleTMonad Maybe (View a)
newtype RuleTRep = RuleTRep RuleT
nextName :: String -> String
nextName (span (/= '\'') -> (prefix,suffix)) = case (maybeRead (safeTail suffix) :: Maybe Int) of
{ Just i -> prefix ++ "'" ++ show (succ i)
; otherwise -> prefix ++ suffix ++ "'1" }
newData :: Functor f => String -> Fctr f -> RuleTMonad Maybe (Type (Fix f))
newData name fctr = do
(log,datas) <- ST.get
case (Map.lookup name datas) of
{ Just (DynF g) -> case feq fctr g of
{ Just Eq -> return (NewData name fctr)
; otherwise -> newData (nextName name) fctr }
; otherwise -> do
ST.put (log,Map.insert name (DynF fctr) datas)
return (NewData name fctr) }
data View a where
View :: Pf (Lens a b) -> Type b -> View a
data ViewFunc a where
ViewFunc :: Pf (a -> b) -> Type b -> ViewFunc a
instance Show (View a) where
show (View _ b) = "(View Lens " ++ (show b) ++ ")"
transformtype :: Type a -> RuleT -> DynType
transformtype a r = maybe (DynT a) id $ do
(View lns b,m) <- transform a r
return $ DynT b
transformSafe :: Type a -> RuleT -> (View a,Map String DynFctr)
transformSafe a r = maybe (View ID_LNS a,Map.empty) (id >< snd) $ runStateT (r a) ([],collectNewDatas a)
transform :: Type a -> RuleT -> Maybe (View a,Map String DynFctr)
transform a r = maybe Nothing (Just . (id >< snd)) $ runStateT (r a) ([],collectNewDatas a)
-- Print the string representation of the target type encapsulated in a view.
showType :: View a -> String
showType (View _ b) = show b
showPf :: Type a -> View a -> String
showPf a (View f b) = gshow (Pf $ Lns a b) f
showOptimisedPf :: Type a -> View a -> String
showOptimisedPf a (View lns b) = gshow (Pf $ Lns a b) lns'
where lns' = reducePf optimise_all_lns (Lns a b) lns
showOptimisedPfIO :: Type a -> View a -> IO ()
showOptimisedPfIO a (View lns b) = reduceIO optimise_all_lns (Lns a b) lns >> return ()
showLog :: Type a -> Log -> String
showLog t l = show t ++ unlines (Prelude.map aux $ reverse l)
where aux (n,s) = "\n<= {" ++ n ++ "}\n " ++ s
-- Apply a traceable data transformation rule.
success :: String -> View a -> RuleTMonad Maybe (View a)
success n v = do
(x,datas) <- ST.get
ST.put $ ((n,showType v) : x,datas)
return v
-- ** Two-level generic combinators
-- Sequential composition
(>>>) :: RuleT -> RuleT -> RuleT
(r >>> s) a = do (View f b) <- r a
(View g c) <- s b
return $ (View (COMP_LNS b g f) c)
-- Left-biased choice
(|||) :: RuleT -> RuleT -> RuleT
(r ||| s) x = r x `mplus` s x
-- Identity
nop :: RuleT
nop x = return $ (View ID_LNS x)
-- Apply a rule or do nothing.
try :: RuleT -> RuleT
try r = r ||| nop
-- Repeat until failure, zero or more times.
many :: RuleT -> RuleT
many r = try (r >>> many r)
-- ** Two-level locators
type TPredicate = forall a . Type a -> Bool
listP :: TPredicate -> TPredicate
listP p (List a) = p a
listP p _ = False
atP :: String -> TPredicate
atP name a@(dataName -> Just n) = sameName name n
atP name _ = False
andP :: TPredicate -> TPredicate -> TPredicate
andP f g a = f a && g a
orP :: TPredicate -> TPredicate -> TPredicate
orP f g a = f a || g a
prodP :: [TPredicate] -> TPredicate
prodP [p] a = p a
prodP (p:ps) (Prod a b) = p a && prodP ps b
prodP _ _ = False
sumP :: [TPredicate] -> TPredicate
sumP [p] a = p a
sumP (p:ps) (Either a b) = p a && sumP ps b
sumP _ _ = False
notP :: TPredicate -> TPredicate
notP p a = Prelude.not (p a)
-- Apply a rule whenever a type-level predicate is satisfied.
when :: TPredicate -> RuleT -> RuleT
when q r a = guard (q a) >> r a
-- Apply a rule to a data type with a given name.
at :: String -> RuleT -> RuleT
at name r = when (atP name) r
not :: RuleT -> RuleT -> RuleT
not r s a = case transform a r of
{ Just v -> mzero
; otherwise -> s a
}
hoist :: RuleT
hoist a@(Data _ fctr) = return $ View OUT_LNS (rep fctr a)
hoist a@(NewData _ fctr) = return $ View OUT_LNS (rep fctr a)
hoist _ = mzero
plunge :: String -> RuleT
plunge s a = do
FRep f <- inferKFctr a
new <- newData s f
Eq <- teq (rep f Dynamic) (rep f new)
return $ View INN_LNS new
rename :: String -> RuleT
rename s a@(Data _ fctr) = do
new <- newData s fctr
return $ View (CATA_LNS INN_LNS) new
rename s a@(NewData _ fctr) = do
new <- newData s fctr
return $ View (CATA_LNS INN_LNS) new
rename s a = mzero
-- ** Two-level abstractions
erase :: RuleT
erase a = success "erase" $ View (BANG_LNS (constPf $ defvalue a)) One
-- The argument types may involve patterns, i.e., type variables
liftPf :: Type a -> Type b -> Pf (a -> b) -> RuleT
liftPf patt app func a = do
(Eq,vars) <- teqvars patt a
b <- replacevar app vars
lns <- lensify (Fun a b) func
success "liftPf" $ View lns b
lift :: Type a -> Type b -> Pf (Lens a b) -> RuleT
lift patt app lns a = do
(Eq,vars) <- teqvars patt a
b <- replacevar app vars
success "lift" $ View lns b
liftQ :: Typeable r => Pf (Q r) -> RuleT
liftQ (q :: Pf (Q r)) a = do
let r = typeof :: Type r
q' = reducePf optimise_xpath (Fun a r) (APPLYQ a q)
liftQ' a r q'
liftQ' :: Type a -> Type r -> Pf (a -> r) -> RuleTMonad Maybe (View a)
liftQ' a r@(teq (List Dynamic) -> Just Eq) q = do
ViewFunc l b <- eraseDyns a q
liftPf a b l a
liftQ' a r@(teq Dynamic -> Just Eq) q = do
ViewFunc l b <- eraseDyn a q
liftPf a b l a
liftQ' a r q = liftPf a r q a
-- tries to eliminate dynamic values from an xpath query
eraseDyns :: MonadPlus m => Type a -> Pf (a -> [Dynamic]) -> m (ViewFunc a)
eraseDyns a pf = do
DynT b <- collectDyn (Pf (Fun a (List Dynamic))) pf
let pf' = reducePf optimise_pf (Fun a (List b)) (COMP (List Dynamic) (MAP $ UNDYN b) pf)
-- unwrap singleton lists
case pf' of
(COMP b WRAP f) -> return $ ViewFunc f b
otherwise -> return $ ViewFunc pf' (List b)
-- tries to eliminate dynamic values from an xpath query returning a single value
eraseDyn :: MonadPlus m => Type a -> Pf (a -> Dynamic) -> m (ViewFunc a)
eraseDyn a pf = do
DynT b <- collectDyn (Pf (Fun a Dynamic)) pf
let pf' = reducePf optimise_pf (Fun a b) (COMP Dynamic (UNDYN b) pf)
return $ ViewFunc pf' b
-- ** Two-level strategies
onceNorm :: RuleT -> RuleT
onceNorm r = once r >>> normalize
-- Apply a rule exhaustively many times starting from the top.
outermost :: RuleT -> RuleT
outermost r = many (onceNorm r)
allNorm :: RuleT -> RuleT
allNorm r = all r >>> normalize
-- Apply argument rule everywhere, in a bottom-up approach
everywhere :: RuleT -> RuleT
everywhere r = allNorm (everywhere r) >>> r
-- Apply argument rule where possible, in a bottom-up approach
anywhere :: RuleT -> RuleT
anywhere r = everywhere (try r)
-- Apply argument rule everywhere, in a top-down approach
everywhere' :: RuleT -> RuleT
everywhere' r = r >>> allNorm (everywhere' r)
type RuleF = forall f. Functor f => Fctr f -> RuleTMonad Maybe (ViewF f)
type NatPfLens f g = forall a. Type a -> Pf (Lens (Rep f a) (Rep g a))
data ViewF f where
ViewF :: (Functor f,Functor g) => NatPfLens f g -> Fctr g -> ViewF f
-- Apply a rule to all childs.
all :: RuleT -> RuleT
all r (List a) = do
View f b <- r a
return $ View (MAP_LNS f) (List b)
all r (Prod a b) = do
View f c <- r a
View g d <- r b
return $ View (PROD_LNS f g) (Prod c d)
all r (Either a b) = do
View f c <- r a
View g d <- r b
return $ View (SUM_LNS f g) (Either c d)
all r t@(Data s f) = (do
ViewF l g <- allF r f
Eq <- feq f g
let cata = CATA_LNS $ COMP_LNS (rep g t) INN_LNS (l t)
return $ View cata t)
`mplus` (do
(ViewF l g) <- allF r f
new <- newData s g
let cata = CATA_LNS $ COMP_LNS (rep g new) INN_LNS (l new)
return $ View cata new)
all r t@(NewData s f) = do
(ViewF l g) <- allF r f
new <- newData s g
let cata = CATA_LNS $ COMP_LNS (rep g new) INN_LNS (l new)
return $ View cata new
all r a = return $ View ID_LNS a
allF :: RuleT -> RuleF
allF r I = return $ ViewF (\a -> ID_LNS) I
allF r L = return $ ViewF (\a -> ID_LNS) L
allF r (K t) = do
View l t' <- r t
return $ ViewF (\a -> l) (K t')
allF r (f :*!: g) = do
ViewF lf f' <- allF r f
ViewF lg g' <- allF r g
return $ ViewF (\a -> PROD_LNS (lf a) (lg a)) (f' :*!: g')
allF r (f :+!: g) = do
ViewF lf f' <- allF r f
ViewF lg g' <- allF r g
return $ ViewF (\a -> SUM_LNS (lf a) (lg a)) (f' :+!: g')
allF r (f :@!: g) = do
ViewF lf f' <- allF r f
ViewF lg g' <- allF r g
return $ ViewF (\a -> COMP_LNS (rep (f :@!: g') a) (lf (rep g' a)) (FMAP_LNS f (Fun (rep g a) (rep g' a)) (lg a))) (f' :@!: g')
once :: RuleT -> RuleT
once r (Id a) = mzero
once r (List a) = r (List a) `mplus` (do
(View f b) <- once r a
return $ View (MAP_LNS f) (List b))
once r (Prod a b) = r (Prod a b) `mplus` (do
View f c <- once r a
return $ View (PROD_LNS f ID_LNS) (Prod c b))
`mplus` (do
View g d <- once r b
return $ View (PROD_LNS ID_LNS g) (Prod a d))
once r (Either a b) = r (Either a b) `mplus` (do
View f c <- once r a
return $ View (SUM_LNS f ID_LNS) (Either c b))
`mplus` (do
View g d <- once r b
return $ View (SUM_LNS ID_LNS g) (Either a d))
once r a@(Data s f) = r a `mplus` (do
ViewF l g <- onceF r f
Eq <- feq f g
let anal = (ANA_LNS $ COMP_LNS (rep f a) (l a) OUT_LNS)
return $ View anal a)
`mplus` (do
ViewF l g <- onceF r f
new <- newData s g
let anal = ANA_LNS $ COMP_LNS (rep f a) (l a) OUT_LNS
return $ View anal new)
once r a@(NewData s f) = r a `mplus` (do
ViewF l g <- onceF r f
new <- newData s g
let anal = ANA_LNS $ COMP_LNS (rep f a) (l a) OUT_LNS
return $ View anal new)
once r a = r a
onceF :: RuleT -> RuleF
onceF r f = do
View l ga <- onceF' r f (Id Any)
FRep g <- inferFctr (Id Any) ga
return $ ViewF (\b -> unsafeCoerce l) g
onceF' :: RuleT -> Fctr f -> Type a -> RuleTMonad Maybe (View (Rep f a))
onceF' r I a = mzero
onceF' r L a = r (List a)
onceF' r (K t) a = r t `mplus` (do
View l t' <- once r t
return $ View l t')
onceF' r (f :*!: g) a = r (rep (f:*!:g) a) `mplus` (do
View lf b <- onceF' r f a
return $ View (PROD_LNS lf ID_LNS) $ Prod b (rep g a))
`mplus` (do
View lg b <- onceF' r g a
return $ View (PROD_LNS ID_LNS lg) $ Prod (rep f a) b)
onceF' r (f :+!: g) a = r (rep (f:+!:g) a) `mplus` (do
View lf b <- onceF' r f a
return $ View (SUM_LNS lf ID_LNS) $ Either b (rep g a))
`mplus` (do
View lg b <- onceF' r g a
return $ View (SUM_LNS ID_LNS lg) $ Either (rep f a) b)
onceF' r (f :@!: g) a = r (rep (f:@!:g) a) `mplus` (do
View lf b <- onceF' r f (rep g a)
return $ View lf b)
`mplus` (do
View lg b <- onceF' r g a
return $ View (FMAP_LNS f (Fun (rep g a) b) lg) $ rep f b)
-- ** Normalized type equality
normalizetype :: RuleT
normalizetype = many (once normalizetyperules)
normalizetyperules :: RuleT
normalizetyperules = prodAssoc ||| sumAssoc
normalizedteq :: Type a -> Type b -> Bool
normalizedteq a b = let dyna = transformtype a normalizetype
dynb = transformtype b normalizetype
in applyDynT2 (\a b -> teqBool (replacedyn a) b) dyna dynb
-- ** Normalization
normalize :: RuleT
normalize = many (once normalizerules)
normalizerules :: RuleT
normalizerules = prodAssoc ||| prodOne ||| listOne ||| filterOne
||| sumAssoc ||| eitherSame ||| listList
prodOne :: RuleT
prodOne (Prod a One) = return $ View (FST_LNS BANG) a
prodOne (Prod One a) = return $ View (SND_LNS BANG) a
prodOne _ = mzero
listOne :: RuleT
listOne (Either (List a) One) = do
l <- lensify (Fun (Either (List a) One) (List a)) (ID `EITHER` ZERO)
return $ View l (List a)
listOne (Either One (List a)) = do
l <- lensify (Fun (Either One (List a)) (List a)) (ZERO `EITHER` ID)
return $ View l (List a)
listOne _ = mzero
filterOne :: RuleT
filterOne (List (Either a One)) = return $ View FILTER_LEFT_LNS (List a)
filterOne (List (Either One a)) = return $ View FILTER_RIGHT_LNS (List a)
filterOne (List a@(Data _ _)) = filterOne' (List a)
filterOne (List a@(NewData _ _)) = filterOne' (List a)
filterOne _ = mzero
filterOne' :: (Mu a,Functor (PF a)) => Type [a] -> RuleTMonad Maybe (View [a])
filterOne' (List a@(dataNameFctr -> Just (n,f :+!: K One))) = do
guard $ Prelude.not $ isRec f
new <- newData n f
Eq <- teq (rep f a) (rep f new)
let l = COMP_LNS (List $ rep f a) (MAP_LNS INN_LNS) $ COMP_LNS (List $ rep (f :+!: K One) a) FILTER_LEFT_LNS (MAP_LNS OUT_LNS)
return $ View l $ List new
filterOne' _ = mzero
eitherSame :: RuleT
eitherSame (Either a a') = do
Eq <- teq a a'
return $ View (ID_LNS .\/<< ID_LNS) a
eitherSame _ = mzero
listList :: RuleT
listList (List (List a)) = return $ View CONCAT_LNS (List a)
listList _ = mzero
prodAssoc :: RuleT
prodAssoc (Prod (Prod a b) c) = return $ View ASSOCR_LNS (Prod a (Prod b c))
prodAssoc _ = mzero
sumAssoc :: RuleT
sumAssoc (Either (Either a b) c) = return $ View COASSOCR_LNS (Either a (Either b c))
sumAssoc _ = mzero