Skip to content

Instantly share code, notes, and snippets.

@reidev275
Created March 11, 2020 18:47
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 reidev275/37533b038035838fdd9b2b72f6ad70d6 to your computer and use it in GitHub Desktop.
Save reidev275/37533b038035838fdd9b2b72f6ad70d6 to your computer and use it in GitHub Desktop.
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