module Each.Transform
( transform
, Env (..)
, Result (..)
) where
import Control.Applicative
import Control.Monad.Reader
import Data.Monoid
import Language.Haskell.TH
import qualified Control.Monad
import qualified Data.Functor
import qualified Each.Invoke
data Result
= Pure ExpQ
| Bind ExpQ
data Env
= Env
{ envType :: Maybe TypeQ
}
type M = ReaderT Env Q
transform :: Exp -> Env -> Q Result
transform ex env = runReaderT (transform' ex) env
transform' :: Exp -> M Result
transform' (InfixE Nothing (VarE v) (Just x))
| v == '(Each.Invoke.~!) = impurify x
transform' (AppE (VarE v) x)
| v == 'Each.Invoke.bind = impurify x
transform' (InfixE (Just (VarE vf)) (VarE vo) (Just x))
| vf == 'Each.Invoke.bind && vo == '(Prelude.$) = impurify x
transform' (VarE vn) = pure $ Pure (varE vn)
transform' (ConE cn) = pure $ Pure (conE cn)
transform' (LitE lit) = pure $ Pure (litE lit)
transform' (AppE f x) =
liftA2 go (transform' f) (transform' x)
where
go (Pure ef) (Pure ex) = Pure [| $(ef) $(ex) |]
go (Pure ef) (Bind ex) = Bind [| Data.Functor.fmap $(ef) $(ex) |]
go (Bind ef) (Pure ex) = Bind [| Data.Functor.fmap ($ $(ex)) $(ef) |]
go (Bind ef) (Bind ex) = Bind [| (Control.Applicative.<*>) $(ef) $(ex) |]
transform' (InfixE Nothing op Nothing) =
transform' op
transform' (InfixE (Just lhs) op Nothing) =
transform' (AppE op lhs)
transform' (InfixE Nothing op (Just rhs)) =
lift [| Prelude.flip $(pure op) $(pure rhs) |] >>= transform'
transform' (InfixE (Just lhs) op (Just rhs)) =
transform' (AppE (AppE op lhs) rhs)
transform' (SigE x ty) = transform' x >>= \tx -> case tx of
Pure ex -> pure $ Pure [| $(ex) :: $(pure ty) |]
Bind ex -> do
Env { envType = mety } <- ask
case mety of
Nothing -> do
ok <- lift $ isExtEnabled PartialTypeSignatures
if ok
then pure $ Bind [| $(ex) :: $(pure WildCardT) $(pure ty) |]
else fail errSig
Just ety ->
pure $ Bind [| $(ex) :: $(ety) $(pure ty) |]
transform' x = fail (errUnsupportedSyntax x)
impurify :: Exp -> M Result
impurify x =
go <$> transform' x
where
go (Pure e) = Bind e
go (Bind e) = Bind [| Control.Monad.join $(e) |]
errUnsupportedSyntax :: Exp -> String
errUnsupportedSyntax x = "each: Unsupported syntax in " <> pprint x
errSig :: String
errSig =
"each: Using type signatures on expressions containing 'bind' requires \
\specifying the context type using 'eachWith', or enabling \
\-XPartialTypeSignatures."