Skip to content

Commit d3b8dc4

Browse files
committed
TypeScript: turn on strictNullChecks
1 parent 0f6d66a commit d3b8dc4

6 files changed

+36
-11
lines changed

src/interpreter.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export type Visitor = (node: TypedNode, env: RuntimeEnv) => InterpreterResult
2121
// Visit each of `nodes` in order, returning the result
2222
// and environment of the last node.
2323
function visitSerial (nodes: TypedNode[], rootEnv: RuntimeEnv): InterpreterResult {
24-
const initialState: [TypedNode, RuntimeEnv] = [null, rootEnv]
24+
const initialState: [TypedNode, RuntimeEnv] = [nodes[0], rootEnv]
2525
return nodes.reduce(([, env], node) => (
2626
visit(node, env)
2727
), initialState)

src/node-types.ts

+16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ export type AstNode
2525
| AstFunctionNode
2626
| AstFunctionClauseNode
2727
| AstIfNode
28+
| AstDefPreValueNode
2829

2930
// Typed*: type checker output. AST nodes augmented with Peach static types.
3031
export type TypedNode
@@ -40,6 +41,7 @@ export type TypedNode
4041
| TypedFunctionNode
4142
| TypedFunctionClauseNode
4243
| TypedIfNode
44+
| TypedDefPreValueNode
4345

4446
export interface Typed {
4547
exprType: Type
@@ -156,6 +158,20 @@ export interface TypedIfNode extends AstIfNode, Typed {
156158
elseBranch: TypedNode
157159
}
158160

161+
//
162+
// Pseudo-node types
163+
//
164+
165+
// A special marker pseudo-node used to represent the value of a
166+
// `Def` node during type checking, before descending into the value itself.
167+
// We only need the Typed variant, but the Ast*Node is required to maintain
168+
// the AstNode -> TypedNode mapping.
169+
export interface AstDefPreValueNode {
170+
type: 'DefPreValue'
171+
}
172+
173+
export interface TypedDefPreValueNode extends Typed, AstDefPreValueNode { }
174+
159175
//
160176
// Nodes with shared traits
161177
//

src/type-checker.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
AstDestructuredArrayNode, AstFunctionNode, AstIfNode,
99
TypedAst, TypedNode, TypedProgramNode, TypedDefNode, TypedNameNode,
1010
TypedNumeralNode, TypedBooleanNode, TypedStringNode, TypedCallNode, TypedArrayNode,
11-
TypedDestructuredArrayNode, TypedFunctionNode, TypedFunctionClauseNode, TypedIfNode
11+
TypedDestructuredArrayNode, TypedFunctionNode, TypedFunctionClauseNode, TypedIfNode,
12+
TypedDefPreValueNode
1213
} from './node-types'
1314

1415
import {
@@ -89,12 +90,17 @@ function visitDef (node: AstDefNode, env: TypeEnv, nonGeneric: Set<Type>): TypeC
8990
throw new PeachError(`${node.name} has already been defined`)
9091
}
9192

92-
// allow for recursive binding by binding ahead of evaluating the child
93-
// analogous to Lisp's letrec, but in the enclosing scope. We don't know
94-
// the value or concrete type yet.
95-
// TODO immutable env
93+
// Allow for recursive binding by binding ahead of evaluating the child
94+
// analogous to Lisp's letrec, but in the enclosing scope. We don't know
95+
// the value or concrete type yet.
96+
// The binding must be created before visiting the value (in case the value
97+
// is a recursive function). We can't create a TypedEnv value for the def
98+
// value without visiting it, so bind a temporary value with a new TypeVariable.
99+
// Once the value is visited, unify the placeholder type and the concrete value
100+
// type. The TypeEnv only really cares about the type of its values so we can
101+
// continue to use the typed stub.
96102
const t = new TypeVariable()
97-
const typedStubNode: TypedDefNode = { ...node, value: null, exprType: t }
103+
const typedStubNode: TypedDefPreValueNode = { type: 'DefPreValue', exprType: t }
98104
env[node.name] = typedStubNode
99105

100106
// if we are defining a function, mark the new identifier as
@@ -294,6 +300,8 @@ function fresh (type: Type, nonGeneric: Set<Type>) {
294300

295301
// FIXME find out how to do this with type safety, given type erasure.
296302
return (pruned.constructor as any).of(pruned.name, freshTypeArgs)
303+
} else {
304+
return t
297305
}
298306
}
299307

src/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export class TypeVariable extends Type {
9494
instance: Type
9595

9696
constructor () {
97-
super(null)
97+
super('')
9898
this.id = TypeVariable.nextId ++
9999
}
100100

src/unify.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isArray, isEqual } from './array'
33
import { zip } from './util'
44
import { AstNode, AstLiteralNode, AstDestructuredArrayNode, Value, isAstLiteralNode } from './node-types'
55

6-
// FIXME tagged union - with didMatch: false, there are never bindings.
6+
// FIXME tagged union - with didMatch: false, there are never bindings (hence binding | null return types)
77
type binding = { [name: string]: Value }
88

99
interface unification {
@@ -29,7 +29,7 @@ export default function unify (patterns: AstNode[], values: Value[]): unificatio
2929
return didMatch(bindings)
3030
}
3131

32-
function unifyOne (pattern: AstNode, value: Value): binding {
32+
function unifyOne (pattern: AstNode, value: Value): binding | null {
3333
if (isAstLiteralNode(pattern) && pattern.value === value) {
3434
// the pattern matched, but there is nothing to bind
3535
return {}
@@ -54,7 +54,7 @@ function unifyOne (pattern: AstNode, value: Value): binding {
5454
}
5555

5656
// TODO this will need to change when Array is a wrapped type
57-
function destructure ({ head, tail }: AstDestructuredArrayNode, values: Value[]): binding {
57+
function destructure ({ head, tail }: AstDestructuredArrayNode, values: Value[]): binding | null {
5858
if (values.length === 0) {
5959
throw new PeachError(`Empty arrays cannot be destructured because they don't have a head`)
6060
}

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"target": "es2017",
88
"sourceMap": true,
99
"noImplicitAny": true,
10+
"strictNullChecks": true,
1011
"noImplicitThis": true,
1112
"alwaysStrict": true
1213
},

0 commit comments

Comments
 (0)