Skip to content

Commit ab6c25b

Browse files
authored
Merge pull request #13 from mkantor/key-path-sanity
Separate `TypeKeyPath` from `KeyPath`
2 parents 997cb9f + 570b16a commit ab6c25b

File tree

7 files changed

+57
-86
lines changed

7 files changed

+57
-86
lines changed

src/language/compiling/semantics/keyword-handlers/lookup-handler.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,14 @@ const lookup = ({
143143
),
144144
right: functionExpression => {
145145
if (functionExpression.parameter === firstPathComponent) {
146-
if (!relativePath.every(key => typeof key === 'string')) {
147-
return either.makeLeft({
148-
kind: 'invalidExpression',
149-
message:
150-
'dynamically-resolved lookup query contains symbolic key',
151-
})
152-
} else {
153-
// Keep an unelaborated `@lookup` around for resolution when the `@function` is called.
154-
return either.makeRight(
155-
option.makeSome(
156-
makeLookupExpression(
157-
makeObjectNode(keyPathToMolecule(relativePath)),
158-
),
146+
// Keep an unelaborated `@lookup` around for resolution when the `@function` is called.
147+
return either.makeRight(
148+
option.makeSome(
149+
makeLookupExpression(
150+
makeObjectNode(keyPathToMolecule(relativePath)),
159151
),
160-
)
161-
}
152+
),
153+
)
162154
} else {
163155
return either.makeRight(option.none)
164156
}

src/language/parsing/syntax-tree.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,6 @@ export const applyKeyPathToSyntaxTree = (
2626
} else {
2727
if (typeof syntaxTree === 'string') {
2828
return option.none
29-
} else if (typeof firstKey === 'symbol') {
30-
// TODO: Treat this as an error? Or change the type of `keyPath`?
31-
return option.none
3229
} else {
3330
const next = withPhantomData<Canonicalized>()(syntaxTree[firstKey])
3431
if (next === undefined) {

src/language/semantics/key-path.ts

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,7 @@ import type { Atom, Molecule } from '../parsing.js'
33
import { unparse } from '../unparsing.js'
44
import { prettyPlz } from '../unparsing/pretty-plz.js'
55

6-
export const functionParameter = Symbol('functionParameter')
7-
export const functionReturn = Symbol('functionReturn')
8-
export const typeParameterAssignableToConstraint = Symbol(
9-
'typeParameterAssignableToConstraint',
10-
)
11-
export type KeyPath = readonly (
12-
| Atom
13-
// These symbol keys are somewhat "internal" at the moment. If they end up not being expressible
14-
// in the surface syntax then `KeyPath` should be split into two separate types.
15-
| typeof functionParameter
16-
| typeof functionReturn
17-
| typeof typeParameterAssignableToConstraint
18-
)[]
6+
export type KeyPath = readonly Atom[]
197

208
export const stringifyKeyPathForEndUser = (keyPath: KeyPath): string =>
219
either.match(
@@ -28,8 +16,4 @@ export const stringifyKeyPathForEndUser = (keyPath: KeyPath): string =>
2816
)
2917

3018
export const keyPathToMolecule = (keyPath: KeyPath): Molecule =>
31-
Object.fromEntries(
32-
keyPath.flatMap((key, index) =>
33-
typeof key === 'symbol' ? [] : [[index, key]],
34-
),
35-
)
19+
Object.fromEntries(keyPath.flatMap((key, index) => [[index, key]]))

src/language/semantics/prelude.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { either, option, type Either } from '../../adts.js'
22
import type { DependencyUnavailable, Panic } from '../errors.js'
33
import type { Atom } from '../parsing.js'
44
import { isFunctionNode, makeFunctionNode } from './function-node.js'
5-
import { keyPathToMolecule } from './key-path.js'
5+
import { keyPathToMolecule, type KeyPath } from './key-path.js'
66
import {
77
isObjectNode,
88
lookupPropertyOfObjectNode,
@@ -44,7 +44,7 @@ const handleUnavailableDependencies =
4444
}
4545

4646
const serializePartiallyAppliedFunction =
47-
(keyPath: readonly string[], argument: SemanticGraph) => () =>
47+
(keyPath: KeyPath, argument: SemanticGraph) => () =>
4848
either.makeRight(
4949
makeUnelaboratedObjectNode({
5050
0: '@apply',
@@ -54,7 +54,7 @@ const serializePartiallyAppliedFunction =
5454
)
5555

5656
const preludeFunction = (
57-
keyPath: readonly string[],
57+
keyPath: KeyPath,
5858
signature: FunctionType['signature'],
5959
f: (
6060
value: SemanticGraph,

src/language/semantics/semantic-graph.ts

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,14 @@ export const applyKeyPathToSemanticGraph = (
3535
atom: _ => option.none,
3636
function: _ => option.none,
3737
object: graph => {
38-
if (typeof firstKey === 'symbol') {
38+
const next = graph[firstKey]
39+
if (next === undefined) {
3940
return option.none
4041
} else {
41-
const next = graph[firstKey]
42-
if (next === undefined) {
43-
return option.none
44-
} else {
45-
return applyKeyPathToSemanticGraph(
46-
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
47-
remainingKeyPath,
48-
)
49-
}
42+
return applyKeyPathToSemanticGraph(
43+
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
44+
remainingKeyPath,
45+
)
5046
}
5147
},
5248
})
@@ -106,28 +102,24 @@ export const updateValueAtKeyPathInSemanticGraph = (
106102
atom: _ => either.makeLeft(makePropertyNotFoundError(keyPath)),
107103
function: _ => either.makeLeft(makePropertyNotFoundError(keyPath)),
108104
object: node => {
109-
if (typeof firstKey === 'symbol') {
105+
const next = node[firstKey]
106+
if (next === undefined) {
110107
return either.makeLeft(makePropertyNotFoundError(keyPath))
111108
} else {
112-
const next = node[firstKey]
113-
if (next === undefined) {
114-
return either.makeLeft(makePropertyNotFoundError(keyPath))
115-
} else {
116-
return either.map(
117-
updateValueAtKeyPathInSemanticGraph(
118-
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
119-
remainingKeyPath,
120-
operation,
121-
),
122-
updatedNode =>
123-
(isUnelaborated(node)
124-
? makeUnelaboratedObjectNode
125-
: makeObjectNode)({
126-
...node,
127-
[firstKey]: updatedNode,
128-
}),
129-
)
130-
}
109+
return either.map(
110+
updateValueAtKeyPathInSemanticGraph(
111+
isSemanticGraph(next) ? next : syntaxTreeToSemanticGraph(next),
112+
remainingKeyPath,
113+
operation,
114+
),
115+
updatedNode =>
116+
(isUnelaborated(node)
117+
? makeUnelaboratedObjectNode
118+
: makeObjectNode)({
119+
...node,
120+
[firstKey]: updatedNode,
121+
}),
122+
)
131123
}
132124
},
133125
})

src/language/semantics/type-system/type-utilities.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import type { Writable } from '../../../utility-types.js'
2-
import {
3-
functionParameter,
4-
functionReturn,
5-
typeParameterAssignableToConstraint,
6-
type KeyPath,
7-
} from '../key-path.js'
2+
import type { Atom } from '../../parsing.js'
83
import { types } from '../type-system.js'
94
import { simplifyUnionType } from './subtyping.js'
105
import {
@@ -19,14 +14,27 @@ import {
1914
type UnionType,
2015
} from './type-formats.js'
2116

17+
const functionParameter = Symbol('functionParameter')
18+
const functionReturn = Symbol('functionReturn')
19+
const typeParameterAssignableToConstraint = Symbol(
20+
'typeParameterAssignableToConstraint',
21+
)
22+
23+
export type TypeKeyPath = readonly (
24+
| Atom
25+
| typeof functionParameter
26+
| typeof functionReturn
27+
| typeof typeParameterAssignableToConstraint
28+
)[]
29+
2230
type StringifiedKeyPath = string // this could be branded if that seems useful
2331
type UnionOfTypeParameters = Omit<UnionType, 'members'> & {
2432
readonly members: ReadonlySet<TypeParameter>
2533
}
2634
export type TypeParametersByKeyPath = Map<
2735
StringifiedKeyPath,
2836
{
29-
readonly keyPath: KeyPath
37+
readonly keyPath: TypeKeyPath
3038
readonly typeParameters: UnionOfTypeParameters
3139
}
3240
>
@@ -36,7 +44,7 @@ export const containedTypeParameters = (type: Type): TypeParametersByKeyPath =>
3644

3745
const containedTypeParametersImplementation = (
3846
type: Type,
39-
root: KeyPath,
47+
root: TypeKeyPath,
4048
): TypeParametersByKeyPath => {
4149
// Avoid infinite recursion when we hit the top type.
4250
if (type === types.something) {
@@ -96,14 +104,14 @@ const containedTypeParametersImplementation = (
96104
export const findKeyPathsToTypeParameter = (
97105
type: Type,
98106
typeParameterToFind: TypeParameter,
99-
): Set<KeyPath> =>
107+
): Set<TypeKeyPath> =>
100108
findKeyPathsToTypeParameterImplementation(type, typeParameterToFind, [])
101109

102110
const findKeyPathsToTypeParameterImplementation = (
103111
type: Type,
104112
typeParameterToFind: TypeParameter,
105-
root: KeyPath,
106-
): Set<KeyPath> => {
113+
root: TypeKeyPath,
114+
): Set<TypeKeyPath> => {
107115
// Avoid infinite recursion when we hit the top type.
108116
if (type === types.something) {
109117
return new Set()
@@ -148,7 +156,7 @@ const findKeyPathsToTypeParameterImplementation = (
148156
union: ({ members }) =>
149157
[...members]
150158
.map(
151-
(member): Set<KeyPath> =>
159+
(member): Set<TypeKeyPath> =>
152160
typeof member === 'string'
153161
? new Set()
154162
: findKeyPathsToTypeParameterImplementation(
@@ -253,7 +261,7 @@ export const supplyTypeArgument = (
253261
*/
254262
export const updateTypeAtKeyPathIfValid = (
255263
type: Type,
256-
keyPath: KeyPath,
264+
keyPath: TypeKeyPath,
257265
// TODO: `operation` should be able to update `Atom`s
258266
operation: (typeAtKeyPath: Exclude<Type, UnionType>) => Type,
259267
): Type => {
@@ -400,7 +408,7 @@ const mergeTypeParametersByKeyPath = (
400408

401409
// The string format is not meant for human consumption. The only guarantee is that every distinct
402410
// key path produces a unique string.
403-
const stringifyKeyPath = (keyPath: KeyPath): string =>
411+
const stringifyKeyPath = (keyPath: TypeKeyPath): string =>
404412
keyPath.reduce((stringifiedKeyPath: string, key) => {
405413
const stringifiedKey =
406414
typeof key === 'symbol' ? key.description : JSON.stringify(key)

src/language/unparsing/pretty-plz.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,7 @@ const sugaredLookup = (keyPathAsNode: Molecule | SemanticGraph) => {
8585
if (
8686
keyPath !== 'invalid' &&
8787
Object.keys(keyPathAsNode).length === keyPath.length &&
88-
keyPath.every(
89-
key => typeof key === 'string' && !either.isLeft(unquotedAtomParser(key)),
90-
)
88+
keyPath.every(key => !either.isLeft(unquotedAtomParser(key)))
9189
) {
9290
return either.makeRight(kleur.cyan(colon.concat(keyPath.join(dot))))
9391
} else {

0 commit comments

Comments
 (0)