{-# LANGUAGE ImplicitParams #-}
module Lambdabot.Plugin.Haskell.Pl.Optimize (
optimize,
) where
import Lambdabot.Plugin.Haskell.Pl.Common
import Lambdabot.Plugin.Haskell.Pl.Rules
import Lambdabot.Plugin.Haskell.Pl.PrettyPrinter ()
import Data.List (nub)
import Data.Maybe (listToMaybe)
cut :: [a] -> [a]
cut = take 1
toMonadPlus :: MonadPlus m => Maybe a -> m a
toMonadPlus Nothing = mzero
toMonadPlus (Just x)= return x
type Size = Double
sizeExpr' :: Expr -> Size
sizeExpr' e = fromIntegral (length $ show e) + adjust e where
adjust :: Expr -> Size
adjust (Var _ str)
| str == "uncurry" = -4
| str == "flip" = 0.1
| str == ">>=" = 0.05
| str == "$" = 0.01
| str == "subtract" = 0.01
| str == "ap" = 2
| str == "liftM2" = 1.01
| str == "return" = -2
| str == "zipWith" = -4
| str == "const" = 0
| str == "fmap" = -1
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
listToMaybe 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 -> (\x -> rewrite x e) =<< rs
RR {} -> rewDeep rl e
CRR {} -> rewDeep rl e
Down {} -> rewDeep rl e
where
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 = (\x -> rew x 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