summaryrefslogtreecommitdiff
path: root/src/Obs/Typing.idr
blob: c5d837f698df4a88902ddccb75904ce70098cdc2 (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
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
module Obs.Typing

import Data.Vect

import Obs.Logging
import Obs.NormalForm
import Obs.Sort
import Obs.Substitution
import Obs.Term

import System

import Text.Bounded
import Text.PrettyPrint.Prettyprinter
import Text.PrettyPrint.Prettyprinter.Render.Terminal

%default total

-- Loggings ----------------------------------------------------------------------

Rename (Logging ann . NormalForm) where
  rename t f = pure $ rename !t f

mismatch : Bounds -> Doc ann -> Doc ann -> Logging ann a
mismatch bounds lhs rhs = fatal $
  MkBounded
    (pretty "expected" <++> lhs <+> comma <+> softline <+> pretty "got" <++> rhs)
    False
    bounds

typeMismatch : Bounds -> Doc ann -> Doc ann -> Logging ann a
typeMismatch bounds lhs rhs = inScope "type mismatch" $ mismatch bounds lhs rhs

sortMismatch : Bounds -> Doc ann -> Doc ann -> Logging ann a
sortMismatch bounds lhs rhs = inScope "sort mismatch" $ mismatch bounds lhs rhs

-- Substitution ----------------------------------------------------------------

wkn : Vect k String -> (Fin n -> Logging ann (NormalForm m)) -> Fin (k + n) -> Logging ann (NormalForm (k + m))
wkn []            f = f
wkn (var :: vars) f =
  add
    (Logging ann . NormalForm)
    (pure $ Ntrl $ Var var FZ)
    (\i => pure $ rename !(wkn vars f i) FS)

covering partial
substCnstr : Constructor n -> (Fin n -> Logging ann (NormalForm m)) -> Logging ann (Constructor m)
covering partial
substNtrl  : Neutral n -> (Fin n -> Logging ann (NormalForm m)) -> Logging ann (NormalForm m)
covering partial
subst      : NormalForm n -> (Fin n -> Logging ann (NormalForm m)) -> Logging ann (NormalForm m)
covering partial
doApp      : NormalForm n -> NormalForm n -> Logging ann (NormalForm n)

substCnstr (Sort s) f = pure $ Sort s
substCnstr (Pi s s' var a b) f = do
  a <- subst a f
  b <- subst b (wkn [var] f)
  pure (Pi s s' var a b)
substCnstr (Lambda var t) f = do
  t <- subst t (wkn [var] f)
  pure (Lambda var t)
substCnstr Top f = pure $ Top
substCnstr Bottom  f = pure $ Bottom

substNtrl (Var var i) f = do
  Ntrl (Var "" j) <- f i
    | t => pure t
  pure (Ntrl (Var var j))
substNtrl (App t u) f = do
  t <- substNtrl t f
  u <- subst u f
  doApp t u
substNtrl Absurd f = pure $ Ntrl Absurd

subst (Ntrl t)  f = substNtrl t f
subst (Cnstr t) f = map Cnstr $ substCnstr t f
subst Irrel     f = pure Irrel

doApp (Ntrl t) u = pure $ Ntrl (App t u)
doApp Irrel u = pure $ Irrel
doApp (Cnstr (Lambda var t)) u = subst t (add (Logging ann . NormalForm) (pure u) (pure . point))
doApp (Cnstr t) u = inScope "wrong constructor in apply:" $ fatal t

partial
subst1 : NormalForm n -> NormalForm (S n) -> Logging ann (NormalForm n)
subst1 t u = subst u (add (Logging ann . NormalForm) (pure t) (pure . point))

-- Conversion ------------------------------------------------------------------

-- invariant: all definitions fully expanded
-- invariant: m |- t, u <- a : s
covering partial
convert : (t, u, a : NormalForm n) -> Sort -> Logging ann Bool
-- In sort Prop
convert Irrel Irrel a Prop = pure True
-- In unknown type in set
convert t u (Ntrl _) (Set k) = pure $ t == u
-- In type Set
convert (Cnstr (Sort s')) (Cnstr (Sort s'')) (Cnstr (Sort _)) (Set _) = pure $ s' == s''
convert (Cnstr (Pi s s' _ a b)) (Cnstr (Pi l l' _ a' b')) (Cnstr (Sort _)) (Set _) =
  pure $
  s == l && s' == l' && !(convert a a' (cast s) (suc s)) && !(convert b b' (cast s') (suc s'))
convert (Cnstr Top) (Cnstr Top) (Cnstr (Sort _)) (Set _) = pure True
convert (Cnstr Bottom) (Cnstr Bottom) (Cnstr (Sort _)) (Set _) = pure True
convert (Ntrl t) u (Cnstr (Sort s)) (Set k) = pure $ Ntrl t == u
convert t (Ntrl u) (Cnstr (Sort s)) (Set k) = pure $ t == Ntrl u
convert t u (Cnstr (Sort s)) (Set k) = pure $ False
-- In type Pi
convert t u (Cnstr (Pi s s' var a b)) (Set k) = do
  t <- doApp (weaken 1 t) (Ntrl $ Var var FZ)
  u <- doApp (weaken 1 u) (Ntrl $ Var var FZ)
  convert t u b s'
-- Default
convert t u a s =
  inScope "invalid conversion:" $ fatal $
  fillSep {ann} [prettyPrec App t, prettyPrec App u, prettyPrec App a, prettyPrec App s]


-- Typing Contexts -------------------------------------------------------------

infix 5 ::<

data TyContext : Nat -> Nat -> Type where
  Nil   : TyContext 0 0
  (:<)  : TyContext m n -> NormalForm.Definition n -> TyContext m (S n)
  (::<) : TyContext m n -> (String, NormalForm n, Sort) -> TyContext (S m) (S n)

fromContext : Context n -> TyContext 0 n
fromContext []           = []
fromContext (ctx :< def) = fromContext ctx :< def

countVars : TyContext m n -> Fin (S n)
countVars []          = FZ
countVars (ctx :< y)  = weaken $ countVars ctx
countVars (ctx ::< y) = FS $ countVars ctx

index : TyContext m n -> Fin n -> (NormalForm n, NormalForm n, Sort)
index (ctx :< def)           FZ     = (weaken 1 def.tm, weaken 1 def.ty, def.sort)
index (ctx ::< (var, ty, s)) FZ     = (Ntrl $ Var var FZ, weaken 1 ty, s)
index (ctx :< _)             (FS i) = bimap (weaken 1) (mapFst (weaken 1)) $ index ctx i
index (ctx ::< _)            (FS i) = bimap (weaken 1) (mapFst (weaken 1)) $ index ctx i

covering partial
asSubst : (ctx : TyContext m n) -> Fin n -> Logging ann (NormalForm m)
asSubst (ctx :< def)           FZ     = subst def.tm (asSubst ctx)
asSubst (ctx ::< (var, _, _))  FZ     = pure $ Ntrl $ Var var FZ
asSubst (ctx :< def)           (FS i) = asSubst ctx i
asSubst (ctx ::< _)            (FS i) = map (weaken 1) (asSubst ctx i)

-- Checking and Inference ------------------------------------------------------

covering partial
check : TyContext m n -> Term n -> NormalForm n -> Sort -> Logging ann (NormalForm n)
covering partial
infer : TyContext m n -> Term n -> Logging ann (NormalForm n, NormalForm n, Sort)
covering partial
inferType : {default typeMismatch tag : forall a . Bounds -> Doc ann -> Doc ann -> Logging ann a}
  -> TyContext m n -> Term n -> Logging ann (NormalForm n, Sort)

check ctx tm@(Lambda _ _ t) (Cnstr (Pi s s' var a b)) _ = do
  inScope "check" $ trace $ map (\_ => pretty {ann} "checking under lambda with type" <++> pretty a) (fullBounds tm)
  inScope "check" $ trace $ map (\_ => "binding new variable to \{var}") (fullBounds tm)
  inScope "check" $ trace $ map (\_ => pretty {ann} "checking for type" <++> pretty b) (fullBounds tm)
  t <- check (ctx ::< (var, a, s)) t b s'
  pure (Cnstr $ Lambda var t)
check ctx tm@(Lambda _ _ _) ty _ = typeMismatch (fullBounds tm).bounds (pretty "pi") (pretty ty)
check ctx tm ty s = do
  let bounded = fullBounds tm
  inScope "check" $ trace $ map (\_ => "checking has fallen through") bounded
  (tm, ty', s') <- infer ctx tm
  inScope "check" $ trace $ map (\_ => pretty {ann} "inferred type is" <++> pretty ty') bounded
  let True = s == s'
    | False => sortMismatch bounded.bounds (pretty s) (pretty s')
  True <- convert !(subst ty $ asSubst ctx) !(subst ty' $ asSubst ctx) (cast s) (suc s)
    | False => typeMismatch bounded.bounds (pretty ty) (pretty ty')
  inScope "check" $ trace $ map (\_ => pretty {ann} "converted" <++> pretty ty' <+> softline <+> pretty "to" <++> pretty ty) bounded
  pure tm

infer ctx tm@(Var b i) = do
  inScope "infer" $ trace $ map (\_ => "encountered variable \{b.val}@\{show i}") (fullBounds tm)
  let (t, a, s) = index ctx i
  inScope "infer" $ trace $ map (\_ => pretty {ann} "variable has type" <++> pretty a) (fullBounds tm)
  pure $ (t, a, s)
infer ctx (Sort b s) = pure $ (cast s, cast (suc s), suc (suc s))
infer ctx tm@(Pi _ var a b) = do
  inScope "infer" $ trace $ map (\_ => "encountered Pi type") (fullBounds tm)
  (a, s) <- inferType ctx a
  inScope "infer" $ trace $ map (\_ => pretty {ann} "argument has type" <++> pretty a) (fullBounds tm)
  (b, s') <- inferType (ctx ::< (var, a, s)) b
  inScope "infer" $ trace $ map (\_ => pretty {ann} "result has type" <++> pretty b <+> comma <+> softline <+> pretty "so Pi type has type" <++> pretty (s ~> s')) (fullBounds tm)
  pure (Cnstr (Pi s s' var a b), cast (s ~> s'), suc (s ~> s'))
infer ctx tm@(Lambda _ _ _) = inScope "cannot infer type" $ fatal (fullBounds tm)
infer ctx tm@(App t u) = do
  inScope "infer" $ trace $ map (\_ => "encountered application") (fullBounds tm)
  let bounded = fullBounds t
  (t, ty@(Cnstr (Pi s s' _ a b)), _) <- infer ctx t
    | (_, t, _) => inScope "wrong type to apply" $ fatal (map (\_ => t) bounded)
  inScope "infer" $ trace $ map (\_ => pretty {ann} "function has type" <++> pretty ty) (fullBounds tm)
  inScope "infer" $ trace $ map (\_ => pretty {ann} "checking argument has type" <++> pretty a) (fullBounds tm)
  u <- check ctx u a s
  inScope "infer" $ trace $ map (\_ => "argument is well typed") (fullBounds tm)
  res <- doApp t u
  ty <- subst1 u b
  inScope "infer" $ trace $ map (\_ => pretty {ann} "final result is" <++> pretty res <+> softline <+> pretty "of type" <++> pretty ty) (fullBounds tm)
  pure (res, ty, s')
infer ctx (Top b) = pure $ (Cnstr Top, cast Prop, Set 0)
infer ctx (Point b) = pure $ (Irrel, Cnstr Top, Prop)
infer ctx (Bottom b) = pure $ (Cnstr Bottom, cast Prop, Set 0)
infer ctx tm@(Absurd b a t) = do
  inScope "infer" $ trace $ map (\_ => "encountered absurd") (fullBounds tm)
  (a, s) <- inferType ctx a
  inScope "infer" $ trace $ map (\_ => pretty {ann} "will fudge to type" <++> pretty a) (fullBounds tm)
  inScope "infer" $ trace $ map (\_ => pretty {ann} "checking for proof of false") (fullBounds tm)
  _ <- check ctx t (Cnstr Bottom) Prop
  inScope "infer" $ trace $ map (\_ => "proof of false is well typed") (fullBounds tm)
  pure (Ntrl Absurd, a, s)

inferType ctx a = do
  (a, Cnstr (Sort s), _) <- infer ctx a
    | (_, ty, _) => tag (fullBounds a).bounds (pretty "sort") (pretty ty)
  pure (a, s)

-- Checking Definitions and Blocks ---------------------------------------------

covering partial
checkDef : Context n -> Term.Definition n -> Logging ann (NormalForm.Definition n)
checkDef ctx def = do
  let tyBounds = (fullBounds def.ty).bounds
  let tmBounds = (fullBounds def.tm).bounds
  (ty, sort) <-
    inferType {tag = \bounds, lhs, rhs => inScope "invalid declaration" $ mismatch bounds lhs rhs}
      (fromContext ctx) def.ty
  inScope "check" $ debug (pretty {ann} "\{def.name} has type" <++> pretty ty)
  tm <- check (fromContext ctx) def.tm ty sort
  inScope "check" $ debug (pretty {ann} "\{def.name} is well typed with value" <++> pretty tm)
  pure $ MkDefinition {name = def.name, ty, tm, sort}

covering partial
export
checkBlock : Block n -> Logging ann (Context n)
checkBlock [] = pure []
checkBlock (blk :< def) = do
  ctx <- checkBlock blk
  def <- checkDef ctx def
  pure (ctx :< def)