Skip to content

Instantly share code, notes, and snippets.

@caasi
Last active November 8, 2022 22:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caasi/45140d940c4ebd72b19cc89c4e66312c to your computer and use it in GitHub Desktop.
Save caasi/45140d940c4ebd72b19cc89c4e66312c to your computer and use it in GitHub Desktop.
type PrimitiveType = 'string' | 'number' | 'boolean' | 'bigint'
const isPrimitiveType = (v: string): v is PrimitiveType => {
return (
v === 'string' ||
v === 'number' ||
v === 'boolean' ||
v === 'bigint'
)
}
type Primitive = {
t: 'primitive',
v: PrimitiveType
}
const primitive = (v: PrimitiveType): Primitive => ({ t: 'primitive', v })
type Sum = {
t: 'sum',
v: ADT[]
}
const sum = (v: ADT[]): Sum => ({ t: 'sum', v })
type Product = {
t: 'product',
v: [string, ADT][]
}
const product = (v: [string, ADT][]): Product => ({ t: 'product', v })
type Other = {
t: 'other',
v: string
}
const other = (v: string): Other => ({ t: 'other', v })
type ADT =
| Primitive
| Sum
| Product
| Other
// namespace/module ADT
const ADT = {
of: (v: any): ADT => {
if (Array.isArray(v)) {
return sum(v.map(ADT.of))
} else if (typeof v === 'object') {
return product(Object.entries(v).map(([k, v]: [string, any]) => [k, ADT.of(v)]))
} else {
const t = typeof v
return isPrimitiveType(t) ? primitive(t) : other(t)
}
},
print: (t: ADT): string => {
switch (t.t) {
case 'primitive': return t.v
case 'sum': return `(${t.v.map(ADT.print).join(' | ')})[]`
case 'product': return `{ ${t.v.map(([key, t]) => `${key}: ${ADT.print(t)}`).join(', ')} }`
case 'other': return ''
}
}
}
type Fields = string[]
const first = <T = any, U = any>([a]: [T, ...U[]]): T => a
type Graph = {
nodes: Fields[],
links: { from: Fields, to: Fields }[]
}
// namespace/module Graph
const Graph = {
empty: () => ({ nodes: [], links: [] }),
fromADT: (t: ADT): Graph => {
switch (t.t) {
case 'primitive': return Graph.empty()
case 'sum': {
if (t.v.length === 0) {
return Graph.empty()
} else {
const p = t.v.find((t) => t.t === 'product') as (Product | undefined)
return p ? { nodes: [p.v.map(first)], links: [] } : Graph.empty()
}
}
case 'product': {
const fields = t.v.map(first)
let nodes: Fields[] = [fields]
let links: { from: Fields, to: Fields }[] = []
for (let [rel, tt] of t.v) {
const subGraph = Graph.fromADT(tt)
nodes.push(...subGraph.nodes)
links.push(...subGraph.links)
if (subGraph.nodes.length) {
links.push({ from: fields, to: subGraph.nodes[0] })
}
}
return { nodes, links }
}
case 'other': return Graph.empty()
}
}
}
const a = {
foo: 'bar',
bar: { name: 'John', age: 30 },
xs: [{ foo: 42 }, { foo: 0, bar: true }],
ys: [1, 2, 3, 4, 5]
}
console.log(ADT.print(ADT.of(a)))
console.log(Graph.fromADT(ADT.of(a)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment