module Data.Thorn.Fmap (
autofmap
, Variance(..)
, autovariance, autovarianceRaw, autofunctorize
) where
import Data.Thorn.Internal
import Language.Haskell.TH
import Data.List
import Data.Maybe
import qualified Data.Sequence as S
import qualified Data.Foldable as F
import Control.Monad
import Control.Applicative
import Control.Monad.State
import Data.Monoid
import Data.Functor
import Data.Functor.Contravariant
import Data.Bifunctor
import Data.Profunctor
autofmap :: TypeQ -> ExpQ
autofmap t = do
(n,tx) <- t >>= normalizeType [] [] >>= apply 0
(e,txnmes) <- runStateT (autofmap' tx) []
return $ LamE (map newFuncP [0..n1]) (LetE (fmap (\(tx,nm,Just e) -> ValD (VarP nm) (NormalB e) []) txnmes) e)
apply :: Int -> Typex -> Q (Int,Typex)
apply n (FuncTx f) = f (SpecialTx n) >>= apply (n+1)
apply n tx@(VarTx _) = return (n,tx)
apply n tx@(DataTx _ _ _) = return (n,tx)
apply n tx@(SeenDataTx _ _) = return (n,tx)
apply n tx@(TupleTx _) = return (n,tx)
apply n tx@(ArrowTx _ _) = return (n,tx)
apply n tx@(ListTx _) = return (n,tx)
autofmap',autofmap'' :: Typex -> StateT [(Typex,Name,Maybe Exp)] Q Exp
autofmap' tx = do
txnmes <- get
case find (\(tx',_,_)->tx==tx') txnmes of
Just (_,nm,_) -> return (VarE nm)
Nothing -> autofmap'' tx
autofmap'' (VarTx _) = return $ mkNameE "id"
autofmap'' (BasicTx _) = return $ mkNameE "id"
autofmap'' (FuncTx _) = fail "Automap doesn't accept such a type with a kind * -> k."
autofmap'' (DataTx nm vmp cxs) = do
txnmes <- get
put ((tx0, newFmap (length txnmes), Nothing) : txnmes)
e <- LamE [newVarP 0] <$> (CaseE (newVarE 0) <$> (mapM go cxs))
txnmes' <- get
put $ map (\(tx,nm,e') -> if tx==tx0 then (tx,nm,Just e) else (tx,nm,e')) txnmes'
return e
where go (NormalCx nm txs) = do
es <- autofmapmap txs
return $ Match (ConP nm (map newVarP [0..length txs1])) (NormalB (apps (ConE nm) es)) []
go (InfixCx nm txa txb) = do
[ea,eb] <- autofmapmap [txa,txb]
return $ Match (InfixP (newVarP 0) nm (newVarP 1)) (NormalB (InfixE (Just ea) (ConE nm) (Just eb))) []
tx0 = SeenDataTx nm vmp
autofmap'' (SeenDataTx nm vmp) = fail "Autofmap doesn't work well, sorry."
autofmap'' (TupleTx txs) = do
es <- autofmapmap txs
return $ LamE [TupP (map newVarP [0..length txs1])] (TupE es)
where go i tx = autofmap' tx >>= \e -> return $ AppE e (newVarE i)
autofmap'' (ArrowTx txa txb) = do
fa <- autofmap' txa
fb <- autofmap' txb
return $ LamE [newVarP 0, newVarP 1] (AppE fb (AppE (newVarE 0) (AppE fa (newVarE 1))))
autofmap'' (ListTx tx) = autofmap' tx >>= \f -> return $ AppE (mkNameE "map") f
autofmap'' (SpecialTx n) = return $ newFuncE n
autofmapmap txs = mapM (\(i,tx) -> autofmap' tx >>= \e -> return $ AppE e (newVarE i)) (zip [0 .. length txs 1] txs)
data Variance =
Co
| Contra
| Free
| Fixed deriving (Show, Read)
instance Monoid Variance where
Free `mappend` v = v
v `mappend` Free = v
Fixed `mappend` _ = Fixed
_ `mappend` Fixed = Fixed
Co `mappend` Contra = Fixed
Contra `mappend` Co = Fixed
mempty = Free
neg :: Variance -> Variance
neg Co = Contra
neg Contra = Co
neg Free = Free
neg Fixed = Fixed
autovariance :: TypeQ -> ExpQ
autovariance t = do
vs <- autovarianceRaw t
return $ ListE (map go vs)
where go Co = mkNameCE "Co"
go Contra = mkNameCE "Contra"
go Free = mkNameCE "Free"
go Fixed = mkNameCE "Fixed"
autovarianceRaw :: TypeQ -> Q [Variance]
autovarianceRaw t = do
(n,tx) <- t >>= normalizeType [] [] >>= apply 0
(_,seq) <- runStateT (autovariance' Co [] tx) (S.replicate n Free)
return $ (F.toList seq)
autovariance' :: Variance -> [(Name,[Conx])] -> Typex -> StateT (S.Seq Variance) Q ()
autovariance' v dts (SpecialTx n) = do
seq <- get
put $ S.adjust (<>v) n seq
autovariance' v dts (VarTx _) = return ()
autovariance' v dts (FuncTx _) = fail "Automap doesn't accept such a type with a kind * -> k."
autovariance' v dts (DataTx nm _ cxs) = mapM_ (mapM_ (autovariance' v ((nm,cxs):dts)) . cxtxs) cxs
autovariance' v dts (SeenDataTx nm _) = return ()
autovariance' v dts (TupleTx txs) = mapM_ (autovariance' v dts) txs
autovariance' v dts (ArrowTx txa txb) = autovariance' (neg v) dts txa >> autovariance' v dts txb
autovariance' v dts (ListTx tx) = autovariance' v dts tx
autofunctorize :: TypeQ -> DecsQ
autofunctorize t = do
vs <- autovarianceRaw t
case vs of
[Co] -> go (mkName "Functor") (mkName "fmap")
[Contra] -> go (mkName "Contravariant") (mkName "contramap")
[Co,Co] -> go (mkName "Bifunctor") (mkName "bimap")
[Contra,Co] -> go (mkName "Profunctor") (mkName "dimap")
_ -> fail "autofunctorize doesn't know the suitable functor class for this variance"
where go cls member = do
e <- autofmap t
t' <- t
return [InstanceD [] (AppT (ConT cls) t') [ValD (VarP member) (NormalB e) []]]