-
Notifications
You must be signed in to change notification settings - Fork 256
/
Copy pathInfer.hs
211 lines (162 loc) · 5.79 KB
/
Infer.hs
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
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Infer where
import Prelude hiding (foldr)
import Type
import Syntax
import Control.Monad.State
import Control.Monad.Except
import Data.Monoid
import Data.List (nub)
import Data.Foldable (foldr)
import qualified Data.Map as Map
import qualified Data.Set as Set
newtype TypeEnv = TypeEnv (Map.Map Var Scheme) deriving Monoid
data Unique = Unique { count :: Int }
type Infer a = ExceptT TypeError (State Unique) a
type Subst = Map.Map TVar Type
data TypeError
= UnificationFail Type Type
| InfiniteType TVar Type
| UnboundVariable String
| GenericTypeError
runInfer :: Infer (Subst, Type) -> Either TypeError Scheme
runInfer m = do
res <- evalState (runExceptT m) initUnique
return (closeOver res)
closeOver :: (Map.Map TVar Type, Type) -> Scheme
closeOver (sub, ty) = normalize sc
where sc = generalize emptyTyenv (apply sub ty)
initUnique :: Unique
initUnique = Unique { count = 0 }
extend :: TypeEnv -> (Var, Scheme) -> TypeEnv
extend (TypeEnv env) (x, s) = TypeEnv $ Map.insert x s env
emptyTyenv :: TypeEnv
emptyTyenv = TypeEnv Map.empty
typeof :: TypeEnv -> Var -> Maybe Type.Scheme
typeof (TypeEnv env) name = Map.lookup name env
class Substitutable a where
apply :: Subst -> a -> a
ftv :: a -> Set.Set TVar
instance Substitutable Type where
apply _ (TCon a) = TCon a
apply s t@(TVar a) = Map.findWithDefault t a s
apply s (t1 `TArr` t2) = apply s t1 `TArr` apply s t2
ftv TCon{} = Set.empty
ftv (TVar a) = Set.singleton a
ftv (t1 `TArr` t2) = ftv t1 `Set.union` ftv t2
instance Substitutable Scheme where
apply s (Forall as t) = Forall as $ apply s' t
where s' = foldr Map.delete s as
ftv (Forall as t) = ftv t `Set.difference` Set.fromList as
instance Substitutable a => Substitutable [a] where
apply = fmap . apply
ftv = foldr (Set.union . ftv) Set.empty
instance Substitutable TypeEnv where
apply s (TypeEnv env) = TypeEnv $ Map.map (apply s) env
ftv (TypeEnv env) = ftv $ Map.elems env
nullSubst :: Subst
nullSubst = Map.empty
compose :: Subst -> Subst -> Subst
s1 `compose` s2 = Map.map (apply s1) s2 `Map.union` s1
unify :: Type -> Type -> Infer Subst
unify (l `TArr` r) (l' `TArr` r') = do
s1 <- unify l l'
s2 <- unify (apply s1 r) (apply s1 r')
return (s2 `compose` s1)
unify (TVar a) t = bind a t
unify t (TVar a) = bind a t
unify (TCon a) (TCon b) | a == b = return nullSubst
unify t1 t2 = throwError $ UnificationFail t1 t2
bind :: TVar -> Type -> Infer Subst
bind a t | t == TVar a = return nullSubst
| occursCheck a t = throwError $ InfiniteType a t
| otherwise = return $ Map.singleton a t
occursCheck :: Substitutable a => TVar -> a -> Bool
occursCheck a t = a `Set.member` ftv t
letters :: [String]
letters = [1..] >>= flip replicateM ['a'..'z']
fresh :: Infer Type
fresh = do
s <- get
put s{count = count s + 1}
return $ TVar $ TV (letters !! count s)
instantiate :: Scheme -> Infer Type
instantiate (Forall as t) = do
as' <- mapM (const fresh) as
let s = Map.fromList $ zip as as'
return $ apply s t
generalize :: TypeEnv -> Type -> Scheme
generalize env t = Forall as t
where as = Set.toList $ ftv t `Set.difference` ftv env
ops :: Binop -> Type
ops Add = typeInt `TArr` typeInt `TArr` typeInt
ops Mul = typeInt `TArr` typeInt `TArr` typeInt
ops Sub = typeInt `TArr` typeInt `TArr` typeInt
ops Eql = typeInt `TArr` typeInt `TArr` typeBool
lookupEnv :: TypeEnv -> Var -> Infer (Subst, Type)
lookupEnv (TypeEnv env) x = do
case Map.lookup x env of
Nothing -> throwError $ UnboundVariable (show x)
Just s -> do t <- instantiate s
return (nullSubst, t)
infer :: TypeEnv -> Expr -> Infer (Subst, Type)
infer env ex = case ex of
Var x -> lookupEnv env x
Lam x e -> do
tv <- fresh
let env' = env `extend` (x, Forall [] tv)
(s1, t1) <- infer env' e
return (s1, apply s1 tv `TArr` t1)
App e1 e2 -> do
tv <- fresh
(s1, t1) <- infer env e1
(s2, t2) <- infer (apply s1 env) e2
s3 <- unify (apply s2 t1) (TArr t2 tv)
return (s3 `compose` s2 `compose` s1, apply s3 tv)
Let x e1 e2 -> do
(s1, t1) <- infer env e1
let env' = apply s1 env
t' = generalize env' t1
(s2, t2) <- infer (env' `extend` (x, t')) e2
return (s2 `compose` s1, t2)
If cond tr fl -> do
tv <- fresh
inferPrim env [cond, tr, fl] (typeBool `TArr` tv `TArr` tv `TArr` tv)
Fix e1 -> do
tv <- fresh
inferPrim env [e1] ((tv `TArr` tv) `TArr` tv)
Op op e1 e2 -> do
inferPrim env [e1, e2] (ops op)
Lit (LInt _) -> return (nullSubst, typeInt)
Lit (LBool _) -> return (nullSubst, typeBool)
inferPrim :: TypeEnv -> [Expr] -> Type -> Infer (Subst, Type)
inferPrim env l t = do
tv <- fresh
(s1, tf) <- foldM inferStep (nullSubst, id) l
s2 <- unify (apply s1 (tf tv)) t
return (s2 `compose` s1, apply s2 tv)
where inferStep (s, tf) exp = do
(s', t) <- infer (apply s env) exp
return (s' `compose` s, tf . (TArr t))
inferExpr :: TypeEnv -> Expr -> Either TypeError Scheme
inferExpr env = runInfer . infer env
inferTop :: TypeEnv -> [(String, Expr)] -> Either TypeError TypeEnv
inferTop env [] = Right env
inferTop env ((name, ex):xs) = do
ty <- (inferExpr env ex)
inferTop (extend env (name, ty)) xs
normalize :: Scheme -> Scheme
normalize (Forall ts body) = Forall (fmap snd ord) (normtype body)
where
ord = zip (nub $ fv body) (fmap TV letters)
fv (TVar a) = [a]
fv (TArr a b) = fv a ++ fv b
fv (TCon _) = []
normtype (TArr a b) = TArr (normtype a) (normtype b)
normtype (TCon a) = TCon a
normtype (TVar a) =
case lookup a ord of
Just x -> TVar x
Nothing -> error "type variable not in signature"