-
-
Save Baudin999/8b67ffbe39896de5d84271f31d13548a to your computer and use it in GitHub Desktop.
Currying and Maybe monad in TypeScript
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
// 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