packages feed

pointfree-fancy 1.1.1.7 → 1.1.1.8

raw patch · 17 files changed

+1567/−1579 lines, 17 filesdep +pointfree-fancydep ~basedep ~containersPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: pointfree-fancy

Dependency ranges changed: base, containers

API changes (from Hackage documentation)

- Pointfree: pointfree :: String -> [String]
- Pointfree: pointfree' :: String -> Maybe String
+ Plugin.Pl.Common: (&&&) :: Arrow a => a b c -> a b c' -> a b (c, c')
+ Plugin.Pl.Common: (***) :: Arrow a => a b c -> a b' c' -> a (b, b') (c, c')
+ Plugin.Pl.Common: (+++) :: ArrowChoice a => a b c -> a b' c' -> a Either b b' Either c c'
+ Plugin.Pl.Common: (|||) :: ArrowChoice a => a b d -> a c d -> a Either b c d
+ Plugin.Pl.Common: App :: Expr -> Expr -> Expr
+ Plugin.Pl.Common: AssocLeft :: l -> Assoc l
+ Plugin.Pl.Common: AssocNone :: l -> Assoc l
+ Plugin.Pl.Common: AssocRight :: l -> Assoc l
+ Plugin.Pl.Common: Define :: String -> Expr -> Decl
+ Plugin.Pl.Common: Inf :: Fixity
+ Plugin.Pl.Common: Lambda :: Pattern -> Expr -> Expr
+ Plugin.Pl.Common: Let :: [Decl] -> Expr -> Expr
+ Plugin.Pl.Common: PCons :: Pattern -> Pattern -> Pattern
+ Plugin.Pl.Common: PTuple :: Pattern -> Pattern -> Pattern
+ Plugin.Pl.Common: PVar :: String -> Pattern
+ Plugin.Pl.Common: Pref :: Fixity
+ Plugin.Pl.Common: TLD :: Bool -> Decl -> TopLevel
+ Plugin.Pl.Common: TLE :: Expr -> TopLevel
+ Plugin.Pl.Common: Var :: Fixity -> String -> Expr
+ Plugin.Pl.Common: [declExpr] :: Decl -> Expr
+ Plugin.Pl.Common: [declName] :: Decl -> String
+ Plugin.Pl.Common: assert :: () => Bool -> a -> a
+ Plugin.Pl.Common: bt :: a
+ Plugin.Pl.Common: comp :: Expr
+ Plugin.Pl.Common: cons :: Expr
+ Plugin.Pl.Common: const' :: Expr
+ Plugin.Pl.Common: data Assoc l
+ Plugin.Pl.Common: data Decl
+ Plugin.Pl.Common: data Expr
+ Plugin.Pl.Common: data Fixity
+ Plugin.Pl.Common: data Pattern
+ Plugin.Pl.Common: data TopLevel
+ Plugin.Pl.Common: first :: Arrow a => a b c -> a (b, d) (c, d)
+ Plugin.Pl.Common: fix' :: Expr
+ Plugin.Pl.Common: flip' :: Expr
+ Plugin.Pl.Common: fromJust :: () => Maybe a -> a
+ Plugin.Pl.Common: fromMaybe :: () => a -> Maybe a -> a
+ Plugin.Pl.Common: getList :: Expr -> ([Expr], Expr)
+ Plugin.Pl.Common: id' :: Expr
+ Plugin.Pl.Common: if' :: Expr
+ Plugin.Pl.Common: infixr 2 +++
+ Plugin.Pl.Common: infixr 3 &&&
+ Plugin.Pl.Common: instance GHC.Classes.Eq Plugin.Pl.Common.Decl
+ Plugin.Pl.Common: instance GHC.Classes.Eq Plugin.Pl.Common.Expr
+ Plugin.Pl.Common: instance GHC.Classes.Eq Plugin.Pl.Common.Fixity
+ Plugin.Pl.Common: instance GHC.Classes.Eq Plugin.Pl.Common.Pattern
+ Plugin.Pl.Common: instance GHC.Classes.Eq Plugin.Pl.Common.TopLevel
+ Plugin.Pl.Common: instance GHC.Classes.Ord Plugin.Pl.Common.Decl
+ Plugin.Pl.Common: instance GHC.Classes.Ord Plugin.Pl.Common.Expr
+ Plugin.Pl.Common: instance GHC.Classes.Ord Plugin.Pl.Common.Fixity
+ Plugin.Pl.Common: instance GHC.Classes.Ord Plugin.Pl.Common.Pattern
+ Plugin.Pl.Common: instance GHC.Classes.Ord Plugin.Pl.Common.TopLevel
+ Plugin.Pl.Common: instance GHC.Show.Show Plugin.Pl.Common.Decl
+ Plugin.Pl.Common: instance GHC.Show.Show Plugin.Pl.Common.Expr
+ Plugin.Pl.Common: instance GHC.Show.Show Plugin.Pl.Common.Fixity
+ Plugin.Pl.Common: instance GHC.Show.Show Plugin.Pl.Common.Pattern
+ Plugin.Pl.Common: instance GHC.Show.Show Plugin.Pl.Common.TopLevel
+ Plugin.Pl.Common: intersperse :: () => a -> [a] -> [a]
+ Plugin.Pl.Common: isJust :: () => Maybe a -> Bool
+ Plugin.Pl.Common: lookupFix :: String -> (Assoc (), Int)
+ Plugin.Pl.Common: makeList :: [Expr] -> Expr
+ Plugin.Pl.Common: mapTopLevel :: (Expr -> Expr) -> TopLevel -> TopLevel
+ Plugin.Pl.Common: mapTopLevel' :: Functor f => (Expr -> f Expr) -> TopLevel -> f TopLevel
+ Plugin.Pl.Common: maxPrec :: Int
+ Plugin.Pl.Common: minPrec :: Int
+ Plugin.Pl.Common: minimumBy :: Foldable t => a -> a -> Ordering -> t a -> a
+ Plugin.Pl.Common: nil :: Expr
+ Plugin.Pl.Common: readM :: (Monad m, Read a) => String -> m a
+ Plugin.Pl.Common: scomb :: Expr
+ Plugin.Pl.Common: second :: Arrow a => a b c -> a (d, b) (d, c)
+ Plugin.Pl.Optimize: optimize :: Expr -> [Expr]
+ Plugin.Pl.Parser: parsePF :: String -> Either String TopLevel
+ Plugin.Pl.PrettyPrinter: instance GHC.Show.Show Plugin.Pl.PrettyPrinter.SExpr
+ Plugin.Pl.PrettyPrinter: prettyDecl :: Decl -> String
+ Plugin.Pl.PrettyPrinter: prettyExpr :: Expr -> String
+ Plugin.Pl.PrettyPrinter: prettyTopLevel :: TopLevel -> String
+ Plugin.Pl.Rules: CRR :: (Expr -> Maybe Expr) -> RewriteRule
+ Plugin.Pl.Rules: Down :: RewriteRule -> RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: Hard :: RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: If :: RewriteRule -> RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: Opt :: RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: Or :: [RewriteRule] -> RewriteRule
+ Plugin.Pl.Rules: OrElse :: RewriteRule -> RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: RR :: Rewrite -> Rewrite -> RewriteRule
+ Plugin.Pl.Rules: Then :: RewriteRule -> RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: Up :: RewriteRule -> RewriteRule -> RewriteRule
+ Plugin.Pl.Rules: data RewriteRule
+ Plugin.Pl.Rules: fire :: Rewrite -> Rewrite -> Expr -> Maybe Expr
+ Plugin.Pl.Rules: instance GHC.Classes.Eq Plugin.Pl.Rules.MExpr
+ Plugin.Pl.Rules: instance Plugin.Pl.Rules.RewriteC Plugin.Pl.Rules.MExpr
+ Plugin.Pl.Rules: instance Plugin.Pl.Rules.RewriteC a => Plugin.Pl.Rules.RewriteC (Plugin.Pl.Rules.MExpr -> a)
+ Plugin.Pl.Rules: rules :: RewriteRule
+ Plugin.Pl.Transform: transform :: Expr -> Expr

Files

Main.hs view
@@ -1,15 +1,15 @@ module Main where -import Plugin.Pl.Common-import Plugin.Pl.Optimize-import Plugin.Pl.Parser-import Plugin.Pl.PrettyPrinter-import Plugin.Pl.Transform--import System.Environment (getArgs)-import System.Console.GetOpt+import           Data.Foldable+import           Plugin.Pl.Common+import           Plugin.Pl.Optimize+import           Plugin.Pl.Parser+import           Plugin.Pl.PrettyPrinter+import           Plugin.Pl.Transform+import           System.Console.GetOpt+import           System.Environment      (getArgs) -data Flag = Verbose +data Flag = Verbose           | StdIn   deriving Eq @@ -25,7 +25,7 @@ parseArgs args =   case getOpt Permute options args of     (flags, nonOptions, []) -> return (flags, nonOptions)-    (_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))+    (_, _, errs) -> ioError (userError (fold errs ++ usageInfo header options))  getQuery :: [Flag] -> [String] -> IO String getQuery flags nonOptions
− Plugin/Pl/Common.hs
@@ -1,133 +0,0 @@-module Plugin.Pl.Common (-        Fixity(..), Expr(..), Pattern(..), Decl(..), TopLevel(..),-        bt, mapTopLevel, mapTopLevel',-        lookupFix, minPrec, maxPrec,-        comp, flip', id', const', scomb, cons, nil, fix', if', readM,-        makeList, getList,-        Assoc(..),-        module Data.Maybe,-        module Control.Arrow,-        module Data.List,-        module Control.Monad,-        module GHC.Base-    ) where--import           Data.List             (intersperse, minimumBy)-import qualified Data.Map              as M-import           Data.Maybe            (fromJust, fromMaybe, isJust)--import           Control.Arrow         (first, second, (&&&), (***), (+++),-                                        (|||))-import           Control.Monad--import           Language.Haskell.Exts (Assoc (..))--import           GHC.Base              (assert)----- The rewrite rules can be found at the end of the file Rules.hs---- Not sure if passing the information if it was used as infix or prefix--- is worth threading through the whole thing is worth the effort,--- but it stays that way until the prettyprinting algorithm gets more--- sophisticated.-data Fixity = Pref | Inf deriving Show--instance Eq Fixity where-  _ == _ = True--instance Ord Fixity where-  compare _ _ = EQ--data Expr-  = Var Fixity String-  | Lambda Pattern Expr-  | App Expr Expr-  | Let [Decl] Expr-  deriving (Eq, Ord, Show)--data Pattern-  = PVar String-  | PCons Pattern Pattern-  | PTuple Pattern Pattern-  deriving (Eq, Ord, Show)--data Decl = Define {-  declName :: String,-  declExpr :: Expr-} deriving (Eq, Ord, Show)--data TopLevel = TLD Bool Decl | TLE Expr deriving (Eq, Ord, Show)--mapTopLevel :: (Expr -> Expr) -> TopLevel -> TopLevel-mapTopLevel f tl = case getExpr tl of (e, c) -> c $ f e--mapTopLevel' :: Functor f => (Expr -> f Expr) -> TopLevel -> f TopLevel-mapTopLevel' f tl = case getExpr tl of (e, c) -> c <$> f e--getExpr :: TopLevel -> (Expr, Expr -> TopLevel)-getExpr (TLD True (Define foo e)) = (Let [Define foo e] (Var Pref foo),-                                     TLD False . Define foo)-getExpr (TLD False (Define foo e)) = (e, TLD False . Define foo)-getExpr (TLE e)      = (e, TLE)--comp, flip', id', const', scomb, cons, nil, fix', if' :: Expr-comp   = Var Inf  "."-flip'  = Var Pref "flip"-id'    = Var Pref "id"-const' = Var Pref "const"-scomb  = Var Pref "ap"-cons   = Var Inf  ":"-nil    = Var Pref "[]"-fix'   = Var Pref "fix"-if'    = Var Pref "if'"--makeList :: [Expr] -> Expr-makeList = foldr (\e1 e2 -> cons `App` e1 `App` e2) nil---- Modularity is a drag-getList :: Expr -> ([Expr], Expr)-getList (c `App` x `App` tl) | c == cons = first (x:) $ getList tl-getList e                    = ([],e)--bt :: a-bt = undefined--shift, minPrec, maxPrec :: Int-shift = 0-maxPrec = shift + 10-minPrec = 0---- operator precedences are needed both for parsing and prettyprinting-operators :: [[(String, (Assoc (), Int))]]-operators = (map . map . second . second $ (+shift))-  [[inf "." (AssocRight ()) 9, inf "!!" (AssocLeft ()) 9],-   [inf name (AssocRight ()) 8 | name <- ["^", "^^", "**"]],-   [inf name (AssocLeft ()) 7-     | name <- ["*", "/", "`quot`", "`rem`", "`div`", "`mod`", ":%", "%"]],-   [inf name (AssocLeft ()) 6  | name <- ["+", "-"]],-   [inf name (AssocRight ()) 5 | name <- [":", "++"]],-   [inf name (AssocNone ()) 4-     | name <- ["==", "/=", "<", "<=", ">=", ">", "`elem`", "`notElem`"]],-   [inf "&&" (AssocRight ()) 3],-   [inf "||" (AssocRight ()) 2],-   [inf ">>" (AssocLeft ()) 1, inf ">>=" (AssocLeft ()) 1, inf "=<<" (AssocRight ()) 1],-   [inf name (AssocRight ()) 0 | name <- ["$", "$!", "`seq`"]]-  ] where-  inf name assoc fx = (name, (assoc, fx))--opFM :: M.Map String (Assoc (), Int)-opFM = M.fromList $ join operators--lookupOp :: String -> Maybe (Assoc (), Int)-lookupOp k = M.lookup k opFM--lookupFix :: String -> (Assoc (), Int)-lookupFix str = fromMaybe (AssocLeft (), 9 + shift) (lookupOp str)--readM :: (Monad m, Read a) => String -> m a-readM s = case [x | (x,t) <- reads s, ("","")  <- lex t] of-            [x] -> return x-            []  -> fail "readM: No parse."-            _   -> fail "readM: Ambiguous parse."-
− Plugin/Pl/Optimize.hs
@@ -1,102 +0,0 @@-{-# LANGUAGE ImplicitParams #-}-module Plugin.Pl.Optimize (-    optimize,-  ) where--import           Plugin.Pl.Common-import           Plugin.Pl.PrettyPrinter (prettyExpr)-import           Plugin.Pl.Rules--import           Data.List               (nub)--cut :: [a] -> [a]-cut = take 1--toMonadPlus :: MonadPlus m => Maybe a -> m a-toMonadPlus Nothing  = mzero-toMonadPlus (Just x)=return x--type Size = Integer--- This seems to be a better size for our purposes,--- despite being "a little" slower because of the wasteful uglyprinting-sizeExpr' :: Expr -> Size-sizeExpr' e = 100 * fromIntegral (length $ prettyExpr e) + adjust e where-  -- hackish thing to favor some expressions if the length is the same:-  -- (+ x) --> (x +)-  -- x >>= f --> f =<< x-  -- f $ g x --> f (g x)-  adjust :: Expr -> Size-  adjust (Var _ str) -- Just n <- readM str = log (n*n+1) / 4-                     | str == "uncurry"    = -400---                     | str == "s"          = 500-                     | str == "flip"       = 10-                     | str == ">>="        = 5-                     | str == "$"          = 1-                     | str == "subtract"   = 1-                     | str == "ap"         = 200-                     | str == "liftM2"     = 101-                     | str == "return"     = -200-                     | str == "zipWith"    = -400-                     | str == "const"      = 0 -- -200-                     | str == "fmap"       = -100-  adjust (Lambda _ e') = adjust e'-  adjust (App e1 e2)  = adjust e1 + adjust e2-  adjust _ = 0--optimize :: Expr -> [Expr]-optimize e = result where-  result :: [Expr]-  result = map (snd . fromJust) . takeWhile isJust .-    iterate ((=<<) simpleStep) $ Just (sizeExpr' e, e)--  simpleStep :: (Size, Expr) -> Maybe (Size, Expr)-  simpleStep t = do-    let chn = let ?first = True in step (snd t)-        chnn = let ?first = False in step =<< chn-        new = filter (\(x,_) -> x < fst t) . map (sizeExpr' &&& id) $-                snd t: chn ++ chnn-    case new of-      []       -> Nothing-      (new':_) -> return new'--step :: (?first :: Bool) => Expr -> [Expr]-step e = nub $ rewrite rules e--rewrite :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]-rewrite rl e = case rl of-    Up r1 r2     -> let e'  = cut $ rewrite r1 e-                        e'' = rewrite r2 =<< e'-                    in if null e'' then e' else e''-    OrElse r1 r2 -> let e'  = rewrite r1 e-                    in if null e' then rewrite r2 e else e'-    Then r1 r2   -> rewrite r2 =<< nub (rewrite r1 e)-    Opt  r       -> e: rewrite r e-    If   p  r    -> if null (rewrite p e) then mzero else rewrite r e-    Hard r       -> if ?first then rewrite r e else mzero-    Or rs        -> (`rewrite` e) =<< rs-    RR {}        -> rewDeep rl e-    CRR {}       -> rewDeep rl e-    Down {}      -> rewDeep rl e--rewDeep :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]-rewDeep rule e = rew rule e `mplus` case e of-    Var _ _    -> mzero-    Lambda _ _ -> error "lambda: optimizer only works for closed expressions"-    Let _ _    -> error "let: optimizer only works for closed expressions"-    App e1 e2  -> ((`App` e2) `map` rewDeep rule e1) `mplus`-                  ((e1 `App`) `map` rewDeep rule e2)--rew :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]-rew (RR r1 r2) e = toMonadPlus $ fire r1 r2 e-rew (CRR r) e = toMonadPlus $ r e-rew (Or rs) e = (`rew` e) =<< rs-rew (Down r1 r2) e-  = if null e'' then e' else e'' where-    e'  = cut $ rew r1 e-    e'' = rewDeep r2 =<< e'-rew r@Then   {} e = rewrite r e-rew r@OrElse {} e = rewrite r e-rew r@Up     {} e = rewrite r e-rew r@Opt    {} e = rewrite r e-rew r@If     {} e = rewrite r e-rew r@Hard   {} e = rewrite r e
− Plugin/Pl/Parser.hs
@@ -1,92 +0,0 @@-module Plugin.Pl.Parser (parsePF) where--import           Plugin.Pl.Common--import qualified Language.Haskell.Exts as HSE--todo :: (Functor e, Show (e ())) => e a -> r-todo thing = error ("pointfree: not supported: " ++ show (void thing))--nameString :: HSE.Name a -> (Fixity, String)-nameString (HSE.Ident _ s)  = (Pref, s)-nameString (HSE.Symbol _ s) = (Inf, s)--qnameString :: HSE.QName a -> (Fixity, String)-qnameString (HSE.Qual _ m n) = fmap ((HSE.prettyPrint m ++ ".") ++) (nameString n)-qnameString (HSE.UnQual _ n) = nameString n-qnameString (HSE.Special _ sc) = case sc of-  HSE.UnitCon _              -> (Pref, "()")-  HSE.ListCon _              -> (Pref, "[]")-  HSE.FunCon _               -> (Inf, "->")-  HSE.TupleCon _ HSE.Boxed n -> (Inf, replicate (n-1) ',')-  HSE.TupleCon{}             -> todo sc-  HSE.Cons _                 -> (Inf, ":")-  HSE.UnboxedSingleCon{}     -> todo sc-  HSE.ExprHole{}             -> todo sc--opString :: HSE.QOp a -> (Fixity, String)-opString (HSE.QVarOp _ qn) = qnameString qn-opString (HSE.QConOp _ qn) = qnameString qn--list :: [Expr] -> Expr-list = foldr (\y ys -> cons `App` y `App` ys) nil--hseToExpr :: HSE.Exp a -> Expr-hseToExpr expr = case expr of-  HSE.Var _ qn -> uncurry Var (qnameString qn)-  HSE.IPVar{} -> todo expr-  HSE.Con _ qn -> uncurry Var (qnameString qn)-  HSE.Lit _ l -> case l of-    HSE.String _ _ s -> list (map (Var Pref . show) s)-    _                -> Var Pref (HSE.prettyPrint l)-  HSE.InfixApp _ p op q -> apps (Var Inf (snd (opString op))) [p,q]-  HSE.App _ f x -> hseToExpr f `App` hseToExpr x-  HSE.NegApp _ e -> Var Pref "negate" `App` hseToExpr e-  HSE.Lambda _ ps e -> foldr (Lambda . hseToPattern) (hseToExpr e) ps-  HSE.Let _ bs e -> case bs of-    HSE.BDecls _ ds   -> Let (map hseToDecl ds) (hseToExpr e)-    HSE.IPBinds _ ips -> todo ips-  HSE.If _ b t f -> apps if' [b,t,f]-  HSE.Case{} -> todo expr-  HSE.Do{} -> todo expr-  HSE.MDo{} -> todo expr-  HSE.Tuple _ HSE.Boxed es -> apps (Var Inf (replicate (length es - 1) ','))  es-  HSE.TupleSection{} -> todo expr-  HSE.List _ xs -> list (map hseToExpr xs)-  HSE.Paren _ e -> hseToExpr e-  HSE.LeftSection _ l op -> Var Inf (snd (opString op)) `App` hseToExpr l-  HSE.RightSection _ op r -> flip' `App` Var Inf (snd (opString op)) `App` hseToExpr r-  HSE.RecConstr{} -> todo expr-  HSE.RecUpdate{} -> todo expr-  HSE.EnumFrom _ x -> apps (Var Pref "enumFrom") [x]-  HSE.EnumFromTo _ x y -> apps (Var Pref "enumFromTo") [x,y]-  HSE.EnumFromThen _ x y -> apps (Var Pref "enumFromThen") [x,y]-  HSE.EnumFromThenTo _ x y z -> apps (Var Pref "enumFromThenTo") [x,y,z]-  _ -> todo expr--apps :: Expr -> [HSE.Exp a] -> Expr-apps = foldl (\a x -> a `App` hseToExpr x)--hseToDecl :: HSE.Decl a -> Decl-hseToDecl dec = case dec of-  HSE.PatBind _ (HSE.PVar _ n) (HSE.UnGuardedRhs _ e) Nothing ->-    Define (snd (nameString n)) (hseToExpr e)-  HSE.FunBind _ [HSE.Match _ n ps (HSE.UnGuardedRhs _ e) Nothing] ->-    Define (snd (nameString n)) (foldr (\p x -> Lambda (hseToPattern p) x) (hseToExpr e) ps)-  _ -> todo dec--hseToPattern :: HSE.Pat a -> Pattern-hseToPattern pat = case pat of-  HSE.PVar _ n -> PVar (snd (nameString n))-  HSE.PInfixApp _ l (HSE.Special _ (HSE.Cons _)) r -> PCons (hseToPattern l) (hseToPattern r)-  HSE.PTuple _ HSE.Boxed [p,q] -> PTuple (hseToPattern p) (hseToPattern q)-  HSE.PParen _ p -> hseToPattern p-  HSE.PWildCard _ -> PVar "_"-  _ -> todo pat--parsePF :: String -> Either String TopLevel-parsePF inp = case HSE.parseExp inp of-  HSE.ParseOk e -> Right (TLE (hseToExpr e))-  HSE.ParseFailed _ _ -> case HSE.parseDecl inp of-    HSE.ParseOk d         -> Right (TLD True (hseToDecl d))-    HSE.ParseFailed _ err -> Left err
− Plugin/Pl/PrettyPrinter.hs
@@ -1,146 +0,0 @@-{-# LANGUAGE PatternGuards #-}-module Plugin.Pl.PrettyPrinter (-  prettyDecl,-  prettyExpr,-  prettyTopLevel,- ) where--import           Plugin.Pl.Common--import           Data.Char-import           Data.List        (intercalate)--prettyDecl :: Decl -> String-prettyDecl (Define f e) = f ++ " = " ++ prettyExpr e--prettyDecls :: [Decl] -> String-prettyDecls = intercalate "; " . map prettyDecl--prettyExpr :: Expr -> String-prettyExpr = show . toSExpr--prettyTopLevel :: TopLevel -> String-prettyTopLevel (TLE e)   = prettyExpr e-prettyTopLevel (TLD _ d) = prettyDecl d--data SExpr-  = SVar !String-  | SLambda ![Pattern] !SExpr-  | SLet ![Decl] !SExpr-  | SApp !SExpr !SExpr-  | SInfix !String !SExpr !SExpr-  | LeftSection !String !SExpr  -- (x +)-  | RightSection !String !SExpr -- (+ x)-  | List ![SExpr]-  | Tuple ![SExpr]-  | Enum !Expr !(Maybe Expr) !(Maybe Expr)--{-# INLINE toSExprHead #-}-toSExprHead :: String -> [Expr] -> Maybe SExpr-toSExprHead hd tl-  | all (==',') hd, length hd+1 == length tl-  = Just . Tuple . reverse $ map toSExpr tl-  | otherwise = case (hd,reverse tl) of-      ("enumFrom", [e])              -> Just $ Enum e Nothing   Nothing-      ("enumFromThen", [e,e'])       -> Just $ Enum e (Just e') Nothing-      ("enumFromTo", [e,e'])         -> Just $ Enum e Nothing   (Just e')-      ("enumFromThenTo", [e,e',e'']) -> Just $ Enum e (Just e') (Just e'')-      _                              -> Nothing--toSExpr :: Expr -> SExpr-toSExpr (Var _ v) = SVar v-toSExpr (Lambda v e) = case toSExpr e of-  (SLambda vs e') -> SLambda (v:vs) e'-  e'              -> SLambda [v] e'-toSExpr (Let ds e) = SLet ds $ toSExpr e-toSExpr e | Just (hd,tl) <- getHead e, Just se <- toSExprHead hd tl = se-toSExpr e | (ls, tl) <- getList e, tl == nil-  = List $ map toSExpr ls-toSExpr (App e1 e2) = case e1 of-  App (Var Inf v) e0-    -> SInfix v (toSExpr e0) (toSExpr e2)-  Var Inf v | v /= "-"-    -> LeftSection v (toSExpr e2)--  Var _ "flip" | Var Inf v <- e2, v == "-" -> toSExpr $ Var Pref "subtract"--  App (Var _ "flip") (Var pr v)-    | v == "-"  -> toSExpr $ Var Pref "subtract" `App` e2-    | v == "id" -> RightSection "$" (toSExpr e2)-    | Inf <- pr, any (/= ',') v -> RightSection v (toSExpr e2)-  _ -> SApp (toSExpr e1) (toSExpr e2)--getHead :: Expr -> Maybe (String, [Expr])-getHead (Var _ v)   = Just (v, [])-getHead (App e1 e2) = second (e2:) `fmap` getHead e1-getHead _           = Nothing--instance Show SExpr where-  showsPrec _ (SVar v) = (getPrefName v ++)-  showsPrec p (SLambda vs e) = showParen (p > minPrec) $ ('\\':) .-    foldr (.) id (intersperse (' ':) (map (prettyPrecPattern $ maxPrec+1) vs)) .-    (" -> "++) . showsPrec minPrec e-  showsPrec p (SApp e1 e2) = showParen (p > maxPrec) $-    showsPrec maxPrec e1 . (' ':) . showsPrec (maxPrec+1) e2-  showsPrec _ (LeftSection fx e) = showParen True $-    showsPrec (snd (lookupFix fx) + 1) e . (' ':) . (getInfName fx++)-  showsPrec _ (RightSection fx e) = showParen True $-    (getInfName fx++) . (' ':) . showsPrec (snd (lookupFix fx) + 1) e-  showsPrec _ (Tuple es) = showParen True-    (join `id` intersperse ", " (map show es) ++)--  showsPrec _ (List es)-    | Just cs <- mapM ((=<<) readM . fromSVar) es = shows (cs::String)-    | otherwise = ('[':) .-      (join `id` intersperse ", " (map show es) ++) . (']':)-    where fromSVar (SVar str) = Just str-          fromSVar _          = Nothing-  showsPrec _ (Enum fr tn to) = ('[':) . showString (prettyExpr fr) .-    showsMaybe (((',':) . prettyExpr) `fmap` tn) . (".."++) .-    showsMaybe (prettyExpr `fmap` to) . (']':)-      where showsMaybe = maybe id (++)-  showsPrec _ (SLet ds e) = ("let "++) . showString (prettyDecls ds ++ " in ") . shows e---  showsPrec p (SInfix fx e1 e2) = showParen (p > fixity) $-    showsPrec f1 e1 . (' ':) . (getInfName fx++) . (' ':) .-    showsPrec f2 e2 where-      fixity = snd $ lookupFix fx-      (f1, f2) = case fst $ lookupFix fx of-        AssocRight _ -> (fixity+1, fixity + infixSafe e2 (AssocLeft ()) fixity)-        AssocLeft _ -> (fixity + infixSafe e1 (AssocRight ()) fixity, fixity+1)-        AssocNone _ -> (fixity+1, fixity+1)--      -- This is a little bit awkward, but at least seems to produce no false-      -- results anymore-      infixSafe :: SExpr -> Assoc () -> Int -> Int-      infixSafe (SInfix fx'' _ _) assoc fx'-        | lookupFix fx'' == (assoc, fx') = 1-        | otherwise = 0-      infixSafe _ _ _ = 0 -- doesn't matter--prettyPrecPattern :: Int -> Pattern -> ShowS-prettyPrecPattern _ (PVar v) = showString v-prettyPrecPattern _ (PTuple p1 p2) = showParen True $-  prettyPrecPattern 0 p1 . (", "++) . prettyPrecPattern 0 p2-prettyPrecPattern p (PCons p1 p2) = showParen (p>5) $-  prettyPrecPattern 6 p1 . (':':) . prettyPrecPattern 5 p2--isOperator :: String -> Bool-isOperator s =-  case break (== '.') s of-    (_, "") -> isUnqualOp s-    (before, _dot : rest)-      | isUnqualOp before -> isUnqualOp rest-      | isModule before -> isOperator rest-      | otherwise -> False-  where-    isModule "" = False-    isModule (c : cs) = isUpper c && all (\c' -> isAlphaNum c' || c' `elem` ['\'', '_']) cs-    isUnqualOp s' = s' /= "()" && all (\c -> isSymbol c || isPunctuation c) s'--getInfName :: String -> String-getInfName str = if isOperator str then str else "`"++str++"`"--getPrefName :: String -> String-getPrefName str = if isOperator str || ',' `elem` str then "("++str++")" else str
− Plugin/Pl/Rules.hs
@@ -1,875 +0,0 @@-{-# OPTIONS_GHC -fno-warn-missing-signatures #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleInstances         #-}-{-# LANGUAGE PatternGuards             #-}-{-# LANGUAGE ScopedTypeVariables       #-}------ | This marvellous module contributed by Thomas J\344ger----module Plugin.Pl.Rules (RewriteRule(..), rules, fire) where--import           Plugin.Pl.Common--import           Data.Array-import qualified Data.Set          as S--import           Control.Monad.Fix (fix)----import PlModule.PrettyPrinter---- Next time I do somthing like this, I'll actually think about the combinator--- language before, instead of producing something ad-hoc like this:-data RewriteRule-  = RR Rewrite Rewrite-  | CRR (Expr -> Maybe Expr)-  | Down RewriteRule RewriteRule-  | Up RewriteRule RewriteRule-  | Or [RewriteRule]-  | OrElse RewriteRule RewriteRule-  | Then RewriteRule RewriteRule-  | Opt RewriteRule-  | If RewriteRule RewriteRule-  | Hard RewriteRule---- No MLambda here because we only consider closed Terms (no alpha-renaming!).-data MExpr-  = MApp !MExpr !MExpr-  | Hole !Int-  | Quote !Expr-  deriving Eq----instance Show MExpr where---  show = show . fromMExpr--data Rewrite = Rewrite {-  holes :: MExpr,-  rid   :: Int -- rlength - 1-} --deriving Show---- What are you gonna do when no recursive modules are possible?-class RewriteC a where-  getRewrite :: a -> Rewrite--instance RewriteC MExpr where-  getRewrite rule = Rewrite {-    holes   = rule,-    rid = 0-  }--type ExprArr = Array Int Expr--myFire :: ExprArr -> MExpr -> MExpr-myFire xs (MApp e1 e2) = MApp (myFire xs e1) (myFire xs e2)-myFire xs (Hole h)     = Quote $ xs ! h-myFire _ me            = me--nub' :: Ord a => [a] -> [a]-nub' = S.toList . S.fromList--uniqueArray :: Ord v => Int -> [(Int, v)] -> Maybe (Array Int v)-uniqueArray n lst-  | length (nub' lst) == n = Just $ array (0,n-1) lst-  | otherwise = Nothing--match :: Rewrite -> Expr -> Maybe ExprArr-match (Rewrite hl rid') e  = uniqueArray rid' =<< matchWith hl e--fire' :: Rewrite -> ExprArr -> MExpr-fire' (Rewrite hl _)   = (`myFire` hl)--fire :: Rewrite -> Rewrite -> Expr -> Maybe Expr-fire r1 r2 e = (fromMExpr . fire' r2) `fmap` match r1 e--matchWith :: MExpr -> Expr -> Maybe [(Int, Expr)]-matchWith (MApp e1 e2) (App e1' e2') =-  liftM2 (++) (matchWith e1 e1') (matchWith e2 e2')-matchWith (Quote e) e' = if e == e' then Just [] else Nothing-matchWith (Hole k) e = Just [(k,e)]-matchWith _ _ = Nothing--fromMExpr :: MExpr -> Expr-fromMExpr (MApp e1 e2) = App (fromMExpr e1) (fromMExpr e2)-fromMExpr (Hole _)     = Var Pref "Hole" -- error "Hole in MExpr"-fromMExpr (Quote e)    = e--instance RewriteC a => RewriteC (MExpr -> a) where-  getRewrite rule = Rewrite {-    holes = holes . getRewrite . rule . Hole $ pid,-    rid   = pid + 1-  } where-    pid = rid $ getRewrite (bt :: a)---- Yet another pointless transformation-transformM :: Int -> MExpr -> MExpr-transformM _ (Quote e) = constE `a` Quote e-transformM n (Hole n') = if n == n' then idE else constE `a` Hole n'-transformM n (Quote (Var _ ".") `MApp` e1 `MApp` e2)-  | e1 `hasHole` n && not (e2 `hasHole` n)-  = flipE `a` compE `a` e2 `c` transformM n e1-transformM n e@(MApp e1 e2)-  | fr1 && fr2 = sE `a` transformM n e1 `a` transformM n e2-  | fr1        = flipE `a` transformM n e1 `a` e2-  | fr2, Hole n' <- e2, n' == n = e1-  | fr2        = e1 `c` transformM n e2-  | otherwise  = constE `a` e-  where-    fr1 = e1 `hasHole` n-    fr2 = e2 `hasHole` n--hasHole :: MExpr -> Int -> Bool-hasHole (MApp e1 e2) n = e1 `hasHole` n || e2 `hasHole` n-hasHole (Quote _)   _  = False-hasHole (Hole n')   n  = n == n'------- haddock doesn't like n+k patterns, so rewrite them----getVariants, getVariants' :: Rewrite -> [Rewrite]-getVariants' r@(Rewrite _ 0)  = [r]-getVariants' r@(Rewrite e nk)-    | nk >= 1    = r : getVariants (Rewrite e' (nk-1))-    | otherwise  = error "getVariants' : nk went negative"-    where-        e' = decHoles $ transformM 0 e--        decHoles (Hole n')    = Hole (n'-1)-        decHoles (MApp e1 e2) = decHoles e1 `MApp` decHoles e2-        decHoles me           = me--getVariants = getVariants' -- r = trace (show vs) vs where vs = getVariants' r--rr, rr0, rr1, rr2 :: RewriteC a => a -> a -> RewriteRule--- use this rewrite rule and rewrite rules derived from it by iterated--- pointless transformation-rrList :: RewriteC a => a -> a -> [RewriteRule]-rrList r1 r2 = zipWith RR (getVariants r1') (getVariants r2') where-  r1' = getRewrite r1-  r2' = getRewrite r2--rr  r1 r2 = Or          $ rrList r1 r2-rr1 r1 r2 = Or . take 2 $ rrList r1 r2-rr2 r1 r2 = Or . take 3 $ rrList r1 r2---- use only this rewrite rule-rr0 r1 r2 = RR r1' r2' where-  r1' = getRewrite r1-  r2' = getRewrite r2--down, up :: RewriteRule -> RewriteRule-down = fix . Down-up   = fix . Up---idE, flipE, bindE, extE, returnE, consE, appendE, nilE, foldrE, foldlE, fstE,-  sndE, dollarE, constE, uncurryE, curryE, compE, headE, tailE, sE, commaE,-  fixE, foldl1E, notE, equalsE, nequalsE, plusE, multE, zeroE, oneE, lengthE,-  sumE, productE, concatE, concatMapE, joinE, mapE, fmapE, fmapIE, subtractE,-  minusE, liftME, apE, liftM2E, seqME, zipE, zipWithE, onE, oedipusE, comp2E,-  crossE, firstE, secondE, andE, orE, allE, anyE :: MExpr-idE        = Quote $ Var Pref "id"-flipE      = Quote $ Var Pref "flip"-constE     = Quote $ Var Pref "const"-compE      = Quote $ Var Inf "."-comp2E     = Quote $ Var Inf ".*"-comp3E     = Quote $ Var Inf ".**"-eyeE       = Quote $ Var Inf "-."-oedipusE   = Quote $ Var Inf "-.*"-oedipus2E  = Quote $ Var Inf "-.**"-onE        = Quote $ Var Pref "on"-ampersandE = Quote $ Var Inf "&"-sE         = Quote $ Var Pref "ap"-fixE       = Quote $ Var Pref "fix"-bindE      = Quote $ Var Inf  ">>="-extE       = Quote $ Var Inf  "=<<"-returnE    = Quote $ Var Pref "return"-consE      = Quote $ Var Inf  ":"-nilE       = Quote $ Var Pref "[]"-appendE    = Quote $ Var Inf  "++"-foldrE     = Quote $ Var Pref "foldr"-foldlE     = Quote $ Var Pref "foldl"-fstE       = Quote $ Var Pref "fst"-sndE       = Quote $ Var Pref "snd"-dollarE    = Quote $ Var Inf  "$"-uncurryE   = Quote $ Var Pref "uncurry"-curryE     = Quote $ Var Pref "curry"-headE      = Quote $ Var Pref "head"-tailE      = Quote $ Var Pref "tail"-commaE     = Quote $ Var Inf  ","-foldl1E    = Quote $ Var Pref "foldl1"-equalsE    = Quote $ Var Inf  "=="-nequalsE   = Quote $ Var Inf  "/="-notE       = Quote $ Var Pref "not"-plusE      = Quote $ Var Inf  "+"-multE      = Quote $ Var Inf  "*"-zeroE      = Quote $ Var Pref "0"-oneE       = Quote $ Var Pref "1"-lengthE    = Quote $ Var Pref "length"-sumE       = Quote $ Var Pref "sum"-productE   = Quote $ Var Pref "product"-concatE    = Quote $ Var Pref "concat"-concatMapE = Quote $ Var Pref "concatMap"-joinE      = Quote $ Var Pref "join"-mapE       = Quote $ Var Pref "map"-fmapE      = Quote $ Var Pref "fmap"-fmapIE     = Quote $ Var Inf  "fmap"-subtractE  = Quote $ Var Pref "subtract"-minusE     = Quote $ Var Inf  "-"-liftME     = Quote $ Var Pref "liftM"-liftM2E    = Quote $ Var Pref "liftM2"-fishE      = Quote $ Var Inf  ">=>"-kliesliE   = Quote $ Var Inf "<=<"-apE        = Quote $ Var Inf  "ap"-seqME      = Quote $ Var Inf  ">>"-zipE       = Quote $ Var Pref "zip"-zipWithE   = Quote $ Var Pref "zipWith"-crossE     = Quote $ Var Inf  "***"-firstE     = Quote $ Var Pref "first"-secondE    = Quote $ Var Pref "second"-andE       = Quote $ Var Pref "and"-orE        = Quote $ Var Pref "or"-allE       = Quote $ Var Pref "all"-anyE       = Quote $ Var Pref "any"-replaceE   = Quote $ Var Inf "<$"-pointyE    = Quote $ Var Inf "$>"----a, c, c2 :: MExpr -> MExpr -> MExpr-a       = MApp-c e1 e2 = compE `a` e1 `a` e2-c2 e1 e2 = comp2E `a` e1 `a` e2-c3 e1 e2 = comp3E `a` e1 `a` e2-o e1 e2 = oedipusE `a` e1 `a` e2-o2 e1 e2 = oedipus2E `a` e1 `a` e2-eye e1 e2 = eyeE `a` e1 `a` e2-infixl 9 `a`-infixr 8 `c`-infixr 8 `c2`-infixr 8 `o`---collapseLists :: Expr -> Maybe Expr-collapseLists (Var _ "++" `App` e1 `App` e2)-  | (xs,x) <- getList e1, x==nil,-    (ys,y) <- getList e2, y==nil = Just $ makeList $ xs ++ ys-collapseLists _ = Nothing--data Binary = forall a b c. (Read a, Show a, Read b, Show b, Read c, Show c) => BA (a -> b -> c)--evalBinary :: [(String, Binary)] -> Expr -> Maybe Expr-evalBinary fs (Var _ f' `App` Var _ x' `App` Var _ y')-  | Just (BA f) <- lookup f' fs = (Var Pref . show) `fmap` liftM2 f (readM x') (readM y')-evalBinary _ _ = Nothing--data Unary = forall a b. (Read a, Show a, Read b, Show b) => UA (a -> b)--evalUnary :: [(String, Unary)] -> Expr -> Maybe Expr-evalUnary fs (Var _ f' `App` Var _ x')-  | Just (UA f) <- lookup f' fs = (Var Pref . show . f) `fmap` readM x'-evalUnary _ _ = Nothing--assocR, assocL, assoc :: [String] -> Expr -> Maybe Expr--- (f `op` g) `op` h --> f `op` (g `op` h)-assocR ops (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2) `App` e3)-  | op1 == op2 && op1 `elem` ops-  = Just (Var f1 op1 `App` e1 `App` (Var f2 op2 `App` e2 `App` e3))-assocR _ _ = Nothing---- f `op` (g `op` h) --> (f `op` g) `op` h-assocL ops (Var f1 op1 `App` e1 `App` (Var f2 op2 `App` e2 `App` e3))-  | op1 == op2 && op1 `elem` ops-  = Just (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2) `App` e3)-assocL _ _ = Nothing---- op f . op g --> op (f `op` g)-assoc ops (Var _ "." `App` (Var f1 op1 `App` e1) `App` (Var f2 op2 `App` e2))-  | op1 == op2 && op1 `elem` ops-  = Just (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2))-assoc _ _ = Nothing--commutative :: [String] -> Expr -> Maybe Expr-commutative ops (Var f op `App` e1 `App` e2)-  | op `elem` ops = Just (Var f op `App` e2 `App` e1)-commutative ops (Var _ "flip" `App` e@(Var _ op)) | op `elem` ops = Just e-commutative _ _ = Nothing---- TODO: Move rules into a file.-{-# INLINE simplifies #-}-simplifies :: RewriteRule-simplifies = Or [-  -- (f . g) x --> f (g x)-  rr0 (\f g x -> (f `c` g) `a` x)-      (\f g x -> f `a` (g `a` x)),-  -- (g -. f) -> f (g x)-  rr0 (\f g x -> (f `eye` g) `a` x)-      (\f g x -> f `a` (g `a` x)),-  -- (f .* g) x y -> f (g x y)-  rr0  (\f g x y -> (f `c2` g) `a` x `a` y)-       (\f g x y -> f `a` (g `a` x `a` y)),-  -- (f .** g) x y z -> f (g x y z)-  rr0 (\f g x y z -> (f `c3` g) `a` x `a` y `a` z)-      (\f g x y z -> f `a` (g `a` x `a` y `a` z)),-  -- (f -.* g) x y -> f x (g y)-  rr0  (\f g x y -> (f `o` g) `a` x `a` y)-       (\f g x y -> f `a` x `a` (g `a` y)),-  -- (f -.** g) -> f x y (g z)-  rr0 (\f g x y z -> (f `o2` g) `a` x `a` y `a` z)-      (\f g x y z -> f `a` x `a` y `a` (g `a` z)),-  -- x & f -> f x-  rr0 (\f x -> x `a` ampersandE `a` f)-      a,-  -- id x --> x-  rr0 (\x -> idE `a` x)-      id,-  -- flip (flip x) --> x-  rr  (\x -> flipE `a` (flipE `a` x))-      id,-  -- flip id x . f --> flip f x-  rr0 (\f x -> (flipE `a` idE `a` x) `c` f)-      (\f x -> flipE `a` f `a` x),-  -- id . f --> f-  rr0 (\f -> idE `c` f)-      id,-  -- f . id --> f-  rr0 (`c` idE)-      id,-  -- const x y --> x-  rr0 (\x y -> constE `a` x `a` y)-      const,-  -- not (not x) --> x-  rr  (\x -> notE `a` (notE `a` x))-      id,-  -- fst (x,y) --> x-  rr  (\x y -> fstE `a` (commaE `a` x `a` y))-      const,-  -- snd (x,y) --> y-  rr  (\x y -> sndE `a` (commaE `a` x `a` y))-      (\_ y -> y),-  -- head (x:xs) --> x-  rr  (\x xs -> headE `a` (consE `a` x `a` xs))-      const,-  -- tail (x:xs) --> xs-  rr  (\x xs -> tailE `a` (consE `a` x `a` xs))-      (\_ xs -> xs),-  -- uncurry f (x,y) --> f x y-  rr1 (\f x y -> uncurryE `a` f `a` (commaE `a` x `a` y))-      (\f x y -> f `a` x `a` y),-  -- uncurry (,) --> id-  rr  (uncurryE `a` commaE)-      idE,-  -- uncurry f . s (,) g --> s f g-  rr1 (\f g -> (uncurryE `a` f) `c` (sE `a` commaE `a` g))-      (\f g -> sE `a` f `a` g),-  -- curry fst --> const-  rr (curryE `a` fstE) constE,-  -- curry snd --> const id-  rr (curryE `a` sndE) (constE `a` idE),-  -- s f g x --> f x (g x)-  rr0 (\f g x -> sE `a` f `a` g `a` x)-      (\f g x -> f `a` x `a` (g `a` x)),-  -- flip f x y --> f y x-  rr0 (\f x y -> flipE `a` f `a` x `a` y)-      (\f x y -> f `a` y `a` x),-  -- flip (=<<) --> (>>=)-  rr0 (flipE `a` extE)-      bindE,--  -- TODO: Think about map/fmap-  -- fmap id --> id-  rr (fmapE `a` idE)-     idE,-  -- map id --> id-  rr (mapE `a` idE)-     idE,-  -- (f . g) . h --> f . (g . h)-  rr0 (\f g h -> (f `c` g) `c` h)-      (\f g h -> f `c` (g `c` h)),-  -- fmap f . fmap g -> fmap (f . g)-  rr0 (\f g -> fmapE `a` f `c` fmapE `a` g)-      (\f g -> fmapE `a` (f `c` g)),-  -- map f . map g -> map (f . g)-  rr0 (\f g -> mapE `a` f `c` mapE `a` g)-      (\f g -> mapE `a` (f `c` g))--  ]--onceRewrites :: RewriteRule-onceRewrites = Hard $ Or [-  -- ($) --> id-  rr0 dollarE-      idE,-  -- concatMap --> (=<<)-  rr concatMapE extE,-  -- concat    --> join-  rr concatE joinE,-  -- liftM --> fmap-  rr liftME fmapE,-  -- map --> fmap-  rr mapE fmapE,-  -- subtract -> flip (-)-  rr  subtractE-      (flipE `a` minusE)-  ]---- Now we can state rewrite rules in a nice high level way--- Rewrite rules should be as pointful as possible since the pointless variants--- will be derived automatically.-rules :: RewriteRule-rules = Or [-  -- f (g x) --> (f . g) x-  Hard $-  rr  (\f g x -> f `a` (g `a` x))-      (\f g x -> (f `c` g) `a` x),-  -- (>>=) --> flip (=<<)-  -- (>>=) --> flip (=<<)-  Hard $-  rr  bindE-      (flipE `a` extE),-  -- (.) id --> id-  rr (compE `a` idE)-     idE,-  -- (++) [x] --> (:) x-  rr  (\x -> appendE `a` (consE `a` x `a` nilE))-      (\x -> consE `a` x),-  -- (=<<) return --> id-  rr  (extE `a` returnE)-      idE,-  -- (=<<) f (return x) -> f x-  rr  (\f x -> extE `a` f `a` (returnE `a` x))-      a,-  -- (=<<) ((=<<) f . g) --> (=<<) f . (=<<) g-  rr  (\f g -> extE `a` ((extE `a` f) `c` g))-      (\f g -> (extE `a` f) `c` (extE `a` g)),-  -- flip (f . g) --> flip (.) g . flip f-  Hard $-  rr  (\f g -> flipE `a` (f `c` g))-      (\f g -> (flipE `a` compE `a` g) `c` (flipE `a` f)),-  -- flip (.) f . flip id --> flip f-  rr  (\f -> (flipE `a` compE `a` f) `c` (flipE `a` idE))-      (\f -> flipE `a` f),-  -- flip (.) f . flip flip --> flip (flip . f)-  rr  (\f -> (flipE `a` compE `a` f) `c` (flipE `a` flipE))-      (\f -> flipE `a` (flipE `c` f)),-  -- flip (flip (flip . f) g) --> flip (flip . flip f) g-  rr1 (\f g -> flipE `a` (flipE `a` (flipE `c` f) `a` g))-      (\f g -> flipE `a` (flipE `c` flipE `a` f) `a` g),--  -- flip (.) id --> id-  rr (flipE `a` compE `a` idE)-     idE,-  -- (.) . flip id --> flip flip-  rr  (compE `c` (flipE `a` idE))-      (flipE `a` flipE),-  -- s const x y --> y-  rr  (\x y -> sE `a` constE `a` x `a` y)-      (\_ y -> y),-  -- s (const . f) g --> f-  rr1 (\f g -> sE `a` (constE `c` f) `a` g)-      const,-  -- s (const f) --> (.) f-  rr  (\f -> sE `a` (constE `a` f))-      (\f -> compE `a` f),-  -- s (f . fst) snd --> uncurry f-  rr  (\f -> sE `a` (f `c` fstE) `a` sndE)-      (\f -> uncurryE `a` f),-  -- fst (join (,) x) --> x-  rr (\x -> fstE `a` (joinE `a` commaE `a` x))-     id,-  -- snd (join (,) x) --> x-  rr (\x -> sndE `a` (joinE `a` commaE `a` x))-     id,-  -- The next two are `simplifies', strictly speaking, but invoked rarely.-  -- uncurry f (x,y) --> f x y-  rr  (\f x y -> uncurryE `a` f `a` (commaE `a` x `a` y))-      (\f x y -> f `a` x `a` y),-  -- curry (uncurry f) --> f-  rr (\f -> curryE `a` (uncurryE `a` f))-     id,-  -- uncurry (curry f) --> f-  rr (\f -> uncurryE `a` (curryE `a` f))-     id,-  -- (const id . f) --> const id-  rr  (\f -> (constE `a` idE) `c` f)-      (\_ -> constE `a` idE),-  -- const x . f --> const x-  rr (\x f -> constE `a` x `c` f)-     (\x _ -> constE `a` x),-  -- fix f --> f (fix x)-  Hard $-  rr0 (\f -> fixE `a` f)-      (\f -> f `a` (fixE `a` f)),-  -- f (fix f) --> fix x-  Hard $-  rr0 (\f -> f `a` (fixE `a` f))-      (\f -> fixE `a` f),-  -- fix f --> f (f (fix x))-  Hard $-  rr0 (\f -> fixE `a` f)-      (\f -> f `a` (f `a` (fixE `a` f))),-  -- fix (const f) --> f-  rr (\f -> fixE `a` (constE `a` f))-     id,-  -- flip const x --> id-  rr  (\x -> flipE `a` constE `a` x)-      (pure idE),-  -- const . f --> flip (const f)-  Hard $-  rr  (\f -> constE `c` f)-      (\f -> flipE `a` (constE `a` f)),-  -- not (x == y) -> x /= y-  rr2 (\x y -> notE `a` (equalsE `a` x `a` y))-      (\x y -> nequalsE `a` x `a` y),-  -- not (x /= y) -> x == y-  rr2 (\x y -> notE `a` (nequalsE `a` x `a` y))-      (\x y -> equalsE `a` x `a` y),-  If (Or [rr plusE plusE, rr minusE minusE, rr multE multE]) $ down $ Or [-    -- 0 + x --> x-    rr  (\x -> plusE `a` zeroE `a` x)-        id,-    -- 0 * x --> 0-    rr  (\x -> multE `a` zeroE `a` x)-        (pure zeroE),-    -- 1 * x --> x-    rr  (\x -> multE `a` oneE `a` x)-        id,-    -- x - x --> 0-    rr  (\x -> minusE `a` x `a` x)-        (pure zeroE),-    -- x - y + y --> x-    rr  (\y x -> plusE `a` (minusE `a` x `a` y) `a` y)-        (\_ x -> x),-    -- x + y - y --> x-    rr  (\y x -> minusE `a` (plusE `a` x `a` y) `a` y)-        (\_ x -> x),-    -- x + (y - z) --> x + y - z-    rr  (\x y z -> plusE `a` x `a` (minusE `a` y `a` z))-        (\x y z -> minusE `a` (plusE `a` x `a` y) `a` z),-    -- x - (y + z) --> x - y - z-    rr  (\x y z -> minusE `a` x `a` (plusE `a` y `a` z))-        (\x y z -> minusE `a` (minusE `a` x `a` y) `a` z),-    -- x - (y - z) --> x + y - z-    rr  (\x y z -> minusE `a` x `a` (minusE `a` y `a` z))-        (\x y z -> minusE `a` (plusE `a` x `a` y) `a` z)-  ],--  -- flip ($) -> &-  rr (flipE `a` dollarE)-     ampersandE,--  -- fmap . const -> (<$)-  rr (fmapE `c` constE)-     replaceE,--  -- flip (<$) -> ($>)-  Hard $-  rr (flipE `a` replaceE)-     pointyE,--  Hard onceRewrites,-  -- join (fmap f x) --> f =<< x-  rr (\f x -> joinE `a` (fmapE `a` f `a` x))-     (\f x -> extE `a` f `a` x),-  -- (=<<) id --> join-  rr (extE `a` idE) joinE,-  -- join --> (=<<) id-  Hard $-  rr joinE (extE `a` idE),-  -- join (return x) --> x-  rr (\x -> joinE `a` (returnE `a` x))-     id,-  -- (return . f) =<< m --> fmap f m-  rr (\f m -> extE `a` (returnE `c` f) `a` m)-     (\f m -> fmapIE `a` f `a` m),-  -- (x >>=) . (return .) . f  --> flip (fmap . f) x-  rr (\f x -> bindE `a` x `c` (compE `a` returnE) `c` f)-     (\f x -> flipE `a` (fmapIE `c` f) `a` x),-  -- (>>=) (return f) --> flip id f-  rr (\f -> bindE `a` (returnE `a` f))-     (\f -> flipE `a` idE `a` f),-  -- liftM2 f x --> ap (f `fmap` x)-  Hard $-  rr (\f x -> liftM2E `a` f `a` x)-     (\f x -> apE `a` (fmapIE `a` f `a` x)),-  -- liftM2 f (return x) --> fmap (f x)-  rr (\f x -> liftM2E `a` f `a` (returnE `a` x))-     (\f x -> fmapIE `a` (f `a` x)),-  -- f `fmap` return x --> return (f x)-  rr (\f x -> fmapE `a` f `a` (returnE `a` x))-     (\f x -> returnE `a` (f `a` x)),-  -- (=<<) . flip (fmap . f) --> flip liftM2 f-  Hard $-  rr (\f -> extE `c` flipE `a` (fmapE `c` f))-     (\f -> flipE `a` liftM2E `a` f),--  -- ((f .) .) . g --> (f .** g)-  Hard $ rr (\f g -> (compE `a` (compE `a` f)) `c` g)-     (\f g -> (comp3E `a` f `a` g)),--  -- (f .) . g --> (f .* g)-  Hard $ rr (\f g -> (compE `a` f) `c` g)-     (\f g -> comp2E `a` f `a` g),--  -- flip (.) -> (-.)-  rr (flipE `a` compE)-     eyeE,--  -- (x &) -> ($ x)-  rr (\x -> ampersandE `a` x)-     (\x -> dollarE `a` x),--  -- f -.* (g . f) -> on g f-  Hard $-  rr (\f g -> oedipusE `a` f `a` (compE `a` g `a` f))-     (\f g -> onE `a` g `a` f),--  -- (.) -> fmap-  Hard $-  rr compE fmapE,--  -- join .* map x y -> (=<<) x y-  Hard $-  rr (joinE `c2` mapE) extE,--  -- map f (zip xs ys) --> zipWith (curry f) xs ys-  Hard $-  rr (\f xs ys -> mapE `a` f `a` (zipE `a` xs `a` ys))-     (\f xs ys -> zipWithE `a` (curryE `a` f) `a` xs `a` ys),--  -- zipWith (,) --> zip (,)-  rr (zipWithE `a` commaE) zipE,--  -- all f --> and . map f-  Hard $-  rr (\f -> allE `a` f)-     (\f -> andE `c` mapE `a` f),-  -- and . map f --> all f-  rr (\f -> andE `c` mapE `a` f)-     (\f -> allE `a` f),-  -- any f --> or . map f-  Hard $-  rr (\f -> anyE `a` f)-     (\f -> orE `c` mapE `a` f),-  -- or . map f --> any f-  rr (\f -> orE `c` mapE `a` f)-     (\f -> anyE `a` f),--  -- return f `ap` x --> fmap f x-  rr (\f x -> apE `a` (returnE `a` f) `a` x)-     (\f x -> fmapIE `a` f `a` x),-  -- ap (f `fmap` x) --> liftM2 f x-  rr (\f x -> apE `a` (fmapIE `a` f `a` x))-     (\f x -> liftM2E `a` f `a` x),-  -- f `ap` x --> (`fmap` x) =<< f-  Hard $-  rr (\f x -> apE `a` f `a` x)-     (\f x -> extE `a` (flipE `a` fmapIE `a` x) `a` f),-  -- (`fmap` x) =<< f --> f `ap` x-  rr (\f x -> extE `a` (flipE `a` fmapIE `a` x) `a` f)-     (\f x -> apE `a` f `a` x),-  -- (x >>=) . flip (fmap . f) -> liftM2 f x-  rr (\f x -> bindE `a` x `c` flipE `a` (fmapE `c` f))-     (\f x -> liftM2E `a` f `a` x),--  -- (f =<< m) x --> f (m x) x-  rr0 (\f m x -> extE `a` f `a` m `a` x)-      (\f m x -> f `a` (m `a` x) `a` x),-  -- (fmap f g x) --> f (g x)-  rr0 (\f g x -> fmapE `a` f `a` g `a` x)-      (\f g x -> f `a` (g `a` x)),-  -- return x y --> y-  rr  (\y x -> returnE `a` x `a` y)-      const,-  -- liftM2 f g h x --> g x `h` h x-  rr0 (\f g h x -> liftM2E `a` f `a` g `a` h `a` x)-      (\f g h x -> f `a` (g `a` x) `a` (h `a` x)),-  -- ap f id --> join f-  rr  (\f -> apE `a` f `a` idE)-      (\f -> joinE `a` f),--  -- flip flip f . ((.) .* g) --> f -.** g-  Hard $-  rr (\f g -> (flipE `a` flipE `a` f `c` (compE `c2` g)))-     (\f g -> oedipus2E `a` f `a` g),--  -- (. f) . g --> (f -.* g)-  Hard $-  rr (\f g -> (flipE `a` compE `a` f) `c` g)-     (\f g -> oedipusE `a` f `a` g),--  -- (=<<) const q --> flip (>>) q-  Hard $ -- ??-  rr (\q p -> extE `a` (constE `a` q) `a` p)-     (\q p -> seqME `a` p `a` q),-  -- p >> q --> const q =<< p-  Hard $-  rr (\p q -> seqME `a` p `a` q)-     (\p q -> extE `a` (constE `a` q) `a` p),--  -- ap (f -.* ((,) . f . fst)) snd --> f (***) f-  Hard $-  rr (\f -> apE `a` (f `o` (commaE `c` f `c` fstE)) `a` sndE)-     (\f -> joinE `a` crossE `a` f),--  -- flip (=<<) --> >>=-  rr (flipE `a` extE)-     bindE,--  -- flip (>=>) --> <=<-  rr (flipE `a` fishE)-     kliesliE,--  -- (.) . (=<<) --> <=<-  rr (compE `c` extE)-     kliesliE,--  -- join . (g .* f) --> f >=> g-  Hard $-    rr (\f g -> joinE `c` (g `c2` f))-       (\f g -> fishE `a` f `a` g),--  -- (`ap` snd) . (fst -.* (flip =<< (.) .* ((,) .))) --> join (***)-  -- Hard $-  rr ((flipE `a` apE `a` sndE) `c` (fstE `o` (extE `a` flipE `a` (compE `c2` (compE `a` commaE)))))-     (joinE `a` crossE),--  -- experimental support for Control.Arrow stuff-  -- (costs quite a bit of performace)-  -- uncurry ((. g) . (,) . f) --> f *** g-  rr (\f g -> uncurryE `a` ((flipE `a` compE `a` g) `c` commaE `c` f))-     (\f g -> crossE `a` f `a` g),-  -- uncurry ((,) . f) --> first f-  rr (\f -> uncurryE `a` (commaE `c` f))-     (\f -> firstE `a` f),-  -- uncurry ((. g) . (,)) --> second g-  rr (\g -> uncurryE `a` ((flipE `a` compE `a` g) `c` commaE))-     (\g -> secondE `a` g),-  -- I think we need all three of them:-  -- uncurry (const f) --> f . snd-  rr (\f -> uncurryE `a` (constE `a` f))-     (`c` sndE),-  -- uncurry const --> fst-  rr (uncurryE `a` constE)-     fstE,-  -- uncurry (const . f) --> f . fst-  rr (\f -> uncurryE `a` (constE `c` f))-     (`c` fstE),--  -- TODO is this the right place?-  -- [x] --> return x-  Hard $-  rr (\x -> consE `a` x `a` nilE)-     (\x -> returnE `a` x),-  -- list destructors-  Hard $-  If (Or [rr consE consE, rr nilE nilE]) $ Or [-    down $ Or [-      -- length [] --> 0-      rr (lengthE `a` nilE)-         zeroE,-      -- length (x:xs) --> 1 + length xs-      rr (\x xs -> lengthE `a` (consE `a` x `a` xs))-         (\_ xs -> plusE `a` oneE `a` (lengthE `a` xs))-    ],-    -- map/fmap elimination-    down $ Or [-      -- map f (x:xs) --> f x: map f xs-      rr (\f x xs -> mapE `a` f `a` (consE `a` x `a` xs))-         (\f x xs -> consE `a` (f `a` x) `a` (mapE `a` f `a` xs)),-      -- fmap f (x:xs) --> f x: Fmap f xs-      rr (\f x xs -> fmapE `a` f `a` (consE `a` x `a` xs))-         (\f x xs -> consE `a` (f `a` x) `a` (fmapE `a` f `a` xs)),-      -- map f []     --> []-      rr (\f -> mapE `a` f `a` nilE)-         (pure nilE),-      -- fmap f []     --> []-      rr (\f -> fmapE `a` f `a` nilE)-         (pure nilE)-    ],-    -- foldr elimination-    down $ Or [-      -- foldr f z (x:xs) --> f x (foldr f z xs)-      rr (\f x xs z -> (foldrE `a` f `a` z) `a` (consE `a` x `a` xs))-         (\f x xs z -> (f `a` x) `a` (foldrE `a` f `a` z `a` xs)),-      -- foldr f z [] --> z-      rr (\f z -> foldrE `a` f `a` z `a` nilE)-         (\_ z -> z)-    ],-    -- foldl elimination-    down $ Opt (CRR $ assocL ["."]) `Then` Or [-      -- sum xs --> foldl (+) 0 xs-      rr (\xs -> sumE `a` xs)-         (\xs -> foldlE `a` plusE `a` zeroE `a` xs),-      -- product xs --> foldl (*) 1 xs-      rr (\xs -> productE `a` xs)-         (\xs -> foldlE `a` multE `a` oneE `a` xs),-      -- foldl1 f (x:xs) --> foldl f x xs-      rr (\f x xs -> foldl1E `a` f `a` (consE `a` x `a` xs))-         (\f x xs -> foldlE `a` f `a` x `a` xs),-      -- foldl f z (x:xs) --> foldl f (f z x) xs-      rr (\f z x xs -> (foldlE `a` f `a` z) `a` (consE `a` x `a` xs))-         (\f z x xs -> foldlE `a` f `a` (f `a` z `a` x) `a` xs),-      -- foldl f z [] --> z-      rr (\f z -> foldlE `a` f `a` z `a` nilE)-         (\_ z -> z),-      -- special rule:-      -- foldl f z [x] --> f z x-      rr (\f z x -> foldlE `a` f `a` z `a` (returnE `a` x))-         (\f z x -> f `a` z `a` x),-      rr (\f z x -> foldlE `a` f `a` z `a` (consE `a` x `a` nilE))-         (\f z x -> f `a` z `a` x)-    ] `OrElse` (-      -- (:) x --> (++) [x]-      Opt (rr0 (\x -> consE `a` x)-         (\x -> appendE `a` (consE `a` x `a` nilE))) `Then`-      -- More special rule: (:) x . (++) ys --> (++) (x:ys)-      up (rr0 (\x ys -> (consE `a` x) `c` (appendE `a` ys))-         (\x ys -> appendE `a` (consE `a` x `a` ys)))-      )-  ],--  -- Complicated Transformations-  CRR collapseLists,-  up $ Or [CRR (evalUnary unaryBuiltins), CRR (evalBinary binaryBuiltins)],-  up $ CRR (assoc assocOps),-  up $ CRR (assocL assocOps),-  up $ CRR (assocR assocOps),-  Up (CRR (commutative commutativeOps)) $ down $ Or [CRR $ assocL assocLOps,-                                                     CRR $ assocR assocROps],--  Hard simplifies-  ] `Then` Opt (up simplifies)-assocLOps, assocROps, assocOps :: [String]-assocLOps = ["+", "*", "&&", "||", "max", "min"]-assocROps = [".", "++", ".*", ".**"]-assocOps  = assocLOps ++ assocROps--commutativeOps :: [String]-commutativeOps = ["*", "+", "==", "/=", "max", "min"]--unaryBuiltins :: [(String,Unary)]-unaryBuiltins = [-    ("not",    UA (not    :: Bool -> Bool)),-    ("negate", UA (negate :: Integer -> Integer)),-    ("signum", UA (signum :: Integer -> Integer)),-    ("abs",    UA (abs    :: Integer -> Integer))-  ]--binaryBuiltins :: [(String,Binary)]-binaryBuiltins = [-    ("+",    BA ((+)  :: Integer -> Integer -> Integer)),-    ("-",    BA ((-)  :: Integer -> Integer -> Integer)),-    ("*",    BA ((*)  :: Integer -> Integer -> Integer)),-    ("^",    BA ((^)  :: Integer -> Integer -> Integer)),-    ("<",    BA ((<)  :: Integer -> Integer -> Bool)),-    (">",    BA ((>)  :: Integer -> Integer -> Bool)),-    ("==",   BA ((==) :: Integer -> Integer -> Bool)),-    ("/=",   BA ((/=) :: Integer -> Integer -> Bool)),-    ("<=",   BA ((<=) :: Integer -> Integer -> Bool)),-    (">=",   BA ((>=) :: Integer -> Integer -> Bool)),-    ("div",  BA (div  :: Integer -> Integer -> Integer)),-    ("mod",  BA (mod  :: Integer -> Integer -> Integer)),-    ("max",  BA (max  :: Integer -> Integer -> Integer)),-    ("min",  BA (min  :: Integer -> Integer -> Integer)),-    ("&&",   BA ((&&) :: Bool -> Bool -> Bool)),-    ("||",   BA ((||) :: Bool -> Bool -> Bool))-  ]
− Plugin/Pl/Transform.hs
@@ -1,146 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE PatternGuards     #-}-module Plugin.Pl.Transform (-    transform,-  ) where--import           Control.Monad.Trans.State-import           Data.Graph                (flattenSCC, flattenSCCs,-                                            stronglyConnComp)-import qualified Data.Map                  as M-import           Data.Maybe-import           Plugin.Pl.Common--occursP :: String -> Pattern -> Bool-occursP v (PVar v')      = v == v'-occursP v (PTuple p1 p2) = v `occursP` p1 || v `occursP` p2-occursP v (PCons  p1 p2) = v `occursP` p1 || v `occursP` p2--freeIn :: String -> Expr -> Int-freeIn v (Var _ v') = fromEnum $ v == v'-freeIn v (Lambda pat e) = if v `occursP` pat then 0 else freeIn v e-freeIn v (App e1 e2) = freeIn v e1 + freeIn v e2-freeIn v (Let ds e') = if v `elem` map declName ds then 0-  else freeIn v e' + sum [freeIn v e | Define _ e <- ds]--isFreeIn :: String -> Expr -> Bool-isFreeIn v e = freeIn v e > 0--tuple :: [Expr] -> Expr-tuple  = foldr1 (\x y -> Var Inf "," `App` x `App` y)--tupleP :: [String] -> Pattern-tupleP vs = foldr1 PTuple $ PVar `map` vs--dependsOn :: [Decl] -> Decl -> [Decl]-dependsOn ds d = [d' | d' <- ds, declName d' `isFreeIn` declExpr d]--unLet :: Expr -> Expr-unLet (App e1 e2) = App (unLet e1) (unLet e2)-unLet (Let [] e) = unLet e-unLet (Let ds e) = unLet $-  Lambda (tupleP $ declName `map` dsYes) (Let dsNo e) `App`-    (fix' `App` Lambda (tupleP $ declName `map` dsYes)-                        (tuple  $ declExpr `map` dsYes))-    where-  comps = stronglyConnComp [(d',d',dependsOn ds d') | d' <- ds]-  dsYes = flattenSCC $ head comps-  dsNo = flattenSCCs $ tail comps--unLet (Lambda v e) = Lambda v (unLet e)-unLet (Var f x) = Var f x--type Env = M.Map String String---- It's a pity we still need that for the pointless transformation.--- Otherwise a newly created id/const/... could be bound by a lambda--- e.g. transform' (\id x -> x) ==> transform' (\id -> id) ==> id-alphaRename :: Expr -> Expr-alphaRename e = alpha e `evalState` M.empty where-  alpha :: Expr -> State Env Expr-  alpha (Var f v)     = Var f . fromMaybe v . M.lookup v <$> get-  alpha (App e1 e2)   = liftM2 App (alpha e1) (alpha e2)-  alpha (Let _ _)     = assert False bt-  alpha (Lambda v e') = inEnv $ liftM2 Lambda (alphaPat v) (alpha e')--  -- act like a reader monad-  inEnv :: State s a -> State s a-  inEnv f = gets $ evalState f--  alphaPat (PVar v) = do-    fm <- get-    let v' = "$" ++ show (M.size fm)-    put $ M.insert v v' fm-    return $ PVar v'-  alphaPat (PTuple p1 p2) = liftM2 PTuple (alphaPat p1) (alphaPat p2)-  alphaPat (PCons p1 p2) = liftM2 PCons (alphaPat p1) (alphaPat p2)---transform :: Expr -> Expr-transform = transform' . alphaRename . unLet---- Infinite generator of variable names.-varNames :: [String]-varNames = flip replicateM usableChars =<< [1..]-  where-    usableChars = ['a'..'z']---- First variable name not already in use-fresh :: [String] -> String-fresh variables = head . filter (not . flip elem variables) $ varNames--names :: Expr -> [String]-names (Var _ str)     = [str]--- Lambda pattern names are rewritten to be meaningless/unwritable, so we don't--- need to include them here. Variables from lambdas used in expressions are--- also rewritten, but there's no reason to special-case it unless it's provably--- poor-performing to scan over the result in `fresh`, which I doubt it is.-names (Lambda _ exp') = names exp'-names (App exp1 exp2) = names exp1 ++ names exp2-names (Let dlcs exp') = (dnames =<< dlcs) ++ names exp'-  where-    dnames (Define nm exp'') = nm : names exp''--transform' :: Expr -> Expr-transform' exp' = go exp'-  where-    -- Explicit sharing for readability-    vars = names exp'--    go Let {} =-      assert False bt-    go (Var f v) =-      Var f v-    go (App e1 e2) =-      App (go e1) (go e2)-    go (Lambda (PTuple p1 p2) e) =-      go $-        Lambda (PVar var) $ (Lambda p1 . Lambda p2 $ e) `App` f `App` s-      where-        var   = fresh vars-        f     = Var Pref "fst" `App` Var Pref var-        s     = Var Pref "snd" `App` Var Pref var-    go (Lambda (PCons p1 p2) e) =-      go $-        Lambda (PVar var) $ (Lambda p1 . Lambda p2 $ e) `App` f `App` s-      where-        var = fresh vars-        f   = Var Pref "head" `App` Var Pref var-        s   = Var Pref "tail" `App` Var Pref var-    go (Lambda (PVar v) e) =-      go $ getRidOfV e-      where-        getRidOfV (Var f v') | v == v'   = id'-                             | otherwise = const' `App` Var f v'-        getRidOfV l@(Lambda pat _) =-          assert (not $ v `occursP` pat) $ getRidOfV $ go l-        getRidOfV Let {} = assert False bt-        getRidOfV e'@(App e1 e2)-          | fr1 && fr2 = scomb `App` getRidOfV e1 `App` getRidOfV e2-          | fr1 = flip' `App` getRidOfV e1 `App` e2-          | Var _ v' <- e2, v' == v = e1-          | fr2 = comp `App` e1 `App` getRidOfV e2-          | otherwise = const' `App` e'-          where-            fr1 = v `isFreeIn` e1-            fr2 = v `isFreeIn` e2
− Pointfree.hs
@@ -1,31 +0,0 @@-module Pointfree where--import Plugin.Pl.Common (mapTopLevel, mapTopLevel')-import Plugin.Pl.Optimize (optimize)-import Plugin.Pl.Parser (parsePF)-import Plugin.Pl.PrettyPrinter (prettyTopLevel)-import Plugin.Pl.Transform (transform)--import Data.Maybe (listToMaybe)--{- |-  >>> pointfree "I'm not a valid Haskell expression!"-  []-  >>> pointfree "sum xs = foldr (+) 0 xs"-  ["sum = id (fix (const (foldr (+) 0)))","sum = fix (const (foldr (+) 0))","sum = foldr (+) 0"]--}-pointfree :: String -> [String]-pointfree-  = either-    (const [])-    (map prettyTopLevel . mapTopLevel' optimize . mapTopLevel transform)-  . parsePF--{- |-  >>> pointfree' "I'm not a valid Haskell expression!"-  Nothing-  >>> pointfree' "sum xs = foldr (+) 0 xs"-  Just "sum = foldr (+) 0"--}-pointfree' :: String -> Maybe String-pointfree' = listToMaybe . reverse . pointfree
+ lib/Pointfree.hs view
@@ -0,0 +1,30 @@+module Pointfree where++import           Data.Maybe              (listToMaybe)+import           Plugin.Pl.Common        (mapTopLevel, mapTopLevel')+import           Plugin.Pl.Optimize      (optimize)+import           Plugin.Pl.Parser        (parsePF)+import           Plugin.Pl.PrettyPrinter (prettyTopLevel)+import           Plugin.Pl.Transform     (transform)++{- |+  >>> pointfree "I'm not a valid Haskell expression!"+  []+  >>> pointfree "sum xs = foldr (+) 0 xs"+  ["sum = id (fix (const (foldr (+) 0)))","sum = fix (const (foldr (+) 0))","sum = foldr (+) 0"]+-}+pointfree :: String -> [String]+pointfree+  = either+    (pure [])+    (map prettyTopLevel . mapTopLevel' optimize . mapTopLevel transform)+  . parsePF++{- |+  >>> pointfree' "I'm not a valid Haskell expression!"+  Nothing+  >>> pointfree' "sum xs = foldr (+) 0 xs"+  Just "sum = foldr (+) 0"+-}+pointfree' :: String -> Maybe String+pointfree' = listToMaybe . reverse . pointfree
pointfree-fancy.cabal view
@@ -1,11 +1,12 @@-cabal-version: >=1.8+cabal-version: 2.0 name: pointfree-fancy-version: 1.1.1.7+version: 1.1.1.8 license: BSD3 license-file: LICENSE maintainer: Vanessa McHale <vamchale@gmail.com> author: Thomas Jäger-tested-with: ghc ==8.0.1 ghc ==8.2.2 ghc ==8.4.1 ghc ==7.10.3+tested-with: ghc ==8.0.2 ghc ==8.2.2 ghc ==8.4.3 ghc ==7.10.3+             ghc ==8.6.1 synopsis: Tool for refactoring expressions into pointfree form description:     The pointfree tool is a standalone command-line version of the pl@@ -23,55 +24,49 @@ library     exposed-modules:         Pointfree-    other-modules:+    hs-source-dirs: lib+    default-language: Haskell98+    ghc-options: -Wall+    build-depends:+        base -any,+        pointfree-internal -any++library pointfree-internal+    exposed-modules:         Plugin.Pl.Common         Plugin.Pl.Parser         Plugin.Pl.PrettyPrinter         Plugin.Pl.Optimize         Plugin.Pl.Rules         Plugin.Pl.Transform+    hs-source-dirs: src+    default-language: Haskell98+    other-extensions: FlexibleInstances PatternGuards ImplicitParams+                      ExistentialQuantification FlexibleInstances ScopedTypeVariables     ghc-options: -Wall     build-depends:-        base >=4.5 && <5.0,+        base >=4.8 && <5.0,         array >=0.3 && <0.6,-        containers >=0.4 && <0.6,+        containers >=0.4 && <0.7,         haskell-src-exts >=1.18 && <1.21,         transformers <0.6  executable pointfree     main-is: Main.hs-    other-modules:-        Plugin.Pl.Common-        Plugin.Pl.Parser-        Plugin.Pl.PrettyPrinter-        Plugin.Pl.Optimize-        Plugin.Pl.Rules-        Plugin.Pl.Transform+    default-language: Haskell98     ghc-options: -Wall     build-depends:         base >=4.3 && <5.0,-        array >=0.3 && <0.6,-        containers >=0.4 && <0.6,-        haskell-src-exts >=1.18 && <1.21,-        transformers <0.6+        pointfree-internal -any  test-suite tests     type: exitcode-stdio-1.0     main-is: Test.hs     hs-source-dirs: . test-    other-modules:-        Plugin.Pl.Common-        Plugin.Pl.Parser-        Plugin.Pl.PrettyPrinter-        Plugin.Pl.Optimize-        Plugin.Pl.Rules-        Plugin.Pl.Transform+    default-language: Haskell98     ghc-options: -Wall     build-depends:-        array >=0.3 && <0.6,         base <5,-        containers >=0.3 && <0.6,-        haskell-src-exts >=1.18 && <1.21,+        pointfree-internal -any,         HUnit >=1.1 && <1.7,-        QuickCheck >=2.1 && <2.11,-        transformers <0.6+        QuickCheck >=2.1 && <2.11
+ src/Plugin/Pl/Common.hs view
@@ -0,0 +1,130 @@+module Plugin.Pl.Common (+        Fixity(..), Expr(..), Pattern(..), Decl(..), TopLevel(..),+        bt, mapTopLevel, mapTopLevel',+        lookupFix, minPrec, maxPrec,+        comp, flip', id', const', scomb, cons, nil, fix', if', readM,+        makeList, getList,+        Assoc(..),+        module Data.Maybe,+        module Control.Arrow,+        module Data.List,+        module Control.Monad,+        module GHC.Base+    ) where++import           Control.Arrow         (first, second, (&&&), (***), (+++),+                                        (|||))+import           Control.Monad+import           Data.List             (intersperse, minimumBy)+import qualified Data.Map              as M+import           Data.Maybe            (fromJust, fromMaybe, isJust)+import           GHC.Base              (assert)+import           Language.Haskell.Exts (Assoc (..))+++-- The rewrite rules can be found at the end of the file Rules.hs++-- Not sure if passing the information if it was used as infix or prefix+-- is worth threading through the whole thing is worth the effort,+-- but it stays that way until the prettyprinting algorithm gets more+-- sophisticated.+data Fixity = Pref | Inf deriving Show++instance Eq Fixity where+  _ == _ = True++instance Ord Fixity where+  compare _ _ = EQ++data Expr+  = Var Fixity String+  | Lambda Pattern Expr+  | App Expr Expr+  | Let [Decl] Expr+  deriving (Eq, Ord, Show)++data Pattern+  = PVar String+  | PCons Pattern Pattern+  | PTuple Pattern Pattern+  deriving (Eq, Ord, Show)++data Decl = Define {+  declName :: String,+  declExpr :: Expr+} deriving (Eq, Ord, Show)++data TopLevel = TLD Bool Decl | TLE Expr deriving (Eq, Ord, Show)++mapTopLevel :: (Expr -> Expr) -> TopLevel -> TopLevel+mapTopLevel f tl = case getExpr tl of (e, c) -> c $ f e++mapTopLevel' :: Functor f => (Expr -> f Expr) -> TopLevel -> f TopLevel+mapTopLevel' f tl = case getExpr tl of (e, c) -> c <$> f e++getExpr :: TopLevel -> (Expr, Expr -> TopLevel)+getExpr (TLD True (Define foo e)) = (Let [Define foo e] (Var Pref foo),+                                     TLD False . Define foo)+getExpr (TLD False (Define foo e)) = (e, TLD False . Define foo)+getExpr (TLE e)      = (e, TLE)++comp, flip', id', const', scomb, cons, nil, fix', if' :: Expr+comp   = Var Inf  "."+flip'  = Var Pref "flip"+id'    = Var Pref "id"+const' = Var Pref "const"+scomb  = Var Pref "ap"+cons   = Var Inf  ":"+nil    = Var Pref "[]"+fix'   = Var Pref "fix"+if'    = Var Pref "if'"++makeList :: [Expr] -> Expr+makeList = foldr (\e1 e2 -> cons `App` e1 `App` e2) nil++-- Modularity is a drag+getList :: Expr -> ([Expr], Expr)+getList (c `App` x `App` tl) | c == cons = first (x:) $ getList tl+getList e                    = ([],e)++bt :: a+bt = undefined++shift, minPrec, maxPrec :: Int+shift = 0+maxPrec = shift + 10+minPrec = 0++-- operator precedences are needed both for parsing and prettyprinting+operators :: [[(String, (Assoc (), Int))]]+operators = (map . map . second . second $ (+shift))+  [[inf "." (AssocRight ()) 9, inf "!!" (AssocLeft ()) 9],+   [inf name (AssocRight ()) 8 | name <- ["^", "^^", "**"]],+   [inf name (AssocLeft ()) 7+     | name <- ["*", "/", "`quot`", "`rem`", "`div`", "`mod`", ":%", "%"]],+   [inf name (AssocLeft ()) 6  | name <- ["+", "-"]],+   [inf name (AssocRight ()) 5 | name <- [":", "++"]],+   [inf name (AssocNone ()) 4+     | name <- ["==", "/=", "<", "<=", ">=", ">", "`elem`", "`notElem`"]],+   [inf "&&" (AssocRight ()) 3],+   [inf "||" (AssocRight ()) 2],+   [inf ">>" (AssocLeft ()) 1, inf ">>=" (AssocLeft ()) 1, inf "=<<" (AssocRight ()) 1],+   [inf name (AssocRight ()) 0 | name <- ["$", "$!", "`seq`"]]+  ] where+  inf name assoc fx = (name, (assoc, fx))++opFM :: M.Map String (Assoc (), Int)+opFM = M.fromList $ join operators++lookupOp :: String -> Maybe (Assoc (), Int)+lookupOp k = M.lookup k opFM++lookupFix :: String -> (Assoc (), Int)+lookupFix str = fromMaybe (AssocLeft (), 9 + shift) (lookupOp str)++readM :: (Monad m, Read a) => String -> m a+readM s = case [x | (x,t) <- reads s, ("","")  <- lex t] of+            [x] -> return x+            []  -> fail "readM: No parse."+            _   -> fail "readM: Ambiguous parse."+
+ src/Plugin/Pl/Optimize.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE ImplicitParams #-}+module Plugin.Pl.Optimize (+    optimize,+  ) where++import           Plugin.Pl.Common+import           Plugin.Pl.PrettyPrinter (prettyExpr)+import           Plugin.Pl.Rules++import           Data.List               (nub)++cut :: [a] -> [a]+cut = take 1++toMonadPlus :: MonadPlus m => Maybe a -> m a+toMonadPlus Nothing  = mzero+toMonadPlus (Just x)=return x++type Size = Integer+-- This seems to be a better size for our purposes,+-- despite being "a little" slower because of the wasteful uglyprinting+sizeExpr' :: Expr -> Size+sizeExpr' e = 100 * fromIntegral (length $ prettyExpr e) + adjust e where+  -- hackish thing to favor some expressions if the length is the same:+  -- (+ x) --> (x +)+  -- x >>= f --> f =<< x+  -- f $ g x --> f (g x)+  adjust :: Expr -> Size+  adjust (Var _ str) -- Just n <- readM str = log (n*n+1) / 4+                     | str == "uncurry"    = -400+--                     | str == "s"          = 500+                     | str == "flip"       = 10+                     | str == ">>="        = 5+                     | str == "$"          = 1+                     | str == "subtract"   = 1+                     | str == "ap"         = 200+                     | str == "liftM2"     = 101+                     | str == "return"     = -200+                     | str == "zipWith"    = -400+                     | str == "const"      = 0 -- -200+                     | str == "fmap"       = -100+  adjust (Lambda _ e') = adjust e'+  adjust (App e1 e2)  = adjust e1 + adjust e2+  adjust _ = 0++optimize :: Expr -> [Expr]+optimize e = result where+  result :: [Expr]+  result = map (snd . fromJust) . takeWhile isJust .+    iterate ((=<<) simpleStep) $ Just (sizeExpr' e, e)++  simpleStep :: (Size, Expr) -> Maybe (Size, Expr)+  simpleStep t = do+    let chn = let ?first = True in step (snd t)+        chnn = let ?first = False in step =<< chn+        new = filter (\(x,_) -> x < fst t) . map (sizeExpr' &&& id) $+                snd t: chn ++ chnn+    case new of+      []       -> Nothing+      (new':_) -> return new'++step :: (?first :: Bool) => Expr -> [Expr]+step e = nub $ rewrite rules e++rewrite :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]+rewrite rl e = case rl of+    Up r1 r2     -> let e'  = cut $ rewrite r1 e+                        e'' = rewrite r2 =<< e'+                    in if null e'' then e' else e''+    OrElse r1 r2 -> let e'  = rewrite r1 e+                    in if null e' then rewrite r2 e else e'+    Then r1 r2   -> rewrite r2 =<< nub (rewrite r1 e)+    Opt  r       -> e: rewrite r e+    If   p  r    -> if null (rewrite p e) then mzero else rewrite r e+    Hard r       -> if ?first then rewrite r e else mzero+    Or rs        -> (`rewrite` e) =<< rs+    RR {}        -> rewDeep rl e+    CRR {}       -> rewDeep rl e+    Down {}      -> rewDeep rl e++rewDeep :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]+rewDeep rule e = rew rule e `mplus` case e of+    Var _ _    -> mzero+    Lambda _ _ -> error "lambda: optimizer only works for closed expressions"+    Let _ _    -> error "let: optimizer only works for closed expressions"+    App e1 e2  -> ((`App` e2) `map` rewDeep rule e1) `mplus`+                  ((e1 `App`) `map` rewDeep rule e2)++rew :: (?first :: Bool) => RewriteRule -> Expr -> [Expr]+rew (RR r1 r2) e = toMonadPlus $ fire r1 r2 e+rew (CRR r) e = toMonadPlus $ r e+rew (Or rs) e = (`rew` e) =<< rs+rew (Down r1 r2) e+  = if null e'' then e' else e'' where+    e'  = cut $ rew r1 e+    e'' = rewDeep r2 =<< e'+rew r@Then   {} e = rewrite r e+rew r@OrElse {} e = rewrite r e+rew r@Up     {} e = rewrite r e+rew r@Opt    {} e = rewrite r e+rew r@If     {} e = rewrite r e+rew r@Hard   {} e = rewrite r e
+ src/Plugin/Pl/Parser.hs view
@@ -0,0 +1,92 @@+module Plugin.Pl.Parser (parsePF) where++import           Plugin.Pl.Common++import qualified Language.Haskell.Exts as HSE++todo :: (Functor e, Show (e ())) => e a -> r+todo thing = error ("pointfree: not supported: " ++ show (void thing))++nameString :: HSE.Name a -> (Fixity, String)+nameString (HSE.Ident _ s)  = (Pref, s)+nameString (HSE.Symbol _ s) = (Inf, s)++qnameString :: HSE.QName a -> (Fixity, String)+qnameString (HSE.Qual _ m n) = fmap ((HSE.prettyPrint m ++ ".") ++) (nameString n)+qnameString (HSE.UnQual _ n) = nameString n+qnameString (HSE.Special _ sc) = case sc of+  HSE.UnitCon _              -> (Pref, "()")+  HSE.ListCon _              -> (Pref, "[]")+  HSE.FunCon _               -> (Inf, "->")+  HSE.TupleCon _ HSE.Boxed n -> (Inf, replicate (n-1) ',')+  HSE.TupleCon{}             -> todo sc+  HSE.Cons _                 -> (Inf, ":")+  HSE.UnboxedSingleCon{}     -> todo sc+  HSE.ExprHole{}             -> todo sc++opString :: HSE.QOp a -> (Fixity, String)+opString (HSE.QVarOp _ qn) = qnameString qn+opString (HSE.QConOp _ qn) = qnameString qn++list :: [Expr] -> Expr+list = foldr (\y ys -> cons `App` y `App` ys) nil++hseToExpr :: HSE.Exp a -> Expr+hseToExpr expr = case expr of+  HSE.Var _ qn -> uncurry Var (qnameString qn)+  HSE.IPVar{} -> todo expr+  HSE.Con _ qn -> uncurry Var (qnameString qn)+  HSE.Lit _ l -> case l of+    HSE.String _ _ s -> list (map (Var Pref . show) s)+    _                -> Var Pref (HSE.prettyPrint l)+  HSE.InfixApp _ p op q -> apps (Var Inf (snd (opString op))) [p,q]+  HSE.App _ f x -> hseToExpr f `App` hseToExpr x+  HSE.NegApp _ e -> Var Pref "negate" `App` hseToExpr e+  HSE.Lambda _ ps e -> foldr (Lambda . hseToPattern) (hseToExpr e) ps+  HSE.Let _ bs e -> case bs of+    HSE.BDecls _ ds   -> Let (map hseToDecl ds) (hseToExpr e)+    HSE.IPBinds _ ips -> todo ips+  HSE.If _ b t f -> apps if' [b,t,f]+  HSE.Case{} -> todo expr+  HSE.Do{} -> todo expr+  HSE.MDo{} -> todo expr+  HSE.Tuple _ HSE.Boxed es -> apps (Var Inf (replicate (length es - 1) ','))  es+  HSE.TupleSection{} -> todo expr+  HSE.List _ xs -> list (map hseToExpr xs)+  HSE.Paren _ e -> hseToExpr e+  HSE.LeftSection _ l op -> Var Inf (snd (opString op)) `App` hseToExpr l+  HSE.RightSection _ op r -> flip' `App` Var Inf (snd (opString op)) `App` hseToExpr r+  HSE.RecConstr{} -> todo expr+  HSE.RecUpdate{} -> todo expr+  HSE.EnumFrom _ x -> apps (Var Pref "enumFrom") [x]+  HSE.EnumFromTo _ x y -> apps (Var Pref "enumFromTo") [x,y]+  HSE.EnumFromThen _ x y -> apps (Var Pref "enumFromThen") [x,y]+  HSE.EnumFromThenTo _ x y z -> apps (Var Pref "enumFromThenTo") [x,y,z]+  _ -> todo expr++apps :: Expr -> [HSE.Exp a] -> Expr+apps = foldl (\a x -> a `App` hseToExpr x)++hseToDecl :: HSE.Decl a -> Decl+hseToDecl dec = case dec of+  HSE.PatBind _ (HSE.PVar _ n) (HSE.UnGuardedRhs _ e) Nothing ->+    Define (snd (nameString n)) (hseToExpr e)+  HSE.FunBind _ [HSE.Match _ n ps (HSE.UnGuardedRhs _ e) Nothing] ->+    Define (snd (nameString n)) (foldr (\p x -> Lambda (hseToPattern p) x) (hseToExpr e) ps)+  _ -> todo dec++hseToPattern :: HSE.Pat a -> Pattern+hseToPattern pat = case pat of+  HSE.PVar _ n -> PVar (snd (nameString n))+  HSE.PInfixApp _ l (HSE.Special _ (HSE.Cons _)) r -> PCons (hseToPattern l) (hseToPattern r)+  HSE.PTuple _ HSE.Boxed [p,q] -> PTuple (hseToPattern p) (hseToPattern q)+  HSE.PParen _ p -> hseToPattern p+  HSE.PWildCard _ -> PVar "_"+  _ -> todo pat++parsePF :: String -> Either String TopLevel+parsePF inp = case HSE.parseExp inp of+  HSE.ParseOk e -> Right (TLE (hseToExpr e))+  HSE.ParseFailed _ _ -> case HSE.parseDecl inp of+    HSE.ParseOk d         -> Right (TLD True (hseToDecl d))+    HSE.ParseFailed _ err -> Left err
+ src/Plugin/Pl/PrettyPrinter.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE PatternGuards #-}+module Plugin.Pl.PrettyPrinter (+  prettyDecl,+  prettyExpr,+  prettyTopLevel,+ ) where++import           Plugin.Pl.Common++import           Data.Char+import           Data.List        (intercalate)++prettyDecl :: Decl -> String+prettyDecl (Define f e) = f ++ " = " ++ prettyExpr e++prettyDecls :: [Decl] -> String+prettyDecls = intercalate "; " . map prettyDecl++prettyExpr :: Expr -> String+prettyExpr = show . toSExpr++prettyTopLevel :: TopLevel -> String+prettyTopLevel (TLE e)   = prettyExpr e+prettyTopLevel (TLD _ d) = prettyDecl d++data SExpr+  = SVar !String+  | SLambda ![Pattern] !SExpr+  | SLet ![Decl] !SExpr+  | SApp !SExpr !SExpr+  | SInfix !String !SExpr !SExpr+  | LeftSection !String !SExpr  -- (x +)+  | RightSection !String !SExpr -- (+ x)+  | List ![SExpr]+  | Tuple ![SExpr]+  | Enum !Expr !(Maybe Expr) !(Maybe Expr)++{-# INLINE toSExprHead #-}+toSExprHead :: String -> [Expr] -> Maybe SExpr+toSExprHead hd tl+  | all (==',') hd, length hd+1 == length tl+  = Just . Tuple . reverse $ map toSExpr tl+  | otherwise = case (hd,reverse tl) of+      ("enumFrom", [e])              -> Just $ Enum e Nothing   Nothing+      ("enumFromThen", [e,e'])       -> Just $ Enum e (Just e') Nothing+      ("enumFromTo", [e,e'])         -> Just $ Enum e Nothing   (Just e')+      ("enumFromThenTo", [e,e',e'']) -> Just $ Enum e (Just e') (Just e'')+      _                              -> Nothing++toSExpr :: Expr -> SExpr+toSExpr (Var _ v) = SVar v+toSExpr (Lambda v e) = case toSExpr e of+  (SLambda vs e') -> SLambda (v:vs) e'+  e'              -> SLambda [v] e'+toSExpr (Let ds e) = SLet ds $ toSExpr e+toSExpr e | Just (hd,tl) <- getHead e, Just se <- toSExprHead hd tl = se+toSExpr e | (ls, tl) <- getList e, tl == nil+  = List $ map toSExpr ls+toSExpr (App e1 e2) = case e1 of+  App (Var Inf v) e0+    -> SInfix v (toSExpr e0) (toSExpr e2)+  Var Inf v | v /= "-"+    -> LeftSection v (toSExpr e2)++  Var _ "flip" | Var Inf v <- e2, v == "-" -> toSExpr $ Var Pref "subtract"++  App (Var _ "flip") (Var pr v)+    | v == "-"  -> toSExpr $ Var Pref "subtract" `App` e2+    | v == "id" -> RightSection "$" (toSExpr e2)+    | Inf <- pr, any (/= ',') v -> RightSection v (toSExpr e2)+  _ -> SApp (toSExpr e1) (toSExpr e2)++getHead :: Expr -> Maybe (String, [Expr])+getHead (Var _ v)   = Just (v, [])+getHead (App e1 e2) = second (e2:) `fmap` getHead e1+getHead _           = Nothing++instance Show SExpr where+  showsPrec _ (SVar v) = (getPrefName v ++)+  showsPrec p (SLambda vs e) = showParen (p > minPrec) $ ('\\':) .+    foldr (.) id (intersperse (' ':) (map (prettyPrecPattern $ maxPrec+1) vs)) .+    (" -> "++) . showsPrec minPrec e+  showsPrec p (SApp e1 e2) = showParen (p > maxPrec) $+    showsPrec maxPrec e1 . (' ':) . showsPrec (maxPrec+1) e2+  showsPrec _ (LeftSection fx e) = showParen True $+    showsPrec (snd (lookupFix fx) + 1) e . (' ':) . (getInfName fx++)+  showsPrec _ (RightSection fx e) = showParen True $+    (getInfName fx++) . (' ':) . showsPrec (snd (lookupFix fx) + 1) e+  showsPrec _ (Tuple es) = showParen True+    (join `id` intersperse ", " (map show es) ++)++  showsPrec _ (List es)+    | Just cs <- mapM ((=<<) readM . fromSVar) es = shows (cs::String)+    | otherwise = ('[':) .+      (join `id` intersperse ", " (map show es) ++) . (']':)+    where fromSVar (SVar str) = Just str+          fromSVar _          = Nothing+  showsPrec _ (Enum fr tn to) = ('[':) . showString (prettyExpr fr) .+    showsMaybe (((',':) . prettyExpr) `fmap` tn) . (".."++) .+    showsMaybe (prettyExpr `fmap` to) . (']':)+      where showsMaybe = maybe id (++)+  showsPrec _ (SLet ds e) = ("let "++) . showString (prettyDecls ds ++ " in ") . shows e+++  showsPrec p (SInfix fx e1 e2) = showParen (p > fixity) $+    showsPrec f1 e1 . (' ':) . (getInfName fx++) . (' ':) .+    showsPrec f2 e2 where+      fixity = snd $ lookupFix fx+      (f1, f2) = case fst $ lookupFix fx of+        AssocRight _ -> (fixity+1, fixity + infixSafe e2 (AssocLeft ()) fixity)+        AssocLeft _ -> (fixity + infixSafe e1 (AssocRight ()) fixity, fixity+1)+        AssocNone _ -> (fixity+1, fixity+1)++      -- This is a little bit awkward, but at least seems to produce no false+      -- results anymore+      infixSafe :: SExpr -> Assoc () -> Int -> Int+      infixSafe (SInfix fx'' _ _) assoc fx'+        | lookupFix fx'' == (assoc, fx') = 1+        | otherwise = 0+      infixSafe _ _ _ = 0 -- doesn't matter++prettyPrecPattern :: Int -> Pattern -> ShowS+prettyPrecPattern _ (PVar v) = showString v+prettyPrecPattern _ (PTuple p1 p2) = showParen True $+  prettyPrecPattern 0 p1 . (", "++) . prettyPrecPattern 0 p2+prettyPrecPattern p (PCons p1 p2) = showParen (p>5) $+  prettyPrecPattern 6 p1 . (':':) . prettyPrecPattern 5 p2++isOperator :: String -> Bool+isOperator s =+  case break (== '.') s of+    (_, "") -> isUnqualOp s+    (before, _dot : rest)+      | isUnqualOp before -> isUnqualOp rest+      | isModule before -> isOperator rest+      | otherwise -> False+  where+    isModule "" = False+    isModule (c : cs) = isUpper c && all (\c' -> isAlphaNum c' || c' `elem` ['\'', '_']) cs+    isUnqualOp s' = s' /= "()" && all (\c -> isSymbol c || isPunctuation c) s'++getInfName :: String -> String+getInfName str = if isOperator str then str else "`"++str++"`"++getPrefName :: String -> String+getPrefName str = if isOperator str || ',' `elem` str then "("++str++")" else str
+ src/Plugin/Pl/Rules.hs view
@@ -0,0 +1,875 @@+{-# OPTIONS_GHC -fno-warn-missing-signatures #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleInstances         #-}+{-# LANGUAGE PatternGuards             #-}+{-# LANGUAGE ScopedTypeVariables       #-}+--+-- | This marvellous module contributed by Thomas J\344ger+--+module Plugin.Pl.Rules (RewriteRule(..), rules, fire) where++import           Plugin.Pl.Common++import           Data.Array+import qualified Data.Set          as S++import           Control.Monad.Fix (fix)++--import PlModule.PrettyPrinter++-- Next time I do somthing like this, I'll actually think about the combinator+-- language before, instead of producing something ad-hoc like this:+data RewriteRule+  = RR Rewrite Rewrite+  | CRR (Expr -> Maybe Expr)+  | Down RewriteRule RewriteRule+  | Up RewriteRule RewriteRule+  | Or [RewriteRule]+  | OrElse RewriteRule RewriteRule+  | Then RewriteRule RewriteRule+  | Opt RewriteRule+  | If RewriteRule RewriteRule+  | Hard RewriteRule++-- No MLambda here because we only consider closed Terms (no alpha-renaming!).+data MExpr+  = MApp !MExpr !MExpr+  | Hole !Int+  | Quote !Expr+  deriving Eq++--instance Show MExpr where+--  show = show . fromMExpr++data Rewrite = Rewrite {+  holes :: MExpr,+  rid   :: Int -- rlength - 1+} --deriving Show++-- What are you gonna do when no recursive modules are possible?+class RewriteC a where+  getRewrite :: a -> Rewrite++instance RewriteC MExpr where+  getRewrite rule = Rewrite {+    holes   = rule,+    rid = 0+  }++type ExprArr = Array Int Expr++myFire :: ExprArr -> MExpr -> MExpr+myFire xs (MApp e1 e2) = MApp (myFire xs e1) (myFire xs e2)+myFire xs (Hole h)     = Quote $ xs ! h+myFire _ me            = me++nub' :: Ord a => [a] -> [a]+nub' = S.toList . S.fromList++uniqueArray :: Ord v => Int -> [(Int, v)] -> Maybe (Array Int v)+uniqueArray n lst+  | length (nub' lst) == n = Just $ array (0,n-1) lst+  | otherwise = Nothing++match :: Rewrite -> Expr -> Maybe ExprArr+match (Rewrite hl rid') e  = uniqueArray rid' =<< matchWith hl e++fire' :: Rewrite -> ExprArr -> MExpr+fire' (Rewrite hl _)   = (`myFire` hl)++fire :: Rewrite -> Rewrite -> Expr -> Maybe Expr+fire r1 r2 e = (fromMExpr . fire' r2) `fmap` match r1 e++matchWith :: MExpr -> Expr -> Maybe [(Int, Expr)]+matchWith (MApp e1 e2) (App e1' e2') =+  liftM2 (++) (matchWith e1 e1') (matchWith e2 e2')+matchWith (Quote e) e' = if e == e' then Just [] else Nothing+matchWith (Hole k) e = Just [(k,e)]+matchWith _ _ = Nothing++fromMExpr :: MExpr -> Expr+fromMExpr (MApp e1 e2) = App (fromMExpr e1) (fromMExpr e2)+fromMExpr (Hole _)     = Var Pref "Hole" -- error "Hole in MExpr"+fromMExpr (Quote e)    = e++instance RewriteC a => RewriteC (MExpr -> a) where+  getRewrite rule = Rewrite {+    holes = holes . getRewrite . rule . Hole $ pid,+    rid   = pid + 1+  } where+    pid = rid $ getRewrite (bt :: a)++-- Yet another pointless transformation+transformM :: Int -> MExpr -> MExpr+transformM _ (Quote e) = constE `a` Quote e+transformM n (Hole n') = if n == n' then idE else constE `a` Hole n'+transformM n (Quote (Var _ ".") `MApp` e1 `MApp` e2)+  | e1 `hasHole` n && not (e2 `hasHole` n)+  = flipE `a` compE `a` e2 `c` transformM n e1+transformM n e@(MApp e1 e2)+  | fr1 && fr2 = sE `a` transformM n e1 `a` transformM n e2+  | fr1        = flipE `a` transformM n e1 `a` e2+  | fr2, Hole n' <- e2, n' == n = e1+  | fr2        = e1 `c` transformM n e2+  | otherwise  = constE `a` e+  where+    fr1 = e1 `hasHole` n+    fr2 = e2 `hasHole` n++hasHole :: MExpr -> Int -> Bool+hasHole (MApp e1 e2) n = e1 `hasHole` n || e2 `hasHole` n+hasHole (Quote _)   _  = False+hasHole (Hole n')   n  = n == n'++--+-- haddock doesn't like n+k patterns, so rewrite them+--+getVariants, getVariants' :: Rewrite -> [Rewrite]+getVariants' r@(Rewrite _ 0)  = [r]+getVariants' r@(Rewrite e nk)+    | nk >= 1    = r : getVariants (Rewrite e' (nk-1))+    | otherwise  = error "getVariants' : nk went negative"+    where+        e' = decHoles $ transformM 0 e++        decHoles (Hole n')    = Hole (n'-1)+        decHoles (MApp e1 e2) = decHoles e1 `MApp` decHoles e2+        decHoles me           = me++getVariants = getVariants' -- r = trace (show vs) vs where vs = getVariants' r++rr, rr0, rr1, rr2 :: RewriteC a => a -> a -> RewriteRule+-- use this rewrite rule and rewrite rules derived from it by iterated+-- pointless transformation+rrList :: RewriteC a => a -> a -> [RewriteRule]+rrList r1 r2 = zipWith RR (getVariants r1') (getVariants r2') where+  r1' = getRewrite r1+  r2' = getRewrite r2++rr  r1 r2 = Or          $ rrList r1 r2+rr1 r1 r2 = Or . take 2 $ rrList r1 r2+rr2 r1 r2 = Or . take 3 $ rrList r1 r2++-- use only this rewrite rule+rr0 r1 r2 = RR r1' r2' where+  r1' = getRewrite r1+  r2' = getRewrite r2++down, up :: RewriteRule -> RewriteRule+down = fix . Down+up   = fix . Up+++idE, flipE, bindE, extE, returnE, consE, appendE, nilE, foldrE, foldlE, fstE,+  sndE, dollarE, constE, uncurryE, curryE, compE, headE, tailE, sE, commaE,+  fixE, foldl1E, notE, equalsE, nequalsE, plusE, multE, zeroE, oneE, lengthE,+  sumE, productE, concatE, concatMapE, joinE, mapE, fmapE, fmapIE, subtractE,+  minusE, liftME, apE, liftM2E, seqME, zipE, zipWithE, onE, oedipusE, comp2E,+  crossE, firstE, secondE, andE, orE, allE, anyE :: MExpr+idE        = Quote $ Var Pref "id"+flipE      = Quote $ Var Pref "flip"+constE     = Quote $ Var Pref "const"+compE      = Quote $ Var Inf "."+comp2E     = Quote $ Var Inf ".*"+comp3E     = Quote $ Var Inf ".**"+eyeE       = Quote $ Var Inf "-."+oedipusE   = Quote $ Var Inf "-.*"+oedipus2E  = Quote $ Var Inf "-.**"+onE        = Quote $ Var Pref "on"+ampersandE = Quote $ Var Inf "&"+sE         = Quote $ Var Pref "ap"+fixE       = Quote $ Var Pref "fix"+bindE      = Quote $ Var Inf  ">>="+extE       = Quote $ Var Inf  "=<<"+returnE    = Quote $ Var Pref "return"+consE      = Quote $ Var Inf  ":"+nilE       = Quote $ Var Pref "[]"+appendE    = Quote $ Var Inf  "++"+foldrE     = Quote $ Var Pref "foldr"+foldlE     = Quote $ Var Pref "foldl"+fstE       = Quote $ Var Pref "fst"+sndE       = Quote $ Var Pref "snd"+dollarE    = Quote $ Var Inf  "$"+uncurryE   = Quote $ Var Pref "uncurry"+curryE     = Quote $ Var Pref "curry"+headE      = Quote $ Var Pref "head"+tailE      = Quote $ Var Pref "tail"+commaE     = Quote $ Var Inf  ","+foldl1E    = Quote $ Var Pref "foldl1"+equalsE    = Quote $ Var Inf  "=="+nequalsE   = Quote $ Var Inf  "/="+notE       = Quote $ Var Pref "not"+plusE      = Quote $ Var Inf  "+"+multE      = Quote $ Var Inf  "*"+zeroE      = Quote $ Var Pref "0"+oneE       = Quote $ Var Pref "1"+lengthE    = Quote $ Var Pref "length"+sumE       = Quote $ Var Pref "sum"+productE   = Quote $ Var Pref "product"+concatE    = Quote $ Var Pref "concat"+concatMapE = Quote $ Var Pref "concatMap"+joinE      = Quote $ Var Pref "join"+mapE       = Quote $ Var Pref "map"+fmapE      = Quote $ Var Pref "fmap"+fmapIE     = Quote $ Var Inf  "fmap"+subtractE  = Quote $ Var Pref "subtract"+minusE     = Quote $ Var Inf  "-"+liftME     = Quote $ Var Pref "liftM"+liftM2E    = Quote $ Var Pref "liftM2"+fishE      = Quote $ Var Inf  ">=>"+kliesliE   = Quote $ Var Inf "<=<"+apE        = Quote $ Var Inf  "ap"+seqME      = Quote $ Var Inf  ">>"+zipE       = Quote $ Var Pref "zip"+zipWithE   = Quote $ Var Pref "zipWith"+crossE     = Quote $ Var Inf  "***"+firstE     = Quote $ Var Pref "first"+secondE    = Quote $ Var Pref "second"+andE       = Quote $ Var Pref "and"+orE        = Quote $ Var Pref "or"+allE       = Quote $ Var Pref "all"+anyE       = Quote $ Var Pref "any"+replaceE   = Quote $ Var Inf "<$"+pointyE    = Quote $ Var Inf "$>"++++a, c, c2 :: MExpr -> MExpr -> MExpr+a       = MApp+c e1 e2 = compE `a` e1 `a` e2+c2 e1 e2 = comp2E `a` e1 `a` e2+c3 e1 e2 = comp3E `a` e1 `a` e2+o e1 e2 = oedipusE `a` e1 `a` e2+o2 e1 e2 = oedipus2E `a` e1 `a` e2+eye e1 e2 = eyeE `a` e1 `a` e2+infixl 9 `a`+infixr 8 `c`+infixr 8 `c2`+infixr 8 `o`+++collapseLists :: Expr -> Maybe Expr+collapseLists (Var _ "++" `App` e1 `App` e2)+  | (xs,x) <- getList e1, x==nil,+    (ys,y) <- getList e2, y==nil = Just $ makeList $ xs ++ ys+collapseLists _ = Nothing++data Binary = forall a b c. (Read a, Show a, Read b, Show b, Read c, Show c) => BA (a -> b -> c)++evalBinary :: [(String, Binary)] -> Expr -> Maybe Expr+evalBinary fs (Var _ f' `App` Var _ x' `App` Var _ y')+  | Just (BA f) <- lookup f' fs = (Var Pref . show) `fmap` liftM2 f (readM x') (readM y')+evalBinary _ _ = Nothing++data Unary = forall a b. (Read a, Show a, Read b, Show b) => UA (a -> b)++evalUnary :: [(String, Unary)] -> Expr -> Maybe Expr+evalUnary fs (Var _ f' `App` Var _ x')+  | Just (UA f) <- lookup f' fs = (Var Pref . show . f) `fmap` readM x'+evalUnary _ _ = Nothing++assocR, assocL, assoc :: [String] -> Expr -> Maybe Expr+-- (f `op` g) `op` h --> f `op` (g `op` h)+assocR ops (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2) `App` e3)+  | op1 == op2 && op1 `elem` ops+  = Just (Var f1 op1 `App` e1 `App` (Var f2 op2 `App` e2 `App` e3))+assocR _ _ = Nothing++-- f `op` (g `op` h) --> (f `op` g) `op` h+assocL ops (Var f1 op1 `App` e1 `App` (Var f2 op2 `App` e2 `App` e3))+  | op1 == op2 && op1 `elem` ops+  = Just (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2) `App` e3)+assocL _ _ = Nothing++-- op f . op g --> op (f `op` g)+assoc ops (Var _ "." `App` (Var f1 op1 `App` e1) `App` (Var f2 op2 `App` e2))+  | op1 == op2 && op1 `elem` ops+  = Just (Var f1 op1 `App` (Var f2 op2 `App` e1 `App` e2))+assoc _ _ = Nothing++commutative :: [String] -> Expr -> Maybe Expr+commutative ops (Var f op `App` e1 `App` e2)+  | op `elem` ops = Just (Var f op `App` e2 `App` e1)+commutative ops (Var _ "flip" `App` e@(Var _ op)) | op `elem` ops = Just e+commutative _ _ = Nothing++-- TODO: Move rules into a file.+{-# INLINE simplifies #-}+simplifies :: RewriteRule+simplifies = Or [+  -- (f . g) x --> f (g x)+  rr0 (\f g x -> (f `c` g) `a` x)+      (\f g x -> f `a` (g `a` x)),+  -- (g -. f) -> f (g x)+  rr0 (\f g x -> (f `eye` g) `a` x)+      (\f g x -> f `a` (g `a` x)),+  -- (f .* g) x y -> f (g x y)+  rr0  (\f g x y -> (f `c2` g) `a` x `a` y)+       (\f g x y -> f `a` (g `a` x `a` y)),+  -- (f .** g) x y z -> f (g x y z)+  rr0 (\f g x y z -> (f `c3` g) `a` x `a` y `a` z)+      (\f g x y z -> f `a` (g `a` x `a` y `a` z)),+  -- (f -.* g) x y -> f x (g y)+  rr0  (\f g x y -> (f `o` g) `a` x `a` y)+       (\f g x y -> f `a` x `a` (g `a` y)),+  -- (f -.** g) -> f x y (g z)+  rr0 (\f g x y z -> (f `o2` g) `a` x `a` y `a` z)+      (\f g x y z -> f `a` x `a` y `a` (g `a` z)),+  -- x & f -> f x+  rr0 (\f x -> x `a` ampersandE `a` f)+      a,+  -- id x --> x+  rr0 (\x -> idE `a` x)+      id,+  -- flip (flip x) --> x+  rr  (\x -> flipE `a` (flipE `a` x))+      id,+  -- flip id x . f --> flip f x+  rr0 (\f x -> (flipE `a` idE `a` x) `c` f)+      (\f x -> flipE `a` f `a` x),+  -- id . f --> f+  rr0 (\f -> idE `c` f)+      id,+  -- f . id --> f+  rr0 (`c` idE)+      id,+  -- const x y --> x+  rr0 (\x y -> constE `a` x `a` y)+      const,+  -- not (not x) --> x+  rr  (\x -> notE `a` (notE `a` x))+      id,+  -- fst (x,y) --> x+  rr  (\x y -> fstE `a` (commaE `a` x `a` y))+      const,+  -- snd (x,y) --> y+  rr  (\x y -> sndE `a` (commaE `a` x `a` y))+      (\_ y -> y),+  -- head (x:xs) --> x+  rr  (\x xs -> headE `a` (consE `a` x `a` xs))+      const,+  -- tail (x:xs) --> xs+  rr  (\x xs -> tailE `a` (consE `a` x `a` xs))+      (\_ xs -> xs),+  -- uncurry f (x,y) --> f x y+  rr1 (\f x y -> uncurryE `a` f `a` (commaE `a` x `a` y))+      (\f x y -> f `a` x `a` y),+  -- uncurry (,) --> id+  rr  (uncurryE `a` commaE)+      idE,+  -- uncurry f . s (,) g --> s f g+  rr1 (\f g -> (uncurryE `a` f) `c` (sE `a` commaE `a` g))+      (\f g -> sE `a` f `a` g),+  -- curry fst --> const+  rr (curryE `a` fstE) constE,+  -- curry snd --> const id+  rr (curryE `a` sndE) (constE `a` idE),+  -- s f g x --> f x (g x)+  rr0 (\f g x -> sE `a` f `a` g `a` x)+      (\f g x -> f `a` x `a` (g `a` x)),+  -- flip f x y --> f y x+  rr0 (\f x y -> flipE `a` f `a` x `a` y)+      (\f x y -> f `a` y `a` x),+  -- flip (=<<) --> (>>=)+  rr0 (flipE `a` extE)+      bindE,++  -- TODO: Think about map/fmap+  -- fmap id --> id+  rr (fmapE `a` idE)+     idE,+  -- map id --> id+  rr (mapE `a` idE)+     idE,+  -- (f . g) . h --> f . (g . h)+  rr0 (\f g h -> (f `c` g) `c` h)+      (\f g h -> f `c` (g `c` h)),+  -- fmap f . fmap g -> fmap (f . g)+  rr0 (\f g -> fmapE `a` f `c` fmapE `a` g)+      (\f g -> fmapE `a` (f `c` g)),+  -- map f . map g -> map (f . g)+  rr0 (\f g -> mapE `a` f `c` mapE `a` g)+      (\f g -> mapE `a` (f `c` g))++  ]++onceRewrites :: RewriteRule+onceRewrites = Hard $ Or [+  -- ($) --> id+  rr0 dollarE+      idE,+  -- concatMap --> (=<<)+  rr concatMapE extE,+  -- concat    --> join+  rr concatE joinE,+  -- liftM --> fmap+  rr liftME fmapE,+  -- map --> fmap+  rr mapE fmapE,+  -- subtract -> flip (-)+  rr  subtractE+      (flipE `a` minusE)+  ]++-- Now we can state rewrite rules in a nice high level way+-- Rewrite rules should be as pointful as possible since the pointless variants+-- will be derived automatically.+rules :: RewriteRule+rules = Or [+  -- f (g x) --> (f . g) x+  Hard $+  rr  (\f g x -> f `a` (g `a` x))+      (\f g x -> (f `c` g) `a` x),+  -- (>>=) --> flip (=<<)+  -- (>>=) --> flip (=<<)+  Hard $+  rr  bindE+      (flipE `a` extE),+  -- (.) id --> id+  rr (compE `a` idE)+     idE,+  -- (++) [x] --> (:) x+  rr  (\x -> appendE `a` (consE `a` x `a` nilE))+      (\x -> consE `a` x),+  -- (=<<) return --> id+  rr  (extE `a` returnE)+      idE,+  -- (=<<) f (return x) -> f x+  rr  (\f x -> extE `a` f `a` (returnE `a` x))+      a,+  -- (=<<) ((=<<) f . g) --> (=<<) f . (=<<) g+  rr  (\f g -> extE `a` ((extE `a` f) `c` g))+      (\f g -> (extE `a` f) `c` (extE `a` g)),+  -- flip (f . g) --> flip (.) g . flip f+  Hard $+  rr  (\f g -> flipE `a` (f `c` g))+      (\f g -> (flipE `a` compE `a` g) `c` (flipE `a` f)),+  -- flip (.) f . flip id --> flip f+  rr  (\f -> (flipE `a` compE `a` f) `c` (flipE `a` idE))+      (\f -> flipE `a` f),+  -- flip (.) f . flip flip --> flip (flip . f)+  rr  (\f -> (flipE `a` compE `a` f) `c` (flipE `a` flipE))+      (\f -> flipE `a` (flipE `c` f)),+  -- flip (flip (flip . f) g) --> flip (flip . flip f) g+  rr1 (\f g -> flipE `a` (flipE `a` (flipE `c` f) `a` g))+      (\f g -> flipE `a` (flipE `c` flipE `a` f) `a` g),++  -- flip (.) id --> id+  rr (flipE `a` compE `a` idE)+     idE,+  -- (.) . flip id --> flip flip+  rr  (compE `c` (flipE `a` idE))+      (flipE `a` flipE),+  -- s const x y --> y+  rr  (\x y -> sE `a` constE `a` x `a` y)+      (\_ y -> y),+  -- s (const . f) g --> f+  rr1 (\f g -> sE `a` (constE `c` f) `a` g)+      const,+  -- s (const f) --> (.) f+  rr  (\f -> sE `a` (constE `a` f))+      (\f -> compE `a` f),+  -- s (f . fst) snd --> uncurry f+  rr  (\f -> sE `a` (f `c` fstE) `a` sndE)+      (\f -> uncurryE `a` f),+  -- fst (join (,) x) --> x+  rr (\x -> fstE `a` (joinE `a` commaE `a` x))+     id,+  -- snd (join (,) x) --> x+  rr (\x -> sndE `a` (joinE `a` commaE `a` x))+     id,+  -- The next two are `simplifies', strictly speaking, but invoked rarely.+  -- uncurry f (x,y) --> f x y+  rr  (\f x y -> uncurryE `a` f `a` (commaE `a` x `a` y))+      (\f x y -> f `a` x `a` y),+  -- curry (uncurry f) --> f+  rr (\f -> curryE `a` (uncurryE `a` f))+     id,+  -- uncurry (curry f) --> f+  rr (\f -> uncurryE `a` (curryE `a` f))+     id,+  -- (const id . f) --> const id+  rr  (\f -> (constE `a` idE) `c` f)+      (\_ -> constE `a` idE),+  -- const x . f --> const x+  rr (\x f -> constE `a` x `c` f)+     (\x _ -> constE `a` x),+  -- fix f --> f (fix x)+  Hard $+  rr0 (\f -> fixE `a` f)+      (\f -> f `a` (fixE `a` f)),+  -- f (fix f) --> fix x+  Hard $+  rr0 (\f -> f `a` (fixE `a` f))+      (\f -> fixE `a` f),+  -- fix f --> f (f (fix x))+  Hard $+  rr0 (\f -> fixE `a` f)+      (\f -> f `a` (f `a` (fixE `a` f))),+  -- fix (const f) --> f+  rr (\f -> fixE `a` (constE `a` f))+     id,+  -- flip const x --> id+  rr  (\x -> flipE `a` constE `a` x)+      (pure idE),+  -- const . f --> flip (const f)+  Hard $+  rr  (\f -> constE `c` f)+      (\f -> flipE `a` (constE `a` f)),+  -- not (x == y) -> x /= y+  rr2 (\x y -> notE `a` (equalsE `a` x `a` y))+      (\x y -> nequalsE `a` x `a` y),+  -- not (x /= y) -> x == y+  rr2 (\x y -> notE `a` (nequalsE `a` x `a` y))+      (\x y -> equalsE `a` x `a` y),+  If (Or [rr plusE plusE, rr minusE minusE, rr multE multE]) $ down $ Or [+    -- 0 + x --> x+    rr  (\x -> plusE `a` zeroE `a` x)+        id,+    -- 0 * x --> 0+    rr  (\x -> multE `a` zeroE `a` x)+        (pure zeroE),+    -- 1 * x --> x+    rr  (\x -> multE `a` oneE `a` x)+        id,+    -- x - x --> 0+    rr  (\x -> minusE `a` x `a` x)+        (pure zeroE),+    -- x - y + y --> x+    rr  (\y x -> plusE `a` (minusE `a` x `a` y) `a` y)+        (\_ x -> x),+    -- x + y - y --> x+    rr  (\y x -> minusE `a` (plusE `a` x `a` y) `a` y)+        (\_ x -> x),+    -- x + (y - z) --> x + y - z+    rr  (\x y z -> plusE `a` x `a` (minusE `a` y `a` z))+        (\x y z -> minusE `a` (plusE `a` x `a` y) `a` z),+    -- x - (y + z) --> x - y - z+    rr  (\x y z -> minusE `a` x `a` (plusE `a` y `a` z))+        (\x y z -> minusE `a` (minusE `a` x `a` y) `a` z),+    -- x - (y - z) --> x + y - z+    rr  (\x y z -> minusE `a` x `a` (minusE `a` y `a` z))+        (\x y z -> minusE `a` (plusE `a` x `a` y) `a` z)+  ],++  -- flip ($) -> &+  rr (flipE `a` dollarE)+     ampersandE,++  -- fmap . const -> (<$)+  rr (fmapE `c` constE)+     replaceE,++  -- flip (<$) -> ($>)+  Hard $+  rr (flipE `a` replaceE)+     pointyE,++  Hard onceRewrites,+  -- join (fmap f x) --> f =<< x+  rr (\f x -> joinE `a` (fmapE `a` f `a` x))+     (\f x -> extE `a` f `a` x),+  -- (=<<) id --> join+  rr (extE `a` idE) joinE,+  -- join --> (=<<) id+  Hard $+  rr joinE (extE `a` idE),+  -- join (return x) --> x+  rr (\x -> joinE `a` (returnE `a` x))+     id,+  -- (return . f) =<< m --> fmap f m+  rr (\f m -> extE `a` (returnE `c` f) `a` m)+     (\f m -> fmapIE `a` f `a` m),+  -- (x >>=) . (return .) . f  --> flip (fmap . f) x+  rr (\f x -> bindE `a` x `c` (compE `a` returnE) `c` f)+     (\f x -> flipE `a` (fmapIE `c` f) `a` x),+  -- (>>=) (return f) --> flip id f+  rr (\f -> bindE `a` (returnE `a` f))+     (\f -> flipE `a` idE `a` f),+  -- liftM2 f x --> ap (f `fmap` x)+  Hard $+  rr (\f x -> liftM2E `a` f `a` x)+     (\f x -> apE `a` (fmapIE `a` f `a` x)),+  -- liftM2 f (return x) --> fmap (f x)+  rr (\f x -> liftM2E `a` f `a` (returnE `a` x))+     (\f x -> fmapIE `a` (f `a` x)),+  -- f `fmap` return x --> return (f x)+  rr (\f x -> fmapE `a` f `a` (returnE `a` x))+     (\f x -> returnE `a` (f `a` x)),+  -- (=<<) . flip (fmap . f) --> flip liftM2 f+  Hard $+  rr (\f -> extE `c` flipE `a` (fmapE `c` f))+     (\f -> flipE `a` liftM2E `a` f),++  -- ((f .) .) . g --> (f .** g)+  Hard $ rr (\f g -> (compE `a` (compE `a` f)) `c` g)+     (\f g -> (comp3E `a` f `a` g)),++  -- (f .) . g --> (f .* g)+  Hard $ rr (\f g -> (compE `a` f) `c` g)+     (\f g -> comp2E `a` f `a` g),++  -- flip (.) -> (-.)+  rr (flipE `a` compE)+     eyeE,++  -- (x &) -> ($ x)+  rr (\x -> ampersandE `a` x)+     (\x -> dollarE `a` x),++  -- f -.* (g . f) -> on g f+  Hard $+  rr (\f g -> oedipusE `a` f `a` (compE `a` g `a` f))+     (\f g -> onE `a` g `a` f),++  -- (.) -> fmap+  Hard $+  rr compE fmapE,++  -- join .* map x y -> (=<<) x y+  Hard $+  rr (joinE `c2` mapE) extE,++  -- map f (zip xs ys) --> zipWith (curry f) xs ys+  Hard $+  rr (\f xs ys -> mapE `a` f `a` (zipE `a` xs `a` ys))+     (\f xs ys -> zipWithE `a` (curryE `a` f) `a` xs `a` ys),++  -- zipWith (,) --> zip (,)+  rr (zipWithE `a` commaE) zipE,++  -- all f --> and . map f+  Hard $+  rr (\f -> allE `a` f)+     (\f -> andE `c` mapE `a` f),+  -- and . map f --> all f+  rr (\f -> andE `c` mapE `a` f)+     (\f -> allE `a` f),+  -- any f --> or . map f+  Hard $+  rr (\f -> anyE `a` f)+     (\f -> orE `c` mapE `a` f),+  -- or . map f --> any f+  rr (\f -> orE `c` mapE `a` f)+     (\f -> anyE `a` f),++  -- return f `ap` x --> fmap f x+  rr (\f x -> apE `a` (returnE `a` f) `a` x)+     (\f x -> fmapIE `a` f `a` x),+  -- ap (f `fmap` x) --> liftM2 f x+  rr (\f x -> apE `a` (fmapIE `a` f `a` x))+     (\f x -> liftM2E `a` f `a` x),+  -- f `ap` x --> (`fmap` x) =<< f+  Hard $+  rr (\f x -> apE `a` f `a` x)+     (\f x -> extE `a` (flipE `a` fmapIE `a` x) `a` f),+  -- (`fmap` x) =<< f --> f `ap` x+  rr (\f x -> extE `a` (flipE `a` fmapIE `a` x) `a` f)+     (\f x -> apE `a` f `a` x),+  -- (x >>=) . flip (fmap . f) -> liftM2 f x+  rr (\f x -> bindE `a` x `c` flipE `a` (fmapE `c` f))+     (\f x -> liftM2E `a` f `a` x),++  -- (f =<< m) x --> f (m x) x+  rr0 (\f m x -> extE `a` f `a` m `a` x)+      (\f m x -> f `a` (m `a` x) `a` x),+  -- (fmap f g x) --> f (g x)+  rr0 (\f g x -> fmapE `a` f `a` g `a` x)+      (\f g x -> f `a` (g `a` x)),+  -- return x y --> y+  rr  (\y x -> returnE `a` x `a` y)+      const,+  -- liftM2 f g h x --> g x `h` h x+  rr0 (\f g h x -> liftM2E `a` f `a` g `a` h `a` x)+      (\f g h x -> f `a` (g `a` x) `a` (h `a` x)),+  -- ap f id --> join f+  rr  (\f -> apE `a` f `a` idE)+      (\f -> joinE `a` f),++  -- flip flip f . ((.) .* g) --> f -.** g+  Hard $+  rr (\f g -> (flipE `a` flipE `a` f `c` (compE `c2` g)))+     (\f g -> oedipus2E `a` f `a` g),++  -- (. f) . g --> (f -.* g)+  Hard $+  rr (\f g -> (flipE `a` compE `a` f) `c` g)+     (\f g -> oedipusE `a` f `a` g),++  -- (=<<) const q --> flip (>>) q+  Hard $ -- ??+  rr (\q p -> extE `a` (constE `a` q) `a` p)+     (\q p -> seqME `a` p `a` q),+  -- p >> q --> const q =<< p+  Hard $+  rr (\p q -> seqME `a` p `a` q)+     (\p q -> extE `a` (constE `a` q) `a` p),++  -- ap (f -.* ((,) . f . fst)) snd --> f (***) f+  Hard $+  rr (\f -> apE `a` (f `o` (commaE `c` f `c` fstE)) `a` sndE)+     (\f -> joinE `a` crossE `a` f),++  -- flip (=<<) --> >>=+  rr (flipE `a` extE)+     bindE,++  -- flip (>=>) --> <=<+  rr (flipE `a` fishE)+     kliesliE,++  -- (.) . (=<<) --> <=<+  rr (compE `c` extE)+     kliesliE,++  -- join . (g .* f) --> f >=> g+  Hard $+    rr (\f g -> joinE `c` (g `c2` f))+       (\f g -> fishE `a` f `a` g),++  -- (`ap` snd) . (fst -.* (flip =<< (.) .* ((,) .))) --> join (***)+  -- Hard $+  rr ((flipE `a` apE `a` sndE) `c` (fstE `o` (extE `a` flipE `a` (compE `c2` (compE `a` commaE)))))+     (joinE `a` crossE),++  -- experimental support for Control.Arrow stuff+  -- (costs quite a bit of performace)+  -- uncurry ((. g) . (,) . f) --> f *** g+  rr (\f g -> uncurryE `a` ((flipE `a` compE `a` g) `c` commaE `c` f))+     (\f g -> crossE `a` f `a` g),+  -- uncurry ((,) . f) --> first f+  rr (\f -> uncurryE `a` (commaE `c` f))+     (\f -> firstE `a` f),+  -- uncurry ((. g) . (,)) --> second g+  rr (\g -> uncurryE `a` ((flipE `a` compE `a` g) `c` commaE))+     (\g -> secondE `a` g),+  -- I think we need all three of them:+  -- uncurry (const f) --> f . snd+  rr (\f -> uncurryE `a` (constE `a` f))+     (`c` sndE),+  -- uncurry const --> fst+  rr (uncurryE `a` constE)+     fstE,+  -- uncurry (const . f) --> f . fst+  rr (\f -> uncurryE `a` (constE `c` f))+     (`c` fstE),++  -- TODO is this the right place?+  -- [x] --> return x+  Hard $+  rr (\x -> consE `a` x `a` nilE)+     (\x -> returnE `a` x),+  -- list destructors+  Hard $+  If (Or [rr consE consE, rr nilE nilE]) $ Or [+    down $ Or [+      -- length [] --> 0+      rr (lengthE `a` nilE)+         zeroE,+      -- length (x:xs) --> 1 + length xs+      rr (\x xs -> lengthE `a` (consE `a` x `a` xs))+         (\_ xs -> plusE `a` oneE `a` (lengthE `a` xs))+    ],+    -- map/fmap elimination+    down $ Or [+      -- map f (x:xs) --> f x: map f xs+      rr (\f x xs -> mapE `a` f `a` (consE `a` x `a` xs))+         (\f x xs -> consE `a` (f `a` x) `a` (mapE `a` f `a` xs)),+      -- fmap f (x:xs) --> f x: Fmap f xs+      rr (\f x xs -> fmapE `a` f `a` (consE `a` x `a` xs))+         (\f x xs -> consE `a` (f `a` x) `a` (fmapE `a` f `a` xs)),+      -- map f []     --> []+      rr (\f -> mapE `a` f `a` nilE)+         (pure nilE),+      -- fmap f []     --> []+      rr (\f -> fmapE `a` f `a` nilE)+         (pure nilE)+    ],+    -- foldr elimination+    down $ Or [+      -- foldr f z (x:xs) --> f x (foldr f z xs)+      rr (\f x xs z -> (foldrE `a` f `a` z) `a` (consE `a` x `a` xs))+         (\f x xs z -> (f `a` x) `a` (foldrE `a` f `a` z `a` xs)),+      -- foldr f z [] --> z+      rr (\f z -> foldrE `a` f `a` z `a` nilE)+         (\_ z -> z)+    ],+    -- foldl elimination+    down $ Opt (CRR $ assocL ["."]) `Then` Or [+      -- sum xs --> foldl (+) 0 xs+      rr (\xs -> sumE `a` xs)+         (\xs -> foldlE `a` plusE `a` zeroE `a` xs),+      -- product xs --> foldl (*) 1 xs+      rr (\xs -> productE `a` xs)+         (\xs -> foldlE `a` multE `a` oneE `a` xs),+      -- foldl1 f (x:xs) --> foldl f x xs+      rr (\f x xs -> foldl1E `a` f `a` (consE `a` x `a` xs))+         (\f x xs -> foldlE `a` f `a` x `a` xs),+      -- foldl f z (x:xs) --> foldl f (f z x) xs+      rr (\f z x xs -> (foldlE `a` f `a` z) `a` (consE `a` x `a` xs))+         (\f z x xs -> foldlE `a` f `a` (f `a` z `a` x) `a` xs),+      -- foldl f z [] --> z+      rr (\f z -> foldlE `a` f `a` z `a` nilE)+         (\_ z -> z),+      -- special rule:+      -- foldl f z [x] --> f z x+      rr (\f z x -> foldlE `a` f `a` z `a` (returnE `a` x))+         (\f z x -> f `a` z `a` x),+      rr (\f z x -> foldlE `a` f `a` z `a` (consE `a` x `a` nilE))+         (\f z x -> f `a` z `a` x)+    ] `OrElse` (+      -- (:) x --> (++) [x]+      Opt (rr0 (\x -> consE `a` x)+         (\x -> appendE `a` (consE `a` x `a` nilE))) `Then`+      -- More special rule: (:) x . (++) ys --> (++) (x:ys)+      up (rr0 (\x ys -> (consE `a` x) `c` (appendE `a` ys))+         (\x ys -> appendE `a` (consE `a` x `a` ys)))+      )+  ],++  -- Complicated Transformations+  CRR collapseLists,+  up $ Or [CRR (evalUnary unaryBuiltins), CRR (evalBinary binaryBuiltins)],+  up $ CRR (assoc assocOps),+  up $ CRR (assocL assocOps),+  up $ CRR (assocR assocOps),+  Up (CRR (commutative commutativeOps)) $ down $ Or [CRR $ assocL assocLOps,+                                                     CRR $ assocR assocROps],++  Hard simplifies+  ] `Then` Opt (up simplifies)+assocLOps, assocROps, assocOps :: [String]+assocLOps = ["+", "*", "&&", "||", "max", "min"]+assocROps = [".", "++", ".*", ".**"]+assocOps  = assocLOps ++ assocROps++commutativeOps :: [String]+commutativeOps = ["*", "+", "==", "/=", "max", "min"]++unaryBuiltins :: [(String,Unary)]+unaryBuiltins = [+    ("not",    UA (not    :: Bool -> Bool)),+    ("negate", UA (negate :: Integer -> Integer)),+    ("signum", UA (signum :: Integer -> Integer)),+    ("abs",    UA (abs    :: Integer -> Integer))+  ]++binaryBuiltins :: [(String,Binary)]+binaryBuiltins = [+    ("+",    BA ((+)  :: Integer -> Integer -> Integer)),+    ("-",    BA ((-)  :: Integer -> Integer -> Integer)),+    ("*",    BA ((*)  :: Integer -> Integer -> Integer)),+    ("^",    BA ((^)  :: Integer -> Integer -> Integer)),+    ("<",    BA ((<)  :: Integer -> Integer -> Bool)),+    (">",    BA ((>)  :: Integer -> Integer -> Bool)),+    ("==",   BA ((==) :: Integer -> Integer -> Bool)),+    ("/=",   BA ((/=) :: Integer -> Integer -> Bool)),+    ("<=",   BA ((<=) :: Integer -> Integer -> Bool)),+    (">=",   BA ((>=) :: Integer -> Integer -> Bool)),+    ("div",  BA (div  :: Integer -> Integer -> Integer)),+    ("mod",  BA (mod  :: Integer -> Integer -> Integer)),+    ("max",  BA (max  :: Integer -> Integer -> Integer)),+    ("min",  BA (min  :: Integer -> Integer -> Integer)),+    ("&&",   BA ((&&) :: Bool -> Bool -> Bool)),+    ("||",   BA ((||) :: Bool -> Bool -> Bool))+  ]
+ src/Plugin/Pl/Transform.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE PatternGuards     #-}+module Plugin.Pl.Transform (+    transform,+  ) where++import           Control.Monad.Trans.State+import           Data.Graph                (flattenSCC, flattenSCCs,+                                            stronglyConnComp)+import qualified Data.Map                  as M+import           Data.Maybe+import           Plugin.Pl.Common++occursP :: String -> Pattern -> Bool+occursP v (PVar v')      = v == v'+occursP v (PTuple p1 p2) = v `occursP` p1 || v `occursP` p2+occursP v (PCons  p1 p2) = v `occursP` p1 || v `occursP` p2++freeIn :: String -> Expr -> Int+freeIn v (Var _ v') = fromEnum $ v == v'+freeIn v (Lambda pat e) = if v `occursP` pat then 0 else freeIn v e+freeIn v (App e1 e2) = freeIn v e1 + freeIn v e2+freeIn v (Let ds e') = if v `elem` map declName ds then 0+  else freeIn v e' + sum [freeIn v e | Define _ e <- ds]++isFreeIn :: String -> Expr -> Bool+isFreeIn v e = freeIn v e > 0++tuple :: [Expr] -> Expr+tuple  = foldr1 (\x y -> Var Inf "," `App` x `App` y)++tupleP :: [String] -> Pattern+tupleP vs = foldr1 PTuple $ PVar `map` vs++dependsOn :: [Decl] -> Decl -> [Decl]+dependsOn ds d = [d' | d' <- ds, declName d' `isFreeIn` declExpr d]++unLet :: Expr -> Expr+unLet (App e1 e2) = App (unLet e1) (unLet e2)+unLet (Let [] e) = unLet e+unLet (Let ds e) = unLet $+  Lambda (tupleP $ declName `map` dsYes) (Let dsNo e) `App`+    (fix' `App` Lambda (tupleP $ declName `map` dsYes)+                        (tuple  $ declExpr `map` dsYes))+    where+  comps = stronglyConnComp [(d',d',dependsOn ds d') | d' <- ds]+  dsYes = flattenSCC $ head comps+  dsNo = flattenSCCs $ tail comps++unLet (Lambda v e) = Lambda v (unLet e)+unLet (Var f x) = Var f x++type Env = M.Map String String++-- It's a pity we still need that for the pointless transformation.+-- Otherwise a newly created id/const/... could be bound by a lambda+-- e.g. transform' (\id x -> x) ==> transform' (\id -> id) ==> id+alphaRename :: Expr -> Expr+alphaRename e = alpha e `evalState` M.empty where+  alpha :: Expr -> State Env Expr+  alpha (Var f v)     = Var f . fromMaybe v . M.lookup v <$> get+  alpha (App e1 e2)   = liftM2 App (alpha e1) (alpha e2)+  alpha (Let _ _)     = assert False bt+  alpha (Lambda v e') = inEnv $ liftM2 Lambda (alphaPat v) (alpha e')++  -- act like a reader monad+  inEnv :: State s a -> State s a+  inEnv f = gets $ evalState f++  alphaPat (PVar v) = do+    fm <- get+    let v' = "$" ++ show (M.size fm)+    put $ M.insert v v' fm+    return $ PVar v'+  alphaPat (PTuple p1 p2) = liftM2 PTuple (alphaPat p1) (alphaPat p2)+  alphaPat (PCons p1 p2) = liftM2 PCons (alphaPat p1) (alphaPat p2)+++transform :: Expr -> Expr+transform = transform' . alphaRename . unLet++-- Infinite generator of variable names.+varNames :: [String]+varNames = flip replicateM usableChars =<< [1..]+  where+    usableChars = ['a'..'z']++-- First variable name not already in use+fresh :: [String] -> String+fresh variables = head . filter (not . flip elem variables) $ varNames++names :: Expr -> [String]+names (Var _ str)     = [str]+-- Lambda pattern names are rewritten to be meaningless/unwritable, so we don't+-- need to include them here. Variables from lambdas used in expressions are+-- also rewritten, but there's no reason to special-case it unless it's provably+-- poor-performing to scan over the result in `fresh`, which I doubt it is.+names (Lambda _ exp') = names exp'+names (App exp1 exp2) = names exp1 ++ names exp2+names (Let dlcs exp') = (dnames =<< dlcs) ++ names exp'+  where+    dnames (Define nm exp'') = nm : names exp''++transform' :: Expr -> Expr+transform' exp' = go exp'+  where+    -- Explicit sharing for readability+    vars = names exp'++    go Let {} =+      assert False bt+    go (Var f v) =+      Var f v+    go (App e1 e2) =+      App (go e1) (go e2)+    go (Lambda (PTuple p1 p2) e) =+      go $+        Lambda (PVar var) $ (Lambda p1 . Lambda p2 $ e) `App` f `App` s+      where+        var   = fresh vars+        f     = Var Pref "fst" `App` Var Pref var+        s     = Var Pref "snd" `App` Var Pref var+    go (Lambda (PCons p1 p2) e) =+      go $+        Lambda (PVar var) $ (Lambda p1 . Lambda p2 $ e) `App` f `App` s+      where+        var = fresh vars+        f   = Var Pref "head" `App` Var Pref var+        s   = Var Pref "tail" `App` Var Pref var+    go (Lambda (PVar v) e) =+      go $ getRidOfV e+      where+        getRidOfV (Var f v') | v == v'   = id'+                             | otherwise = const' `App` Var f v'+        getRidOfV l@(Lambda pat _) =+          assert (not $ v `occursP` pat) $ getRidOfV $ go l+        getRidOfV Let {} = assert False bt+        getRidOfV e'@(App e1 e2)+          | fr1 && fr2 = scomb `App` getRidOfV e1 `App` getRidOfV e2+          | fr1 = flip' `App` getRidOfV e1 `App` e2+          | Var _ v' <- e2, v' == v = e1+          | fr2 = comp `App` e1 `App` getRidOfV e2+          | otherwise = const' `App` e'+          where+            fr1 = v `isFreeIn` e1+            fr2 = v `isFreeIn` e2
test/Test.hs view
@@ -1,42 +1,40 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Main (main) where -import           Test.HUnit-import           Test.QuickCheck-+import           Data.Char               (isSpace)+import           Data.Foldable           (fold) import           Plugin.Pl.Common import           Plugin.Pl.Optimize import           Plugin.Pl.Parser import           Plugin.Pl.PrettyPrinter import           Plugin.Pl.Transform--import           Data.Char               (isSpace)- import           System.Environment      (getArgs) import           System.Exit             (exitFailure) import           System.IO               (BufferMode (NoBuffering),                                           hSetBuffering, stdout)+import           Test.HUnit+import           Test.QuickCheck  instance Arbitrary Expr where-  arbitrary = sized $ \size -> frequency $ zipWith (,) [1,size,size]+  arbitrary = sized $ \size -> frequency $ zip [1,size,size]     [arbVar,      liftM2 Lambda arbitrary arbitrary,      let se = resize (size `div` 2) arbitrary in liftM2 App se se ]    shrink (Var _ _) = []   shrink (Lambda v e) =-    e : map (\v' -> Lambda v' e) (shrink v) ++ map (Lambda v) (shrink e)+    e : map (`Lambda` e) (shrink v) ++ map (Lambda v) (shrink e)   shrink (App e1 e2) = [e1, e2] ++     map (App e1) (shrink e2) ++ map (`App` e2) (shrink e1)   -- Let isn't generated by arbitrary, so we can probably ignore it-  shrink (Let{}) = error "Expr.shrink: Let"+  shrink Let{} = error "Expr.shrink: Let"  instance Arbitrary Pattern where   arbitrary = sized $ \size ->     let       spat = resize (size `div` 5) arbitrary     in-      frequency $ zipWith (,) [1,size,size] [+      frequency $ zip [1,size,size] [         (PVar . return) `fmap` choose ('a','z'),         liftM2 PTuple spat spat,         liftM2 PCons  spat spat]@@ -71,10 +69,10 @@     print d     putStrLn "Transformed to pointfree style:"     let d' = mapTopLevel transform d-    print $ d'+    print d'     putStrLn "Optimized expression:"     mapM_ print $ mapTopLevel' optimize d'-  Left err -> putStrLn $ err+  Left err -> putStrLn err  unitTest :: String -> [String] -> Test unitTest inp out = TestCase $ do@@ -86,7 +84,7 @@     [] -> error "Test case expected result missing!"     [x] -> assertEqual (inp ++ " failed.") x res     _ -> assertBool-      (concat [inp, " failed.",+      (fold [inp, " failed.",         "\nexpected one of:\n", show out,         "\n        but got:\n", show res])       (res `elem` out)@@ -236,5 +234,4 @@ doTests = do   Counts{ errors = es, failures = fs } <- runTestTT unitTests   qcTests-  when (es > 0 || fs > 0) $ exitFailure-  return ()+  when (es > 0 || fs > 0) exitFailure