{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE CPP #-}
module Language.Haskell.Holes
( hole
)
where
import Data.Function
import Control.Monad.Trans.State
import Control.Monad.Trans.Except
import Control.Monad.Trans.Class
import Control.Arrow
import Control.Applicative
import Data.Foldable
import Language.Haskell.TH
import Data.List
import Data.Maybe
newtype ErrorMsg = ErrorMsg (String, Maybe Context)
instance Semigroup ErrorMsg where
_ <> b@(ErrorMsg (_, Just _)) = b
a@(ErrorMsg (_, Just _)) <> _ = a
_ <> b = b
instance Monoid ErrorMsg where
mempty = mkError "Unknown" Nothing
type Context = [(Exp, Type)]
variables :: Variables
variables =
map mkName $
map pure letters ++
(zipWith (\c n -> c : show (n :: Integer)) (concat $ repeat letters) $
concatMap (\n -> take (length letters) $ repeat n) [1..])
where letters = "abcdefghijklmnopqrstuvwxyz"
type Variables = [Name]
mkError :: String -> Maybe Context -> ErrorMsg
mkError str mctx = ErrorMsg (str, mctx)
{-# INLINE mkError #-}
type Env a = ExceptT ErrorMsg (State Variables) a
runEnv :: Env a -> Either ErrorMsg a
runEnv = flip evalState variables . runExceptT
{-# INLINE runEnv #-}
requestVar :: Env Name
requestVar = lift $ get >>= \(var : rest) -> put rest >> pure var
extractProduct
:: Type
-> Env ([Pat], Context)
extractProduct (TupleT _) = pure ([], [])
extractProduct (AppT i o) = do
(pat, ctx) <- extractContext o
first (++ [pat]) . second (ctx ++) <$> extractProduct i
extractProduct _ = throwE $ mkError "Not a product type" Nothing
extractContext
:: Type
-> Env (Pat, Context)
extractContext td =
first TupP <$> extractProduct td <|> bindVariable
where
bindVariable = do
var <- requestVar
pure (VarP var, [(VarE var, td)])
prove
:: Context
-> Type
-> Env Exp
prove ctx (AppT (AppT ArrowT acd) csq) =
case acd of
(AppT (AppT (ConT c) a) b)
| c == ''Either -> do
lamVar <- requestVar
aVar <- requestVar
aExpr <- prove ctx (AppT (AppT ArrowT a) csq)
bVar <- requestVar
bExpr <- prove ctx (AppT (AppT ArrowT b) csq)
pure $
LamE ([VarP lamVar]) $
CaseE (VarE lamVar) [ Match (ConP 'Left [VarP aVar]) (NormalB (AppE aExpr (VarE aVar))) []
, Match (ConP 'Right [VarP bVar]) (NormalB (AppE bExpr (VarE bVar))) []
]
_ -> do
(pat, ctx') <- extractContext acd
LamE [pat] <$> prove (ctx' ++ ctx) csq
prove ctx (AppT (AppT c a) b)
| c == ConT ''Either =
(prove ctx a <&> proceedWith 'Left) <|>
(prove ctx b <&> proceedWith 'Right)
where
proceedWith con expr = AppE (ConE con) expr
prove ctx (AppT a b) = do
aExpr <- prove ctx a
bExpr <- prove ctx b
pure (AppE aExpr bExpr)
prove ctx (TupleT n) =
fromMaybe (throwE (mkError "Not a tuple" $ Just ctx)) $
pure . ConE <$> nth n tupleConstructors
prove ctx goal = asum branches
where
branches :: [Env Exp]
branches = map try $ pulls ctx
try :: ((Exp, Type), Context) -> Env Exp
try ((expr, AppT (AppT ArrowT a) b), context') =
prove context' a >>= \exprA ->
prove ((AppE expr exprA, b) : context') goal
try ((expr, typ), _)
| typ == goal = pure expr
try _ =
throwE (mkError "Can't infer type " $ Just ctx)
pulls xs = init $ zipWith pull (inits xs) (tails xs)
pull xs (y:ys) = (y, xs ++ ys)
pull _ _ = error "Impossible happened"
hole :: Q Exp -> Q Exp
hole qexpr = do
let context = []
expr <- qexpr
extractType expr &
fmap (\(goal, holeName, inputType) ->
either describeError (proceed goal holeName inputType) $
runEnv $ prove context goal) &
fromMaybe (onFail expr)
where
proceed :: Type -> Name -> Type -> Exp -> Q Exp
proceed goal holeName inputType term = do
let normalized = normalize term
runIO . putStrLn $ "hole: '" ++ pprint holeName ++ "' := "
++ pprint normalized ++ " :: " ++ pprint goal
pure $ SigE normalized inputType
describeError :: ErrorMsg -> Q Exp
describeError (ErrorMsg (msg, mcontext)) = do
context <- printContext $ fromMaybe [] mcontext
fail $ msg ++ if not (null context)
then
"\nin the context:\n" ++ context
else
" (no context available)"
onFail :: Exp -> Q Exp
onFail expr = fail $ "Can't parse type definition in " ++ show expr
++ "\nHoles must be in form of '$(hole [| _ :: <place type "
++ "definition here> |])'"
extractType :: Exp -> Maybe (Type, Name, Type)
extractType (SigE (VarE n) tp@(ForallT _ _ t)) =
pure (t, n, tp)
extractType (SigE (VarE n) t) =
pure (t, n, t)
extractType (SigE (UnboundVarE n) tp@(ForallT _ _ t)) =
pure (t, n, tp)
extractType (SigE (UnboundVarE n) t) =
pure (t, n, t)
extractType _ = Nothing
foldLambdas :: Exp -> Maybe ([Pat], Exp)
foldLambdas (LamE pat l@(LamE _ _)) = do
(ls, t) <- foldLambdas l
pure (pat ++ ls, t)
foldLambdas (LamE pat b) =
pure (pat, b)
foldLambdas _ = Nothing
normalizeProduct
:: Exp
-> Maybe Exp
normalizeProduct expr = case go 0 expr of
Nothing -> Nothing
Just exprs -> Just $ TupE $ reverse exprs
where
go :: Int -> Exp -> Maybe [Exp]
go n (AppE (ConE tc) a) =
if elemIndex tc tupleConstructors == Just (n + 1)
then pure [a]
else Nothing
go n (AppE a b) = do
exprs <- go (n+1) a
pure (b : exprs)
go _ _ = Nothing
normalize :: Exp -> Exp
normalize expr =
case foldLambdas expr of
Just (pats, expr') -> LamE pats $ normalize expr'
Nothing -> case normalizeProduct expr of
Nothing -> stepDown expr
Just expr' -> normalize expr'
where
stepDown (AppE a b) = AppE (normalize a) (normalize b)
stepDown (LamE pats b) = LamE pats (normalize b)
stepDown (TupE exps) = TupE (map normalize exps)
stepDown other = other
tupleConstructors :: [Name]
tupleConstructors =
[ '()
, '(,)
, '(,,)
, '(,,,)
, '(,,,,)
, '(,,,,,)
, '(,,,,,,)
, '(,,,,,,,)
, '(,,,,,,,,)
, '(,,,,,,,,,)
, '(,,,,,,,,,,)
, '(,,,,,,,,,,,)
, '(,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
, '(,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,)
]
printContext :: Context -> Q String
printContext [] = pure ""
printContext ((term, typeDef) : rest) =
((pprint term ++ " :: " ++ pprint typeDef ++ "\n") ++) <$> printContext rest
nth :: Int -> [a] -> Maybe a
nth _ [] = Nothing
nth 0 (x : _) = Just x
nth n (_ : xs) = nth (n - 1) xs
#if !MIN_VERSION_base (3,0,0)
instance Functor (Either a) where
fmap :: Functor f => (a - b) -> f a -> f b
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)
instance Applicative (Either e) where
pure = Right
Left e <*> _ = Left e
Right f <*> r = fmap f r
#endif
#if !MIN_VERSION_base (4,4,0)
instance Monad (Either e) where
Left l >>= _ = Left l
Right r >>= k = k r
#endif
(<&>) :: Functor f => f a -> (a -> b) -> f b
(<&>) = flip fmap
infixl 4 <&>