-
Notifications
You must be signed in to change notification settings - Fork 2
/
kindInference.js
126 lines (118 loc) · 3.4 KB
/
kindInference.js
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
const {
TApp,
TForall,
substTVar,
TVar,
} = require('./types');
const {
showKind,
pruneKind,
occursKMeta,
KFun,
kType,
} = require('./kinds');
const {
terr,
freshKMeta,
freshTSkol,
} = require('./util');
const bindKMeta = (x, t) => {
if (x.kind) return unifyKind(x.kind, t);
if (t.tag === 'KMeta' && t.kind) return unifyKind(x, t.kind);
if (occursKMeta(x, t)) return terr(`${showTy(x)} occurs in ${showTy(t)}`);
x.kind = t;
};
const unifyKind = (a, b) => {
if (a === b) return;
if (a.tag === 'KMeta') return bindKMeta(a, b);
if (b.tag === 'KMeta') return bindKMeta(b, a);
if (a.tag === 'KFun' && b.tag === 'KFun') {
unifyKind(a.left, b.left);
return unifyKind(a.right, b.right);
}
if (a.tag === 'KCon' && b.tag === 'KCon' && a.name === b.name) return;
return terr(`failed to unify kinds: ${showKind(a)} ~ ${showKind(b)}`);
};
const inferKindR = (env, t) => {
if (t.tag === 'TMeta') return [t.kind, t];
if (t.tag === 'TVar') return terr(`tvar ${t.name} in inferKindR`);
if (t.tag === 'TSkol') return [t.kind, t];
if (t.tag === 'TCon') {
if (!env.tcons[t.name])
return terr(`undefined type constructor ${showTy(t)}`);
return [env.tcons[t.name], t];
}
if (t.tag === 'TApp') {
const [l, tl] = inferKindR(env, t.left);
const [r, tr] = inferKindR(env, t.right);
const km = freshKMeta();
unifyKind(l, KFun(r, km));
return [km, tl === t.left && tr === t.right ? t : TApp(tl, tr)];
}
if (t.tag === 'TForall') {
const { tvs, type } = t;
const ks = t.ks || [];
const m = {};
const nks = Array(tvs.length);
for (let i = 0, l = tvs.length; i < l; i++) {
const ki = ks[i] || freshKMeta();
const k = freshTSkol(tvs[i], ki);
m[tvs[i]] = k;
nks[i] = ki;
}
const [km, b] = inferKindR(env, substTVar(m, type));
return [km, TForall(tvs, nks, b)];
}
};
const defaultKindInKind = k => {
if (k.tag === 'KCon') return k;
if (k.tag === 'KMeta') {
if (k.kind) return defaultKindInKind(k.kind);
return kType;
}
if (k.tag === 'KFun') {
const l = defaultKindInKind(k.left);
const r = defaultKindInKind(k.right);
return l === k.left && r === k.right ? k : KFun(l, r);
}
};
const defaultKind = t => {
if (t.tag === 'TApp') {
const l = defaultKind(t.left);
const r = defaultKind(t.right);
return l === t.left && r === t.right ? t : TApp(l, r);
}
if (t.tag === 'TForall') {
const { tvs, ks, type } = t;
const nks = ks.map(k => k ? defaultKindInKind(k) : kType);
const b = defaultKind(type);
return TForall(tvs, nks, b);
}
if (t.tag === 'TSkol')
return TVar(t.name);
if (t.tag === 'TMeta')
return terr(`tmeta ?${t.id} in defaultKind`);
return t;
};
const inferKind = (env, ty) => {
const [_, ti] = inferKindR(env, ty);
return defaultKind(ti);
};
const kindOf = (env, t) => {
if (t.tag === 'TMeta') return t.kind;
if (t.tag === 'TSkol') return t.kind;
if (t.tag === 'TCon')
return env.tcons[t.name] || terr(`undefined type constructor ${showTy(t)}`);
if (t.tag === 'TApp') {
const f = kindOf(env, t.left);
if (f.tag !== 'KFun')
return terr(`not a kind fun in left side of type application (${showTy(t)}): ${showKind(f)}`);
return f.right;
}
if (t.tag === 'TForall') return terr(`tforall ${showTy(t)} in kindOf`);
if (t.tag === 'TVar') return terr(`tvar ${showTy(t)} in kindOf`);
};
module.exports = {
inferKind,
kindOf,
};