summaryrefslogtreecommitdiff
path: root/lambda-calcul/haskell/src/Minilang/Lambda/Unify.hs
blob: ad06552f847c5f0a0c56af843eccd5195e3c3ee1 (plain)
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
--  ported from https://www.cs.cornell.edu/courses/cs3110/2011sp/Lectures/lec26-type-inference/type-inference.htm
module Minilang.Lambda.Unify where

import Data.Text (Text)

type Identifier = Text

data Type
  = TyVar Text
  | Type :-> Type
  deriving (Show, Eq)

infixr 5 :->

-- invariant: no identifier on the left hand-side can appear in
-- an earlier term in the list, ie. the list is in "dependency order"
-- and forms a DAG.
type Substitution = [(Identifier, Type)]

occurs :: Identifier -> Type -> Bool
occurs x (TyVar y) = x == y
occurs x (f :-> g) = occurs x f || occurs x g

subst :: Type -> Identifier -> Type -> Type
subst s x t@(TyVar y)
  | x == y = s
  | otherwise = t
subst s x (f :-> g) = subst s x f :-> subst s x g

apply :: Substitution -> Type -> Type
apply subs t =
  foldr (\(x, s) -> subst s x) t subs

data UnifyError
  = MismatchHead Text Text [Type] [Type]
  | Circularity Identifier Type
  deriving (Show, Eq)

unifyOne :: Type -> Type -> Either UnifyError Substitution
unifyOne (TyVar x) (TyVar y) = if x == y then Right [] else Right [(x, TyVar y)]
unifyOne (f :-> g) (f' :-> g') =
  unify [(f, f'), (g, g')]
unifyOne (TyVar x) t =
  if occurs x t
    then Left $ Circularity x t
    else Right [(x, t)]
unifyOne t (TyVar x) =
  if occurs x t
    then Left $ Circularity x t
    else Right [(x, t)]

unify :: [(Type, Type)] -> Either UnifyError Substitution
unify [] = Right []
unify ((s, t) : rest) = do
  sub2 <- unify rest
  sub1 <- unifyOne (apply sub2 s) (apply sub2 t)
  return $ sub1 ++ sub2