-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathalgorithmw.hs
More file actions
296 lines (237 loc) · 9.8 KB
/
algorithmw.hs
File metadata and controls
296 lines (237 loc) · 9.8 KB
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import qualified Data.Map as Map
import qualified Data.Set as Set
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.State
import qualified Text.PrettyPrint as PP
data Exp = EVar String
| ELit Lit
| EApp Exp Exp
| EAbs String Exp
| ELet String Exp Exp
deriving (Eq, Ord)
data Lit = LInt Integer
| LBool Bool
deriving (Eq, Ord)
data Type = TVar String
| TInt
| TBool
| TFun Type Type
deriving (Eq, Ord)
data Scheme = Scheme [String] Type
class Types a where
ftv :: a -> Set.Set String
apply :: Subst -> a -> a
instance Types Type where
ftv (TVar n) = Set.singleton n
ftv TInt = Set.empty
ftv TBool = Set.empty
ftv (TFun t1 t2) = ftv t1 `Set.union` ftv t2
apply s (TVar n) = case Map.lookup n s of
Nothing -> TVar n
Just t -> t
apply s (TFun t1 t2) = TFun (apply s t1) (apply s t2)
apply s t = t
instance Types Scheme where
ftv (Scheme vars t) = (ftv t) `Set.difference` (Set.fromList vars)
apply s (Scheme vars t) = Scheme vars (apply (foldr Map.delete s vars) t)
instance Types a => Types [a] where
apply s = map (apply s)
ftv l = foldr Set.union Set.empty (map ftv l)
type Subst = Map.Map String Type
nullSubst :: Subst
nullSubst = Map.empty
composeSubst :: Subst -> Subst -> Subst
composeSubst s1 s2 = (Map.map (apply s1) s2) `Map.union` s1
newtype TypeEnv = TypeEnv (Map.Map String Scheme)
remove :: TypeEnv -> String -> TypeEnv
remove (TypeEnv env) var = TypeEnv (Map.delete var env)
instance Types TypeEnv where
ftv (TypeEnv env) = ftv (Map.elems env)
apply s (TypeEnv env) = TypeEnv (Map.map (apply s) env)
generalize :: TypeEnv -> Type -> Scheme
generalize env t = Scheme vars t
where vars = Set.toList ((ftv t) `Set.difference` (ftv env))
data TIEnv = TIEnv {}
data TIState = TIState { tiSupply :: Int }
type TI a = ErrorT String (ReaderT TIEnv (StateT TIState IO)) a
runTI :: TI a -> IO (Either String a, TIState)
runTI t =
do (res, st) <- runStateT (runReaderT (runErrorT t) initTIEnv) initTIState
return (res, st)
where initTIEnv = TIEnv
initTIState = TIState{tiSupply = 0}
newTyVar :: String -> TI Type
newTyVar prefix =
do s <- get
put s{tiSupply = tiSupply s + 1}
return (TVar (prefix ++ show (tiSupply s)))
instantiate :: Scheme -> TI Type
instantiate (Scheme vars t) = do nvars <- mapM (\ _ -> newTyVar "a") vars
let s = Map.fromList (zip vars nvars)
return $ apply s t
mgu :: Type -> Type -> TI Subst
mgu (TFun l r) (TFun l' r') = do s1 <- mgu l l'
s2 <- mgu (apply s1 r) (apply s1 r')
return (s1 `composeSubst` s2)
mgu (TVar u) t = varBind u t
mgu t (TVar u) = varBind u t
mgu TInt TInt = return nullSubst
mgu TBool TBool = return nullSubst
mgu t1 t2 = throwError $ "types do not unify: " ++ show t1 ++
" vs. " ++ show t2
varBind :: String -> Type -> TI Subst
varBind u t | t == TVar u = return nullSubst
| u `Set.member` ftv t = throwError $ "occurs check fails: " ++ u ++
" vs. " ++ show t
| otherwise = return (Map.singleton u t)
tiLit :: Lit -> TI (Subst, Type)
tiLit (LInt _) = return (nullSubst, TInt)
tiLit (LBool _) = return (nullSubst, TBool)
ti :: TypeEnv -> Exp -> TI (Subst, Type)
ti (TypeEnv env) (EVar n) =
case Map.lookup n env of
Nothing -> throwError $ "unbound variable: " ++ n
Just sigma -> do t <- instantiate sigma
return (nullSubst, t)
ti _ (ELit l) = tiLit l
ti env (EAbs n e) =
do tv <- newTyVar "a"
let TypeEnv env' = remove env n
env'' = TypeEnv (env' `Map.union` (Map.singleton n (Scheme [] tv)))
(s1, t1) <- ti env'' e
return (s1, TFun (apply s1 tv) t1)
ti env exp@(EApp e1 e2) =
do tv <- newTyVar "a"
(s1, t1) <- ti env e1
(s2, t2) <- ti (apply s1 env) e2
s3 <- mgu (apply s2 t1) (TFun t2 tv)
return (s3 `composeSubst` s2 `composeSubst` s1, apply s3 tv)
`catchError`
\e -> throwError $ e ++ "\n in " ++ show exp
ti env (ELet x e1 e2) =
do (s1, t1) <- ti env e1
let TypeEnv env' = remove env x
t' = generalize (apply s1 env) t1
env'' = TypeEnv (Map.insert x t' env')
(s2, t2) <- ti (apply s1 env'') e2
return (s1 `composeSubst` s2, t2)
typeInference :: Map.Map String Scheme -> Exp -> TI Type
typeInference env e =
do (s, t) <- ti (TypeEnv env) e
return (apply s t)
e0 = ELet "id" (EAbs "x" (EVar "x"))
(EVar "id")
e1 = ELet "id" (EAbs "x" (EVar "x"))
(EApp (EVar "id") (EVar "id"))
e2 = ELet "id" (EAbs "x" (ELet "y" (EVar "x") (EVar "y")))
(EApp (EVar "id") (EVar "id"))
e3 = ELet "id" (EAbs "x" (ELet "y" (EVar "x") (EVar "y")))
(EApp (EApp (EVar "id") (EVar "id")) (ELit (LInt 2)))
e4 = ELet "id" (EAbs "x" (EApp (EVar "x") (EVar "x")))
(EVar "id")
e5 = EAbs "m" (ELet "y" (EVar "m")
(ELet "x" (EApp (EVar "y") (ELit (LBool True)))
(EVar "x")))
e6 = EApp (ELit (LInt 2)) (ELit (LInt 2))
test :: Exp -> IO ()
test e =
do (res, _) <- runTI (typeInference Map.empty e)
case res of
Left err -> putStrLn $ show e ++ "\n " ++ err ++ "\n"
Right t -> putStrLn $ show e ++ " :: " ++ show t ++ "\n"
main :: IO ()
main = mapM_ test [e0, e1, e2, e3, e4, e5, e6]
-- |Collecting Constraints|
-- |main = mapM_ test' [e0, e1, e2, e3, e4, e5]|
instance Show Type where
showsPrec _ x = shows (prType x)
prType :: Type -> PP.Doc
prType (TVar n) = PP.text n
prType TInt = PP.text "Int"
prType TBool = PP.text "Bool"
prType (TFun t s) = prParenType t PP.<+> PP.text "->" PP.<+> prType s
prParenType :: Type -> PP.Doc
prParenType t = case t of
TFun _ _ -> PP.parens (prType t)
_ -> prType t
instance Show Exp where
showsPrec _ x = shows (prExp x)
prExp :: Exp -> PP.Doc
prExp (EVar name) = PP.text name
prExp (ELit lit) = prLit lit
prExp (ELet x b body) = PP.text "let" PP.<+>
PP.text x PP.<+> PP.text "=" PP.<+>
prExp b PP.<+> PP.text "in" PP.$$
PP.nest 2 (prExp body)
prExp (EApp e1 e2) = prExp e1 PP.<+> prParenExp e2
prExp (EAbs n e) = PP.char '\\' PP.<> PP.text n PP.<+>
PP.text "->" PP.<+>
prExp e
prParenExp :: Exp -> PP.Doc
prParenExp t = case t of
ELet _ _ _ -> PP.parens (prExp t)
EApp _ _ -> PP.parens (prExp t)
EAbs _ _ -> PP.parens (prExp t)
_ -> prExp t
instance Show Lit where
showsPrec _ x = shows (prLit x)
prLit :: Lit -> PP.Doc
prLit (LInt i) = PP.integer i
prLit (LBool b) = if b then PP.text "True" else PP.text "False"
instance Show Scheme where
showsPrec _ x = shows (prScheme x)
prScheme :: Scheme -> PP.Doc
prScheme (Scheme vars t) = PP.text "All" PP.<+>
PP.hcat
(PP.punctuate PP.comma (map PP.text vars))
PP.<> PP.text "." PP.<+> prType t
test' :: Exp -> IO ()
test' e =
do (res, _) <- runTI (bu Set.empty e)
case res of
Left err -> putStrLn $ "error: " ++ err
Right t -> putStrLn $ show e ++ " :: " ++ show t
data Constraint = CEquivalent Type Type
| CExplicitInstance Type Scheme
| CImplicitInstance Type (Set.Set String) Type
instance Show Constraint where
showsPrec _ x = shows (prConstraint x)
prConstraint :: Constraint -> PP.Doc
prConstraint (CEquivalent t1 t2) = PP.hsep [prType t1, PP.text "=", prType t2]
prConstraint (CExplicitInstance t s) =
PP.hsep [prType t, PP.text "<~", prScheme s]
prConstraint (CImplicitInstance t1 m t2) =
PP.hsep [prType t1,
PP.text "<=" PP.<>
PP.parens (PP.hcat (PP.punctuate PP.comma (map PP.text (Set.toList m)))),
prType t2]
type Assum = [(String, Type)]
type CSet = [Constraint]
bu :: Set.Set String -> Exp -> TI (Assum, CSet, Type)
bu m (EVar n) = do b <- newTyVar "b"
return ([(n, b)], [], b)
bu m (ELit (LInt _)) = do b <- newTyVar "b"
return ([], [CEquivalent b TInt], b)
bu m (ELit (LBool _)) = do b <- newTyVar "b"
return ([], [CEquivalent b TBool], b)
bu m (EApp e1 e2) =
do (a1, c1, t1) <- bu m e1
(a2, c2, t2) <- bu m e2
b <- newTyVar "b"
return (a1 ++ a2, c1 ++ c2 ++ [CEquivalent t1 (TFun t2 b)],
b)
bu m (EAbs x body) =
do b@(TVar vn) <- newTyVar "b"
(a, c, t) <- bu (vn `Set.insert` m) body
return (a `removeAssum` x, c ++ [CEquivalent t' b | (x', t') <- a,
x == x'], TFun b t)
bu m (ELet x e1 e2) =
do (a1, c1, t1) <- bu m e1
(a2, c2, t2) <- bu (x `Set.delete` m) e2
return (a1 ++ removeAssum a2 x,
c1 ++ c2 ++ [CImplicitInstance t' m t1 |
(x', t') <- a2, x' == x], t2)
removeAssum [] _ = []
removeAssum ((n', _) : as) n | n == n' = removeAssum as n
removeAssum (a:as) n = a : removeAssum as n