Last active
November 19, 2019 21:15
-
-
Save reidev275/97da0fb29e72b1d5305011f31a1cbcf6 to your computer and use it in GitHub Desktop.
Mars Rover Kata written in TypeScript with a DSL and monoids allowing the effect to be defined by the interpreter. No Higher Kinded Types required
This file contains hidden or 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 data structure with our core commands and type | |
export type DslC = | |
| { kind: "position"; a: Position } | |
| { kind: "forward"; a: DslC } | |
| { kind: "right"; a: DslC }; | |
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 Iso<A> = (a: A) => A; | |
// isomorphic functions (A -> A) form a monoid | |
// which means we can compose 0 -> n of them together | |
export const go = <A>(...as: Iso<A>[]): Iso<A> => | |
as.reduce((a, b) => x => b(a(x)), x => x); |
This file contains hidden or 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
import { DslC, Position, Heading } from "./dsl" | |
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 | |
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) }; | |
} | |
}; | |
//could extend our implementation to any effect | |
//in this case we could use async communication | |
//to a remote server via promises | |
export const evaluateP = async (a: DslC): Promise<Position> => { | |
switch (a.kind) { | |
case "position": | |
return a.a; | |
case "forward": | |
const pf = await evaluate(a.a); | |
return _forward(pf); | |
case "right": | |
const pr = await evaluate(a.a); | |
return { ...pr, heading: _right(pr.heading) }; | |
} | |
}; |
This file contains hidden or 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
import { right, forward, pos, go } from "./dsl"; | |
import { evaluate } from "./evaluators"; | |
// read direction is right to left when functions are nested | |
//const knight2 = pos => forward(right(forward(forward(pos)))); | |
//using a monoid to pass instructions allows us to write left to right | |
const knight = go(forward, forward, right, forward); | |
//we can implement directions in terms of other directions | |
const left = go(right, right, right); | |
const backward = go(right, right, forward, right, right); | |
const origin = pos({ heading: "north", x: 0, y: 0 }); | |
const raw = knight(origin); | |
console.log(raw); | |
//{ kind: 'forward', | |
// a: { kind: 'right', a: { kind: 'forward', a: [Object] } } } | |
// we can now evaluate our program | |
const result = evaluate(raw); | |
console.log(result); | |
//{ heading: 'east', x: 1, y: 2 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment