Skip to content

Instantly share code, notes, and snippets.

@frankpf
Created June 23, 2018 23:13
Show Gist options
  • Save frankpf/3d059543271a8c6a929bff757c4c4012 to your computer and use it in GitHub Desktop.
Save frankpf/3d059543271a8c6a929bff757c4c4012 to your computer and use it in GitHub Desktop.
// Trying to simulate do-notation in TypeScript using async/await
// Trying to convert https://codewithstyle.info/advanced-functional-programming-typescript-monads-generators/
// to use async/await. Generators work but aren't type safe because of https://github.com/Microsoft/TypeScript/issues/2983
interface MyPromise<A> {
then<B>(fn: (a: A) => Option<B>): MyPromise<B>
}
export class Option<A> implements MyPromise<A> {
constructor(private readonly value: A | null) {}
static some<A>(value: A) {
return new Option<A>(value)
}
static none<A>() {
return new Option<A>(null)
}
static fromNullable<A>(value: A | null): Option<A> {
return value != null ? Option.some(value) : Option.none<A>()
}
map<B>(fn: (a: A) => B): Option<B> {
if (this.value == null) {
return Option.none()
}
return Option.some(fn(this.value))
}
flatMap<B>(fn: (a: A) => Option<B>): Option<B> {
if (this.value == null) {
return Option.none()
}
return fn(this.value)
}
then = this.flatMap
}
class EmployeeRepository {
private employees: Employee[] = [
{ id: 1, name: "John", supervisorId: Option.none() },
{ id: 2, name: "Jane", supervisorId: Option.some(1) },
{ id: 3, name: "Joe", supervisorId: Option.some(2) },
];
findById(id: number): Option<Employee> {
const results = this.employees.filter(employee => employee.id === id);
return results.length ? Option.some(results[0]) : Option.none();
}
}
interface Employee {
id: number;
name: string;
supervisorId: Option<number>;
}
const repository = new EmployeeRepository()
function getSupervisorName1(maybeEnteredId: Option<string>): Option<string> {
return maybeEnteredId
.flatMap(employeeIdString => Option.fromNullable(parseInt(employeeIdString)))
.flatMap(employeeId => repository.findById(employeeId))
.flatMap(employee => employee.supervisorId)
.flatMap(supervisorId => repository.findById(supervisorId))
.map(supervisor => supervisor.name);
}
async function getSupervisorName2(maybeEnteredId: Option<string>): Promise<string> {
const enteredIdStr = await maybeEnteredId;
const enteredId = parseInt(enteredIdStr);
const employee = await repository.findById(enteredId);
const supervisorId = await employee.supervisorId;
const supervisor = await repository.findById(supervisorId);
return Option.some(supervisor.name);
}
async function main() {
const id = '3'
const r1 = getSupervisorName1(Option.some(id))
console.log('r1: ', r1)
const r3 = await getSupervisorName2(Option.some(id))
console.log('r3: ', r3)
}
main().then(() => console.log('Done!'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment