module Data.Thorn.Functor (
autofmap, autofmaptype, autofmapdec, autofunctorize
, Variance(..)
, autovariance
) where
import Data.Thorn.Internal
import Language.Haskell.TH
import Data.Maybe
import Data.List
import qualified Data.Sequence as S
import qualified Data.Foldable as F
import Data.Monoid
import Control.Applicative
import Control.Monad.State
autofmap :: TypeQ -> ExpQ
autofmap t = do
(n,tx) <- t >>= type2typex [] [] >>= applySpecial 0
u <- unique
(e,(txnmes,bs)) <- runStateT (autofmap' u tx) ([],S.replicate n False)
let txnmes' = filter (\(_,nm,_) -> isJust nm) txnmes
return $ LamE (map (\i -> if S.index bs i then newFuncP (i+u) else WildP) [0..n1]) (LetE (fmap (\(_,Just nm,Just e') -> ValD (VarP nm) (NormalB e') []) txnmes') e)
autofmap',autofmap'' :: Unique -> Typex -> StateT ([(Typex,Maybe Name,Maybe Exp)],S.Seq Bool) Q Exp
autofmap' u tx = do
(txnmes,bs) <- get
case find (\(tx',_,_)->tx==tx') txnmes of
Just (_,Just nm,_) -> return (VarE nm)
Just (_,Nothing,_) -> do
u2 <- unique
let nm = newFmap u2
put (map (\(tx',nm',e) -> if tx==tx' then (tx,Just nm,e) else (tx',nm',e)) txnmes, bs)
return (VarE nm)
Nothing -> autofmap'' u tx
autofmap'' _ (VarTx _) = return idE
autofmap'' _ (BasicTx _) = return idE
autofmap'' _ (FixedTx _) = return idE
autofmap'' _ NotTx = fail "Thorn doesn't work well, sorry."
autofmap'' _ (FuncTx _) = fail "Thorn doesn't accept such a type with a kind * -> k, sorry."
autofmap'' u (DataTx nm vmp cxs) = do
(txnmes,bs) <- get
put ((tx0,Nothing,Nothing) : txnmes, bs)
u2 <- unique
e <- LamE [newVarP u2] <$> (CaseE (newVarE u2) <$> (mapM go cxs))
(txnmes',bs') <- get
put (map (\(tx,nm',e') -> if tx==tx0 then (tx,nm',Just e) else (tx,nm',e')) txnmes', bs')
return e
where go (nm',txs) = do
(u2,es) <- autofmapmap u txs
return $ Match (ConP nm' (map newVarP [u2..u2+length txs1])) (NormalB (applistE (ConE nm') es)) []
tx0 = SeenDataTx nm vmp
autofmap'' _ (SeenDataTx _ _) = fail "Thorn doesn't work well, sorry."
autofmap'' u (TupleTx txs) = do
(u2,es) <- autofmapmap u txs
return $ LamE [TupP (map newVarP [u2..u2+length txs1])] (TupE es)
autofmap'' u (ArrowTx txa txb) = do
fa <- autofmap' u txa
fb <- autofmap' u txb
u2 <- unique
return $ LamE [newVarP u2, newVarP (u2+1)] (AppE fb (AppE (newVarE u2) (AppE fa (newVarE (u2+1)))))
autofmap'' u (ListTx tx) = autofmap' u tx >>= \f -> return $ AppE (mkNameE "map") f
autofmap'' u (SpecialTx n) = do
(txnmes,bs) <- get
put (txnmes,S.update n True bs)
return $ newFuncE (u+n)
autofmapmap :: Unique -> [Typex] -> StateT ([(Typex,Maybe Name,Maybe Exp)],S.Seq Bool) Q (Unique,[Exp])
autofmapmap u txs = do
u2 <- unique
es <- mapM (\(i,tx) -> autofmap' u tx >>= \e -> return $ AppE e (newVarE i)) (zip [u2..u2+length txs1] txs)
return (u2,es)
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` Co = Co
Contra `mappend` Contra = Contra
_ `mappend` _ = Fixed
mempty = Free
neg :: Variance -> Variance
neg Co = Contra
neg Contra = Co
neg Free = Free
neg Fixed = Fixed
includes :: Variance -> Variance -> Bool
includes _ Free = True
includes Free _ = False
includes Fixed _ = True
includes _ Fixed = False
includes Co Co = True
includes Contra Contra = True
includes _ _ = False
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 >>= type2typex [] [] >>= applySpecial 0
(_,sq) <- runStateT (autovariance' Co [] tx) (S.replicate n Free)
return $ (F.toList sq)
autovariance' :: Variance -> [(Name,[Conx],Variance)] -> Typex -> StateT (S.Seq Variance) Q ()
autovariance' _ _ (VarTx _) = return ()
autovariance' _ _ (BasicTx _) = return ()
autovariance' v _ (SpecialTx n) = do
sq <- get
put $ S.adjust (<>v) n sq
autovariance' _ _ (FixedTx _) = return ()
autovariance' _ _ NotTx = fail "Thorn doesn't work well, sorry."
autovariance' _ _ (FuncTx _) = fail "Thorn doesn't accept such a type with a kind * -> k, sorry."
autovariance' v dts (DataTx nm _ cxs) = mapM_ (mapM_ (autovariance' v ((nm,cxs,v):dts)) . cxtxs) cxs
autovariance' v dts (SeenDataTx nm _)
| v' `includes` v = return ()
| otherwise = mapM_ (mapM_ (autovariance' v dts') . cxtxs) cxs
where Just (_,cxs,v') = find (\(nm',_,_) -> nm==nm') dts
dts' = map (\tpl@(nm',_,_) -> if nm==nm' then (nm',cxs,v<>v') else tpl) dts
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
autofmaptype :: TypeQ -> TypeQ
autofmaptype t = do
tx <- type2typex [] [] =<< t
vs <- autovarianceRaw t
let ivs = zip [0..length vs1] vs
a i = mkNameTx ("a"++show i)
b i = mkNameTx ("b"++show i)
c i = mkNameTx ("c"++show i)
a' i = mkName ("a"++show i)
b' i = mkName ("b"++show i)
c' i = mkName ("c"++show i)
gofunc (i,Co) = ArrowTx (a i) (b i)
gofunc (i,Contra) = ArrowTx (b i) (a i)
gofunc (i,Free) = a i
gofunc (i,Fixed) = ArrowTx (a i) (a i)
gosrc (i,Co) = a i
gosrc (i,Contra) = a i
gosrc (i,Free) = b i
gosrc (i,Fixed) = a i
godst (i,Co) = b i
godst (i,Contra) = b i
godst (i,Free) = c i
godst (i,Fixed) = a i
gonm (i,Co) = [a' i,b' i]
gonm (i,Contra) = [a' i,b' i]
gonm (i,Free) = [a' i,b' i,c' i]
gonm (i,Fixed) = [a' i]
tvs = map PlainTV $ concatMap gonm ivs
funcs <- mapM (typex2type . gofunc) ivs
src <- typex2type =<< applistTx tx (map gosrc ivs)
dst <- typex2type =<< applistTx tx (map godst ivs)
return $ ForallT tvs [] (foldr1 (\ta tb -> applistT ArrowT [ta,tb]) (funcs++[src]++[dst]))
autofmapdec :: String -> TypeQ -> DecsQ
autofmapdec = gendec1 autofmap autofmaptype
autofunctorize :: TypeQ -> DecsQ
autofunctorize t = do
vs <- autovarianceRaw t
case vs of
[Co] -> functor
[Contra] -> contravariant
[Free] -> (++) <$> functor <*> contravariant
[Co,Co] -> bifunctor
[Contra,Co] -> profunctor
[Free,Co] -> (++) <$> bifunctor <*> profunctor
_ -> fail "Thorn doesn't know any suitable functor class for this variance, sorry."
where go cls member = do
e <- autofmap t
t' <- normalizetype =<< t
return [InstanceD [] (AppT (ConT cls) t') [ValD (VarP member) (NormalB e) []]]
functor = go (mkName "Prelude.Functor") (mkName "fmap")
contravariant = go (mkName "Data.Functor.Contravariant.Contravariant") (mkName "contramap")
bifunctor = go (mkName "Data.Bifunctor.Bifunctor") (mkName "bimap")
profunctor = go (mkName "Data.Profunctor.Profunctor") (mkName "dimap")