+
+unifies :: Type Type -> Solve Unifier
+unifies t1 t2
+ | t1 === t2 = pure ('DM'.newMap, [])
+unifies (VarType v) t = tbind v t
+unifies t (VarType v) = tbind v t
+unifies (Arrow t1 t2) (Arrow t3 t4) = unifyMany [t1, t2] [t3, t4]
+unifies t1 t2 = liftT (Left (UnificationFail t1 t2))
+
+unifyMany :: [Type] [Type] -> Solve Unifier
+unifyMany [] [] = pure ('DM'.newMap, [])
+unifyMany [t1:ts1] [t2:ts2] = unifies t1 t2
+ >>= \(su1, cs1)->unifyMany (apply su1 ts1) (apply su1 ts2)
+ >>= \(su2, cs2)->pure (su2 `compose` su1, cs1 ++ cs2)
+unifyMany t1 t2 = liftT (Left (UnificationMismatch t1 t2))
+
+(`compose`) infix 1 :: Subst Subst -> Subst
+(`compose`) s1 s2 = 'DM'.union (apply s1 <$> s2) s1
+
+tbind :: String Type -> Solve Unifier
+tbind a t
+ | t === VarType a = pure ('DM'.newMap, [])
+ | occursCheck a t = liftT $ Left $ InfiniteType a t
+ = pure $ ('DM'.singleton a t, [])
+
+occursCheck :: String a -> Bool | Substitutable a
+occursCheck a t = 'DS'.member a $ ftv t
+
+solver :: Solve Subst
+solver = getState >>= \(su, cs)->case cs of
+ [] = pure su
+ [(t1, t2):cs0] = unifies t1 t2
+ >>= \(su1, cs1)->'MS'.put (su1 `compose` su, cs1 ++ (apply su1 cs0))
+ >>| solver