Skip to content

Instantly share code, notes, and snippets.

@Baudin999
Created March 26, 2019 21:05
Show Gist options
  • Save Baudin999/8b67ffbe39896de5d84271f31d13548a to your computer and use it in GitHub Desktop.
Save Baudin999/8b67ffbe39896de5d84271f31d13548a to your computer and use it in GitHub Desktop.
Currying and Maybe monad in TypeScript
// The Maybe Monad implemented in TypeScript
type Maybe<T> = Nothing<T> | Just<T>
class Just<T> {
a: T;
constructor(a) {
if (a !== null && a !== undefined) this.a = a;
// @ts-ignore
else return new Nothing();
}
}
class Nothing<T> {}
// BIND and LIFT
type Bind = <T,U>(m:Maybe<T>, f:(t:T) => Maybe<U> | U) => Maybe<U>;
type Lift = <T>(t:T) => Maybe<T>;
const bind:Bind = <T, U>(m: Maybe<T>, f:(t:T) => Maybe<U>) : Maybe<U> => {
if (m instanceof Just) {
return f(m.a);
} else {
return new Nothing<U>();
}
}
const lift:Lift = t => {
if (t instanceof Just) return t;
else if (t instanceof Nothing) return t;
else if (t === null || t === undefined) return new Nothing();
else return new Just(t);
}
// We'll need some code to test this stuff with
interface Employee {
Supervisor: Maybe<string>
}
type Employees = Record<number, Employee>;
const log = (...params) => {
console.log.apply(null, params);
return params;
}
const getEmployeeById = (employees: Employees, id:number) : Maybe<Employee> => {
let employee = employees[id];
if (!employee) {
return new Nothing();
} else {
return new Just<Employee>(employee);
}
}
const getSupervisor = employee => {
return employee.Supervisor;
}
const printSupervisor = supervisor => {
return log(supervisor);
}
const employees:Employees = {
1: { Supervisor: new Just<string>("Tom") },
2: { Supervisor: new Just<string>("Jasper") },
3: { Supervisor: new Nothing() },
4: { Supervisor: new Just<string>("Yordi") },
5: { Supervisor: new Just<string>("Niek") },
6: { Supervisor: new Nothing() },
7: { Supervisor: new Just<string>("Carlos") },
};
// How to bind stuff...super simple and known to
// every javascript programmer, function passing style
// but always error free!
bind(getEmployeeById(employees, 1), employee => {
bind(getSupervisor(employee), supervisor => {
log(supervisor);
})
});
/**
* A little Curry with the saus!
* Weird implementation...I know
*/
const curry = (f, l = f.length, $params = []) => {
function c(...params) {
let newParams = $params.concat(params);
if (newParams.length !== l) {
return curry(f, l, newParams);
} else {
return f.apply(null, newParams);
}
}
return c;
}
// Now to get really, really weird about it...
/**
* Let's implement the famous "do" syntax.
*/
const $do = (root, ...funcs) => {
let rootArguments: number = root.length;
let $params = [];
const fun = (...params) => {
let arg = root.apply(null, params);
for (let i = 0; i < funcs.length; ++i) {
arg = bind(arg, p => funcs[i](p));
}
return lift(arg);
};
return curry(fun, root.length); // <-- need to pass in the length cause we're not passing in the real root function for us to calculate the length. The Curry function should work with simple functions....
};
// How to use it
$do(
getEmployeeById,
getSupervisor,
log
)(employees, 2);
// Curry!! And Loops!!
const printSupervisorName:any = $do(
getEmployeeById,
getSupervisor,
log
)(employees);
for (var i = 0; i < 8; ++i ) {
printSupervisorName(i);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment