File: UniplateDirect.hs

package info (click to toggle)
haskell-derive 2.5.16-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 460 kB
  • sloc: haskell: 3,686; makefile: 5
file content (220 lines) | stat: -rw-r--r-- 7,810 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
{-# LANGUAGE PatternGuards #-}
-- NOTE: Cannot be guessed as it relies on type information

-- | Derive Uniplate and Biplate using the Direct combinators.
--   You must request monomorphic instances, i.e:
--
-- > data Foo a = Foo a (Bool, a)
-- >
-- > {-!
-- > deriving instance UniplateDirect (Foo Int)
-- > deriving instance UniplateDirect (Bool, Int) Int
-- > deriving instance UniplateDirect (Foo Int) Int
-- > !-}
--
--   This will generate the instances @Uniplate (Foo Int)@,
--   @Biplate (Bool, Int) Int@ and @Biplate (Foo Int) Int@.
--   Generally, start with the instance you are after (e.g. @UniplateDirect (Foo Int)@),
--   try to compile and add further instances as necessary. @UniplateDirect@ with
--   one argument derives Uniplate, and with two arguments derives Biplate.
--
--   @deriving UniplateDirect@ on a data type with no arguments derives Uniplate
--   with all type parameters defaulting to @()@.
module Data.Derive.UniplateDirect(makeUniplateDirect) where


{-
import "uniplate" Data.Generics.Uniplate.Direct


-- test tuples
test :: UniplateDirect ((), Maybe ()) ()
instance Biplate ((), Maybe ()) () where
    {-# INLINE biplate #-}
    biplate (x1, x2) = plate (,) |* x1 |+ x2

test :: UniplateDirect (Sample Int)
instance Uniplate (Sample Int) where
    {-# INLINE uniplate #-}
    uniplate x = plate x

test :: UniplateDirect (Sample Int) Int
instance Biplate (Sample Int) Int where
    {-# INLINE biplate #-}
    biplate (Second x1 x2) = plate Second |* x1 |* x2
    biplate (Third x1) = plate Third |* x1
    biplate x = plate x

test :: UniplateDirect Computer
instance Uniplate Computer where
    {-# INLINE uniplate #-}
    uniplate x = plate x

test :: UniplateDirect Computer Computer
instance Biplate Computer Computer where
    {-# INLINE biplate #-}
    biplate = plateSelf

test :: UniplateDirect Computer Double
instance Biplate Computer Double where
    {-# INLINE biplate #-}
    biplate (Laptop x1 x2) = plate Laptop |* x1 |- x2
    biplate x = plate x

test :: UniplateDirect (Assoced (Maybe Bool)) Char
instance Biplate (Assoced (Maybe Bool)) Char where
    {-# INLINE biplate #-}
    biplate (Assoced x1 x2) = plate (Assoced x1) ||* x2

-- test following external declarations
test :: UniplateDirect (Either Bool Computer) Int
instance Biplate (Either Bool Computer) Int where
    {-# INLINE biplate #-}
    biplate (Right x1) = plate Right |+ x1
    biplate x = plate x

-- test recursive bits
test :: UniplateDirect (List Int) Bool
instance Biplate (List Int) Bool where
    {-# INLINE biplate #-}
    biplate x = plate x
-}

import Language.Haskell
import Data.Generics.Uniplate.DataOnly
import Data.Derive.Internal.Derivation
import Data.Maybe
import qualified Data.Map as Map
import Control.Arrow
import Control.Monad.Trans.State


makeUniplateDirect :: Derivation
makeUniplateDirect = derivationParams "UniplateDirect" $ \args grab (_,ty) -> simplify $
    let known = map (declName &&& id) knownCtors
        grab2 x = fromMaybe (grab x) $ lookup x known
    in case args of
        _ | not $ null [() | TyVar _ <- universeBi args] -> error "UniplateDirect only accepts monomorphic types"
        [] -> make True grab2 x x
            where x = tyApps (tyCon $ dataDeclName ty) $ replicate (dataDeclArity ty) $ TyCon $ Special UnitCon
        [x] -> make True grab2 x x
        [x,y] -> make False grab2 x y
        _ -> error $ "UniplateDirect requires exactly one or two arguments, got " ++ show (length args)
        

make :: Bool -> (String -> DataDecl) -> Type -> Type -> Either String [Decl]
make uni grab from to = Right [InstDecl sl [] (UnQual $ Ident $ if uni then "Uniplate" else "Biplate") (from : [to | not uni])
        [InsDecl $ InlineSig sl True AlwaysActive (qname $ if uni then "uniplate" else "biplate"), InsDecl ms]]
    where
        ty = grab $ tyRoot from
        match pat bod = Match sl (Ident $ if uni then "uniplate" else "biplate") [pat] Nothing (UnGuardedRhs bod) (BDecls [])
        ms = if uni || from /= to
             then FunBind $ map (uncurry match) (catMaybes bods) ++ [match (pVar "x") (var "plate" `App` var "x") | any isNothing bods]
             else PatBind sl (pVar "biplate") Nothing (UnGuardedRhs $ var "plateSelf") (BDecls [])
        bods = run (fromTyParens to) $ mapM (make1 grab) $ substData from ty


make1 :: (String -> DataDecl) -> (String,[Type]) -> S (Maybe (Pat, Exp))
make1 grab (name,tys) = do
    ops <- mapM (fmap show . operator grab) tys
    let vars = ['x':show i | i <- [1..length tys]]
        pat = PParen $ PApp (qname name) $ map pVar vars
        (good,bad) = span ((==) "|-" . fst) $ zip ops $ map var vars
        bod = foldl (\x (y,z) -> InfixApp x (QVarOp $ UnQual $ Symbol y) z) (App (var "plate") $ paren $ apps (con name) (map snd good)) bad
    return $ if all (== "|-") ops then Nothing else Just (pat,bod)


data Ans = Hit | Miss | Try | ListHit | ListTry deriving Eq

instance Show Ans where
    show Hit = "|*"
    show Miss = "|-"
    show Try = "|+"
    show ListHit = "||*"
    show ListTry = "||+"

ansList Hit = ListHit
ansList Miss = Miss
ansList _ = ListTry


ansJoin (Miss:xs) = ansJoin xs
ansJoin [] = Miss
ansJoin _ = Try


type S a = State (Map.Map Type Ans) a

run :: Type -> S a -> a
run to act = evalState act (Map.singleton to Hit)

operator :: (String -> DataDecl) -> Type -> S Ans
operator grab from = do
    mp <- get
    case Map.lookup from mp of
        Just y -> return y
        Nothing -> do
            fix Miss
    where
        fix ans = do
            s <- get
            modify $ Map.insert from ans
            ans2 <- operator2 grab from
            if ans == ans2
                then return ans
                else put s >> fix ans2


operator2 :: (String -> DataDecl) -> Type -> S Ans
operator2 grab from
    | isTyFun from = return Try
    | Just from2 <- fromTyList from = fmap ansList $ operator grab from2
    | otherwise = case subst from $ grab $ tyRoot from of
        Left from2 -> operator grab from2
        Right ctrs -> fmap ansJoin $ mapM (operator grab) $ concatMap snd ctrs


subst :: Type -> Decl -> Either Type [(String,[Type])]
subst ty x@TypeDecl{} = Left $ substType ty x
subst ty x = Right $ substData ty x

substData :: Type -> Decl -> [(String,[Type])]
substData ty dat = [(ctorDeclName x, map (fromTyParens . transform f . fromBangType . snd) $ ctorDeclFields x) | x <- dataDeclCtors dat]
    where
        rep = zip (dataDeclVars dat) (snd $ fromTyApps $ fromTyParen ty)
        f (TyVar x) = fromMaybe (TyVar x) $ lookup (prettyPrint x) rep
        f x = x

substType :: Type -> Decl -> Type
substType ty (TypeDecl _ _ vars d) = fromTyParens $ transform f d
    where
        rep = zip (map prettyPrint vars) (snd $ fromTyApps ty)
        f (TyVar x) = fromMaybe (TyVar x) $ lookup (prettyPrint x) rep
        f x = x


knownCtors :: [Decl]
knownCtors = map (fromParseResult . parseDecl)
    ["data Int = Int"
    ,"data Bool = Bool"
    ,"data Char = Char"
    ,"data Double = Double"
    ,"data Float = Float"
    ,"data Integer = Integer"
    ,"data Maybe a = Nothing | Just a"
    ,"data Either a b = Left a | Right b"
    ,"type Rational = Ratio Integer"
    ,"data (Integral a) => Ratio a = !a :% !a"
    ,"type String = [Char]"
    ] ++
    listCtor :
    map tupleDefn (0:[2..32])

listCtor = DataDecl sl  DataType [] (Ident "[]") [UnkindedVar $ Ident "a"]
    [QualConDecl sl [] [] $ ConDecl (Ident "[]") []
    ,QualConDecl sl [] [] $ ConDecl (Ident "(:)") [UnBangedTy $ tyVar "a", UnBangedTy $ TyList $ tyVar "a"]] []

tupleDefn :: Int -> Decl
tupleDefn n = DataDecl sl DataType [] (Ident s) (map (UnkindedVar . Ident) vars) [QualConDecl sl [] [] $ ConDecl (Ident s) (map (UnBangedTy . tyVar) vars)] []
    where s = "(" ++ replicate (n - 1) ',' ++ ")"
          vars = ['v':show i | i <- [1..n]]