Created
March 11, 2020 18:47
-
-
Save reidev275/37533b038035838fdd9b2b72f6ad70d6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export type Heading = "north" | "south" | "east" | "west"; | |
export type Position = { | |
heading: Heading; | |
x: number; | |
y: number; | |
}; | |
//recursive, sum type data structure with our core commands and type | |
export type DslC = | |
| { kind: "position"; a: Position } | |
| { kind: "forward"; a: DslC } | |
| { kind: "right"; a: DslC }; | |
/* | |
* function helpers to create different DslC values | |
* 1 for each case | |
*/ | |
export const pos = (a: Position): DslC => ({ | |
kind: "position", | |
a | |
}); | |
export const forward = (a: DslC): DslC => ({ | |
kind: "forward", | |
a | |
}); | |
export const right = (a: DslC): DslC => ({ | |
kind: "right", | |
a | |
}); | |
//type for an isomorphic function | |
//any function from some type to the same type | |
//left, right, forward, and backward are all functions from Position to Position | |
type Iso<A> = (a: A) => A; | |
// once we realize all isomorphic functions are monoids we can write | |
// this go function which combines 0 to infinite instructions into a single | |
// instruction. | |
// The spread operator allows us to omit the brackets in calling code. | |
export const go = <A>(...isos: Iso<A>[]): Iso<A> => | |
isos.reduce( | |
(a: Iso<A>, b: Iso<A>): Iso<A> => x => b(a(x)), //append | |
x => x //empty | |
); | |
// user created programs that can be infinitely nested | |
const left = go(right, right, right); | |
const backward = go(right, right, forward, right, right); | |
const directions = go(right, forward, left, forward, backward); | |
/* | |
* evaluator helpers | |
*/ | |
const _right = (x: Heading) => { | |
switch (x) { | |
case "north": | |
return "east"; | |
case "east": | |
return "south"; | |
case "south": | |
return "west"; | |
case "west": | |
return "north"; | |
} | |
}; | |
const _forward = (x: Position): Position => { | |
switch (x.heading) { | |
case "north": | |
return { ...x, y: x.y + 1 }; | |
case "east": | |
return { ...x, x: x.x + 1 }; | |
case "south": | |
return { ...x, y: x.y - 1 }; | |
case "west": | |
return { ...x, y: x.x - 1 }; | |
} | |
}; | |
//simple recursive evaluation of our recursive data type | |
//we don't need higher kinded types or the type parameter from a free monad because the | |
//interpreter can define any output it wants | |
export const evaluate = (a: DslC): Position => { | |
switch (a.kind) { | |
case "position": | |
return a.a; | |
case "forward": | |
const pf = evaluate(a.a); | |
return _forward(pf); | |
case "right": | |
const pr = evaluate(a.a); | |
return { ...pr, heading: _right(pr.heading) }; | |
} | |
}; | |
const raw = directions(pos({ heading: "north", x: 0, y: 0 })); | |
console.log(JSON.stringify(raw)); | |
//{ kind: 'forward', | |
// a: { kind: 'right', a: { kind: 'forward', a: [Object] } } } | |
// we can now evaluate our program | |
const result = evaluate(raw); | |
console.log(result); | |
//{ x: 1, y: 0, heading: 'north' } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment